Compare commits

..

2 Commits

Author SHA1 Message Date
Kirill Fedoseev
5490b8b029 Drop duplicated UserRequestForSignature events in monitor 2021-05-19 14:48:42 +03:00
Kirill Fedoseev
3f93fef016 Add oracle e2e test for requestMessageReconfirm 2021-05-17 19:46:29 +03:00
113 changed files with 2112 additions and 2441 deletions

View File

@@ -77,7 +77,7 @@ jobs:
- name: Rebuild and push updated images - name: Rebuild and push updated images
run: | run: |
function check_if_image_exists() { function check_if_image_exists() {
curl -fsSlL "https://${{ github.actor }}:${{ github.token }}@${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
} }
updated=() updated=()
if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi
@@ -104,7 +104,7 @@ jobs:
- name: Rebuild and push molecule runner e2e image - name: Rebuild and push molecule runner e2e image
run: | run: |
function check_if_image_exists() { function check_if_image_exists() {
curl -fsSlL "https://${{ github.actor }}:${{ github.token }}@${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
} }
if check_if_image_exists molecule_runner ${MOLECULE_RUNNER_TAG}; then if check_if_image_exists molecule_runner ${MOLECULE_RUNNER_TAG}; then
echo "Image already exists" echo "Image already exists"
@@ -205,7 +205,7 @@ jobs:
- name: Login to docker registry - name: Login to docker registry
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }} run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
- name: Deploy contracts - name: Deploy contracts
run: ${{ steps.cache-repo.outputs.cache-hit }} && e2e-commons/up.sh deploy generate-amb-tx blocks run: ${{ steps.cache-repo.outputs.cache-hit }} && e2e-commons/up.sh deploy blocks
- name: Pull e2e oracle image - name: Pull e2e oracle image
run: | run: |
docker-compose -f ./e2e-commons/docker-compose.yml pull oracle-amb docker-compose -f ./e2e-commons/docker-compose.yml pull oracle-amb

View File

@@ -52,9 +52,6 @@ ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain a
ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature` ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature`
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer` ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer`
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Home chain. Infinite, if not provided. | `integer` ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Home chain. Infinite, if not provided. | `integer`
ORACLE_JSONRPC_ERROR_CODES | Override default JSON rpc error codes that can trigger RPC fallback to the next URL from the list (or a retry in case of a single RPC URL). Default is `-32603,-32002,-32005`. Should be a comma-separated list of negative integers. | `string`
ORACLE_VALIDATOR_KEYSTORE_PATH | Path to the keystore v3 json file with the encrypted validator key. | `string`
ORACLE_VALIDATOR_KEYSTORE_PASSWORD | Password from the provided keystore file, oracle won't startup properly, if the provided password is invalid | `string`
## Monitor configuration ## Monitor configuration

View File

@@ -19,7 +19,6 @@ COPY --from=contracts /mono/contracts/build ./contracts/build
COPY commons/package.json ./commons/ COPY commons/package.json ./commons/
COPY oracle-e2e/package.json ./oracle-e2e/ COPY oracle-e2e/package.json ./oracle-e2e/
COPY monitor-e2e/package.json ./monitor-e2e/ COPY monitor-e2e/package.json ./monitor-e2e/
COPY oracle/src/utils/constants.js ./oracle/src/utils/constants.js
COPY yarn.lock . COPY yarn.lock .
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cd $(dirname $0) cd $(dirname $0)
../e2e-commons/up.sh deploy generate-amb-tx blocks alm alm-e2e ../e2e-commons/up.sh deploy blocks alm alm-e2e
# run oracle amb e2e tests to generate transactions for alm # run oracle amb e2e tests to generate transactions for alm
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run alm docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run alm

View File

@@ -6,8 +6,8 @@ jest.setTimeout(60000)
const statusText = 'Success' const statusText = 'Success'
const statusSelector = 'label[data-id="status"]' const statusSelector = 'label[data-id="status"]'
const homeToForeignTxURL = 'http://localhost:3004/77/0x295efbe6ae98937ef35d939376c9bd752b4dc6f6899a9d5ddd6a57cea3d76c89' const homeToForeignTxURL = 'http://localhost:3004/77/0xbc83d43bdc675a615a2b820e43e52d25857aa5fdd77acf2dd92cd247af2c693c'
const foreignToHomeTxURL = 'http://localhost:3004/42/0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd' const foreignToHomeTxURL = 'http://localhost:3004/42/0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
describe('ALM', () => { describe('ALM', () => {
let browser let browser

View File

@@ -123,24 +123,6 @@ const abi: AbiItem[] = [
stateMutability: 'nonpayable', stateMutability: 'nonpayable',
type: 'function' type: 'function'
}, },
{
constant: false,
inputs: [
{
name: '_data',
type: 'bytes'
},
{
name: '_signatures',
type: 'bytes'
}
],
name: 'safeExecuteSignaturesWithAutoGasLimit',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{ {
constant: true, constant: true,
inputs: [ inputs: [

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react' import React from 'react'
import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks' import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks'
import { useWindowWidth } from '@react-hook/window-size' import { useWindowWidth } from '@react-hook/window-size'
import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS, ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from '../config/constants' import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS, ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from '../config/constants'
@@ -9,7 +9,6 @@ import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
import { ExplorerTxLink } from './commons/ExplorerTxLink' import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { Thead, AgeTd, StatusTd } from './commons/Table' import { Thead, AgeTd, StatusTd } from './commons/Table'
import { ManualExecutionButton } from './ManualExecutionButton' import { ManualExecutionButton } from './ManualExecutionButton'
import { useStateProvider } from '../state/StateProvider'
const StyledExecutionConfirmation = styled.div` const StyledExecutionConfirmation = styled.div`
margin-top: 30px; margin-top: 30px;
@@ -34,8 +33,6 @@ export const ExecutionConfirmation = ({
executionEventsFetched, executionEventsFetched,
setPendingExecution setPendingExecution
}: ExecutionConfirmationParams) => { }: ExecutionConfirmationParams) => {
const { foreign } = useStateProvider()
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
const availableManualExecution = const availableManualExecution =
!isHome && !isHome &&
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING || (executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
@@ -51,22 +48,6 @@ export const ExecutionConfirmation = ({
const formattedValidator = const formattedValidator =
windowWidth < 850 && executionData.validator ? formatTxHash(executionData.validator) : executionData.validator windowWidth < 850 && executionData.validator ? formatTxHash(executionData.validator) : executionData.validator
useEffect(
() => {
if (!availableManualExecution || !foreign.bridgeContract) return
const p = foreign.bridgeContract.methods.getBridgeInterfacesVersion().call()
p.then(({ major, minor }: any) => {
major = parseInt(major, 10)
minor = parseInt(minor, 10)
if (major < 5 || (major === 5 && minor < 7)) return
setSafeExecutionAvailable(true)
})
},
[availableManualExecution, foreign.bridgeContract]
)
const getExecutionStatusElement = (validatorStatus = '') => { const getExecutionStatusElement = (validatorStatus = '') => {
switch (validatorStatus) { switch (validatorStatus) {
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS: case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
@@ -124,7 +105,6 @@ export const ExecutionConfirmation = ({
{availableManualExecution && ( {availableManualExecution && (
<td> <td>
<ManualExecutionButton <ManualExecutionButton
safeExecutionAvailable={safeExecutionAvailable}
messageData={messageData} messageData={messageData}
setExecutionData={setExecutionData} setExecutionData={setExecutionData}
signatureCollected={signatureCollected as string[]} signatureCollected={signatureCollected as string[]}

View File

@@ -15,19 +15,16 @@ import { signatureToVRS, packSignatures } from '../utils/signatures'
import { getSuccessExecutionData } from '../utils/getFinalizationEvent' import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
import { TransactionReceipt } from 'web3-eth' import { TransactionReceipt } from 'web3-eth'
const ActionButton = styled.button` const StyledButton = styled.button`
color: var(--button-color); color: var(--button-color);
border-color: var(--font-color); border-color: var(--font-color);
margin-top: 10px; margin-top: 10px;
min-width: 120px;
padding: 1rem;
&:focus { &:focus {
outline: var(--button-color); outline: var(--button-color);
} }
` `
interface ManualExecutionButtonParams { interface ManualExecutionButtonParams {
safeExecutionAvailable: boolean
messageData: string messageData: string
setExecutionData: Function setExecutionData: Function
signatureCollected: string[] signatureCollected: string[]
@@ -35,7 +32,6 @@ interface ManualExecutionButtonParams {
} }
export const ManualExecutionButton = ({ export const ManualExecutionButton = ({
safeExecutionAvailable,
messageData, messageData,
setExecutionData, setExecutionData,
signatureCollected, signatureCollected,
@@ -44,8 +40,6 @@ export const ManualExecutionButton = ({
const { foreign, setError } = useStateProvider() const { foreign, setError } = useStateProvider()
const { library, activate, account, active } = useWeb3React() const { library, activate, account, active } = useWeb3React()
const [manualExecution, setManualExecution] = useState(false) const [manualExecution, setManualExecution] = useState(false)
const [allowFailures, setAllowFailures] = useState(false)
const notReady = !foreign.bridgeContract || !signatureCollected || !signatureCollected.length
useEffect( useEffect(
() => { () => {
@@ -78,11 +72,7 @@ export const ManualExecutionButton = ({
const signatures = packSignatures(signatureCollected.map(signatureToVRS)) const signatures = packSignatures(signatureCollected.map(signatureToVRS))
const messageId = messageData.slice(0, 66) const messageId = messageData.slice(0, 66)
const bridge = foreign.bridgeContract const bridge = foreign.bridgeContract
const executeMethod = const data = bridge.methods.executeSignatures(messageData, signatures).encodeABI()
safeExecutionAvailable && !allowFailures
? bridge.methods.safeExecuteSignaturesWithAutoGasLimit
: bridge.methods.executeSignatures
const data = executeMethod(messageData, signatures).encodeABI()
setManualExecution(false) setManualExecution(false)
library.eth library.eth
@@ -142,35 +132,15 @@ export const ManualExecutionButton = ({
messageData, messageData,
signatureCollected, signatureCollected,
setExecutionData, setExecutionData,
setPendingExecution, setPendingExecution
safeExecutionAvailable,
allowFailures
] ]
) )
return ( return (
<div> <div className="is-center">
<div className="is-center"> <StyledButton className="button outline" onClick={() => setManualExecution(true)}>
<ActionButton disabled={notReady} className="button outline" onClick={() => setManualExecution(true)}> Execute
Execute </StyledButton>
</ActionButton>
</div>
{safeExecutionAvailable && (
<div
title="Allow executed message to fail and record its failure on-chain without reverting the whole transaction.
Use fixed gas limit for execution."
className="is-center"
style={{ paddingTop: 10 }}
>
<input
type="checkbox"
id="allow-failures"
checked={allowFailures}
onChange={e => setAllowFailures(e.target.checked)}
/>
<label htmlFor="allow-failures">Unsafe mode</label>
</div>
)}
</div> </div>
) )
} }

View File

@@ -86,14 +86,6 @@ export const getConfirmationsForTx = async (
setPendingConfirmations(validatorPendingConfirmations.length > 0) setPendingConfirmations(validatorPendingConfirmations.length > 0)
} else { } else {
setPendingConfirmations(false) setPendingConfirmations(false)
if (fromHome) {
// fetch collected signatures for possible manual processing
setSignatureCollected(
await Promise.all(
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
)
)
}
} }
const undefinedConfirmations = validatorConfirmations.filter( const undefinedConfirmations = validatorConfirmations.filter(
@@ -123,6 +115,15 @@ export const getConfirmationsForTx = async (
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
})) }))
updateConfirmations(notRequiredConfirmations) updateConfirmations(notRequiredConfirmations)
if (fromHome) {
// fetch collected signatures for possible manual processing
setSignatureCollected(
await Promise.all(
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
)
)
}
} }
// get transactions from success signatures // get transactions from success signatures

View File

@@ -1,5 +1,5 @@
const HOME_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/HomeBridgeErcToNative').abi const HOME_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/HomeBridgeErcToNative').abi
const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/XDaiForeignBridge.json').abi const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/ForeignBridgeErcToNative').abi
const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/BlockRewardMock').abi const BLOCK_REWARD_ABI = require('../contracts/build/contracts/BlockRewardMock').abi
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi

View File

@@ -1,5 +1,3 @@
const { soliditySha3 } = require('web3-utils')
function strip0x(input) { function strip0x(input) {
return input.replace(/^0x/, '') return input.replace(/^0x/, '')
} }
@@ -41,35 +39,8 @@ const normalizeAMBMessageEvent = e => {
return parseAMBMessage(msgData) return parseAMBMessage(msgData)
} }
const ambInformationSignatures = [
'eth_call(address,bytes)',
'eth_call(address,bytes,uint256)',
'eth_call(address,address,uint256,bytes)',
'eth_blockNumber()',
'eth_getBlockByNumber()',
'eth_getBlockByNumber(uint256)',
'eth_getBlockByHash(bytes32)',
'eth_getBalance(address)',
'eth_getBalance(address,uint256)',
'eth_getTransactionCount(address)',
'eth_getTransactionCount(address,uint256)',
'eth_getTransactionByHash(bytes32)',
'eth_getTransactionReceipt(bytes32)',
'eth_getStorageAt(address,bytes32)',
'eth_getStorageAt(address,bytes32,uint256)'
]
const ambInformationSelectors = Object.fromEntries(ambInformationSignatures.map(sig => [soliditySha3(sig), sig]))
const normalizeAMBInfoRequest = e => ({
messageId: e.returnValues.messageId,
sender: e.returnValues.sender,
requestSelector: ambInformationSelectors[e.returnValues.requestSelector] || 'unknown',
data: e.returnValues.data
})
module.exports = { module.exports = {
strip0x, strip0x,
parseAMBMessage, parseAMBMessage,
normalizeAMBMessageEvent, normalizeAMBMessageEvent
ambInformationSignatures,
normalizeAMBInfoRequest
} }

View File

@@ -9,8 +9,7 @@
}, },
"dependencies": { "dependencies": {
"gas-price-oracle": "^0.1.5", "gas-price-oracle": "^0.1.5",
"web3-utils": "^1.3.0", "web3-utils": "^1.3.0"
"node-fetch": "^2.1.2"
}, },
"devDependencies": { "devDependencies": {
"bn-chai": "^1.0.1", "bn-chai": "^1.0.1",

View File

@@ -1,6 +1,5 @@
const { toWei, toBN, BN } = require('web3-utils') const { toWei, toBN, BN } = require('web3-utils')
const { GasPriceOracle } = require('gas-price-oracle') const { GasPriceOracle } = require('gas-price-oracle')
const fetch = require('node-fetch')
const { BRIDGE_MODES } = require('./constants') const { BRIDGE_MODES } = require('./constants')
const { REWARDABLE_VALIDATORS_ABI } = require('./abis') const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
@@ -179,16 +178,17 @@ const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei')) return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei'))
} }
const gasPriceFromSupplier = async (url, options = {}) => { // fetchFn has to be supplied (instead of just url to oracle),
// because this utility function is shared between Browser and Node,
// we use built-in 'fetch' on browser side, and `node-fetch` package in Node.
const gasPriceFromSupplier = async (fetchFn, options = {}) => {
try { try {
let json let json
if (url === 'gas-price-oracle') { if (fetchFn) {
json = await gasPriceOracle.fetchGasPricesOffChain() const response = await fetchFn()
} else if (url) {
const response = await fetch(url, { timeout: 2000 })
json = await response.json() json = await response.json()
} else { } else {
return null json = await gasPriceOracle.fetchGasPricesOffChain()
} }
const oracleGasPrice = json[options.speedType] const oracleGasPrice = json[options.speedType]

View File

@@ -1,4 +1,4 @@
FROM python:3.7 FROM python:3.7-stretch
RUN curl -fsSL https://get.docker.com | sh RUN curl -fsSL https://get.docker.com | sh
RUN pip3 install docker molecule[docker,ansible] pytest pytest-testinfra flake8 RUN pip3 install docker molecule==2.22rc1 molecule[docker] flake8
WORKDIR mono/deployment-e2e WORKDIR mono/deployment-e2e

View File

@@ -3,6 +3,12 @@ dependency:
name: galaxy name: galaxy
driver: driver:
name: docker name: docker
lint:
name: yamllint
enabled: True
options:
config-data:
ignore: ../../hosts.yml
platforms: platforms:
- name: monitor-host - name: monitor-host
groups: groups:
@@ -16,6 +22,11 @@ platforms:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
provisioner: provisioner:
name: ansible name: ansible
lint:
name: ansible-lint
enabled: True
options:
r: ["bug"]
playbooks: playbooks:
prepare: ../prepare.yml prepare: ../prepare.yml
converge: ./converge.yml converge: ./converge.yml
@@ -26,11 +37,14 @@ provisioner:
syslog_server_port: "udp://127.0.0.1:514" syslog_server_port: "udp://127.0.0.1:514"
verifier: verifier:
name: testinfra name: testinfra
lint:
name: flake8
additional_files_or_dirs: additional_files_or_dirs:
- ../../tests/* - ../../tests/*
scenario: scenario:
name: monitor name: monitor
test_sequence: test_sequence:
- lint
- cleanup - cleanup
- destroy - destroy
- dependency - dependency

View File

@@ -3,6 +3,12 @@ dependency:
name: galaxy name: galaxy
driver: driver:
name: docker name: docker
lint:
name: yamllint
enabled: True
options:
config-data:
ignore: ../../hosts.yml
platforms: platforms:
- name: multiple-host - name: multiple-host
groups: groups:
@@ -17,6 +23,11 @@ platforms:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
provisioner: provisioner:
name: ansible name: ansible
lint:
name: ansible-lint
enabled: True
options:
r: ["bug"]
playbooks: playbooks:
prepare: ../prepare.yml prepare: ../prepare.yml
converge: ../monitor/converge.yml converge: ../monitor/converge.yml
@@ -28,11 +39,14 @@ provisioner:
syslog_server_port: "udp://127.0.0.1:514" syslog_server_port: "udp://127.0.0.1:514"
verifier: verifier:
name: testinfra name: testinfra
lint:
name: flake8
additional_files_or_dirs: additional_files_or_dirs:
- ../../tests/* - ../../tests/*
scenario: scenario:
name: multiple name: multiple
test_sequence: test_sequence:
- lint
- cleanup - cleanup
- destroy - destroy
- dependency - dependency

View File

@@ -3,12 +3,18 @@ dependency:
name: galaxy name: galaxy
driver: driver:
name: docker name: docker
lint:
name: yamllint
enabled: True
options:
config-data:
ignore: ../../hosts.yml
platforms: platforms:
- name: oracle-host - name: oracle-host
groups: groups:
- example - example
children: children:
- oracle_swarm - oracle
image: ubuntu:16.04 image: ubuntu:16.04
privileged: true privileged: true
network_mode: host network_mode: host
@@ -16,22 +22,29 @@ platforms:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
provisioner: provisioner:
name: ansible name: ansible
lint:
name: ansible-lint
enabled: True
options:
r: ["bug"]
playbooks: playbooks:
prepare: ../prepare.yml prepare: ../prepare.yml
converge: ../../../deployment/site.yml converge: ../../../deployment/site.yml
inventory: inventory:
host_vars: host_vars:
oracle-host: oracle-host:
ORACLE_VALIDATOR_KEYSTORE_PATH: "../../../e2e-commons/keystore.json" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "6c48435bd464a53ed66ed62127c4dba8af75cf1a99a8ebe2680599948fbfbc6d"
ORACLE_VALIDATOR_KEYSTORE_PASSWORD: "12345678"
syslog_server_port: "udp://127.0.0.1:514" syslog_server_port: "udp://127.0.0.1:514"
verifier: verifier:
name: testinfra name: testinfra
lint:
name: flake8
additional_files_or_dirs: additional_files_or_dirs:
- ../../tests/* - ../../tests/*
scenario: scenario:
name: oracle name: oracle
test_sequence: test_sequence:
- lint
- cleanup - cleanup
- destroy - destroy
- dependency - dependency

View File

@@ -3,21 +3,22 @@ import pytest
import testinfra.utils.ansible_runner import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('oracle_swarm') os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('oracle')
@pytest.mark.parametrize("name", [ @pytest.mark.parametrize("name", [
("oracle_rabbit"), ("oracle_rabbit_1"),
("oracle_redis"), ("oracle_redis_1"),
("oracle_bridge_request"), ("oracle_bridge_request_1"),
("oracle_bridge_collected"), ("oracle_bridge_collected_1"),
("oracle_bridge_affirmation"), ("oracle_bridge_affirmation_1"),
("oracle_bridge_senderhome"), ("oracle_bridge_senderhome_1"),
("oracle_bridge_senderforeign"), ("oracle_bridge_senderforeign_1"),
("oracle_bridge_shutdown"), ("oracle_bridge_shutdown_1"),
]) ])
def test_docker_containers(host, name): def test_docker_containers(host, name):
assert host.docker(name) is not None container = host.docker(name)
assert container.is_running
@pytest.mark.parametrize("service", [ @pytest.mark.parametrize("service", [

View File

@@ -7,7 +7,7 @@ platforms:
- ultimate - ultimate
- amb - amb
children: children:
- oracle_swarm - oracle
image: ubuntu:16.04 image: ubuntu:16.04
privileged: true privileged: true
network_mode: host network_mode: host
@@ -21,10 +21,12 @@ provisioner:
inventory: inventory:
host_vars: host_vars:
oracle-amb-host: oracle-amb-host:
ORACLE_VALIDATOR_KEYSTORE_PATH: "../../../e2e-commons/keystore_ultimate.json" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_KEYSTORE_PASSWORD: "12345678" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
verifier: verifier:
name: testinfra name: testinfra
lint:
name: flake8
scenario: scenario:
name: ultimate-amb name: ultimate-amb
test_sequence: test_sequence:

View File

@@ -27,6 +27,8 @@ provisioner:
ORACLE_FOREIGN_START_BLOCK: 1 ORACLE_FOREIGN_START_BLOCK: 1
verifier: verifier:
name: testinfra name: testinfra
lint:
name: flake8
scenario: scenario:
name: ultimate-erc-to-native name: ultimate-erc-to-native
test_sequence: test_sequence:

View File

@@ -7,13 +7,6 @@ sokol-kovan:
ansible_user: ubuntu ansible_user: ubuntu
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
#syslog_server_port: "udp://127.0.0.1:514" #syslog_server_port: "udp://127.0.0.1:514"
oracle_swarm:
hosts:
127.0.0.1:
ansible_user: ubuntu
ORACLE_VALIDATOR_KEYSTORE_PATH: "/path/to/keystore.json"
ORACLE_VALIDATOR_KEYSTORE_PASSWORD: "12345678"
#syslog_server_port: "udp://127.0.0.1:514"
monitor: monitor:
hosts: hosts:
127.0.0.1: 127.0.0.1:

View File

@@ -0,0 +1,4 @@
# pre-release because it contains "CI Fixes for ansible 2.8"
molecule==2.22rc1
docker
flake8

View File

@@ -1,4 +1,4 @@
{ {
"live-restore": false, "live-restore": true,
"no-new-privileges": true "no-new-privileges": true
} }

View File

@@ -31,7 +31,6 @@
owner: "root" owner: "root"
group: "root" group: "root"
mode: "0755" mode: "0755"
when: skip_compose is undefined
- name: Upgrade pip version - name: Upgrade pip version
shell: pip3 install --upgrade pip==19.3.1 shell: pip3 install --upgrade pip==19.3.1
@@ -46,9 +45,6 @@
group: docker group: docker
createhome: yes createhome: yes
- name: reset ssh connection to allow user changes to affect ansible user
meta: reset_connection
- name: Install auditd - name: Install auditd
apt: apt:
name: auditd name: auditd

View File

@@ -1,6 +1,6 @@
/var/log/docker/*/docker.log { /var/log/docker/*/docker.log {
rotate 5 rotate 5
size 100M size 1G
compress compress
missingok missingok
delaycompress delaycompress
@@ -8,7 +8,7 @@
} }
/var/log/docker/*.log { /var/log/docker/*.log {
rotate 5 rotate 5
size 100M size 1G
compress compress
missingok missingok
delaycompress delaycompress

View File

@@ -7,7 +7,7 @@
loop_control: loop_control:
loop_var: file loop_var: file
- name: Set the oracle's containers local logs configuration file - name: Set the local container logs configuration file
template: template:
src: 31-oracle-docker.conf.j2 src: 31-oracle-docker.conf.j2
dest: /etc/rsyslog.d/31-oracle-docker.conf dest: /etc/rsyslog.d/31-oracle-docker.conf
@@ -15,22 +15,6 @@
group: root group: root
mode: 0644 mode: 0644
- name: Set the redis container local logs configuration file
template:
src: 32-redis-docker.conf.j2
dest: /etc/rsyslog.d/32-redis-docker.conf
owner: root
group: root
mode: 0644
- name: Set the rabbit MQ container local logs configuration file
template:
src: 33-rabbit-docker.conf.j2
dest: /etc/rsyslog.d/33-rabbit-docker.conf
owner: root
group: root
mode: 0644
- name: Set the log configuration file to send container logs to remote server - name: Set the log configuration file to send container logs to remote server
template: template:
src: 36-oracle-remote-logging.conf.j2 src: 36-oracle-remote-logging.conf.j2

View File

@@ -27,6 +27,17 @@
set_fact: set_fact:
ORACLE_VALIDATOR_ADDRESS: "{{ VADDRESS.stdout }}" ORACLE_VALIDATOR_ADDRESS: "{{ VADDRESS.stdout }}"
- name: Get foreign erc type
become_user: "{{ compose_service_user }}"
shell: docker-compose run --rm --entrypoint "node scripts/initialChecks.js" bridge_affirmation
args:
chdir: "{{ bridge_path }}/oracle"
register: ERCTYPE
- name: Set FOREIGN_ERC_TYPE variable
set_fact:
FOREIGN_ERC_TYPE: "{{ (ERCTYPE.stdout).foreignERC | default('') }}"
- name: Extend docker compose file for erc to native - name: Extend docker compose file for erc to native
set_fact: composefileoverride="-f docker-compose-transfer.yml" set_fact: composefileoverride="-f docker-compose-transfer.yml"
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"

View File

@@ -11,16 +11,9 @@ ORACLE_HOME_RPC_POLLING_INTERVAL={{ ORACLE_HOME_RPC_POLLING_INTERVAL }}
## Foreign contract ## Foreign contract
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }} COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
{% if ORACLE_FOREIGN_ARCHIVE_RPC_URL | default('') != '' %}
ORACLE_FOREIGN_ARCHIVE_RPC_URL={{ ORACLE_FOREIGN_ARCHIVE_RPC_URL }}
{% endif %}
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }} COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
ORACLE_FOREIGN_RPC_POLLING_INTERVAL={{ ORACLE_FOREIGN_RPC_POLLING_INTERVAL }} ORACLE_FOREIGN_RPC_POLLING_INTERVAL={{ ORACLE_FOREIGN_RPC_POLLING_INTERVAL }}
{% if ORACLE_TX_REDUNDANCY | default('') != '' %}
ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
{% endif %}
## Gasprice ## Gasprice
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %} {% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }} COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
@@ -54,28 +47,8 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
ORACLE_ALLOW_HTTP_FOR_RPC={{ "yes" if ORACLE_ALLOW_HTTP_FOR_RPC else "no" }} ORACLE_ALLOW_HTTP_FOR_RPC={{ "yes" if ORACLE_ALLOW_HTTP_FOR_RPC else "no" }}
ORACLE_QUEUE_URL={{ ORACLE_QUEUE_URL }} ORACLE_QUEUE_URL={{ ORACLE_QUEUE_URL }}
ORACLE_REDIS_URL={{ ORACLE_REDIS_URL }} ORACLE_REDIS_URL={{ ORACLE_REDIS_URL }}
{% if ORACLE_FOREIGN_TX_RESEND_INTERVAL | default('') != '' %} {% if ORACLE_TX_REDUNDANCY | default('') != '' %}
ORACLE_FOREIGN_TX_RESEND_INTERVAL={{ ORACLE_FOREIGN_TX_RESEND_INTERVAL }} ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
{% endif %}
{% if ORACLE_HOME_TX_RESEND_INTERVAL | default('') != '' %}
ORACLE_HOME_TX_RESEND_INTERVAL={{ ORACLE_HOME_TX_RESEND_INTERVAL }}
{% endif %}
## Emergency shutdown configuration
{% if ORACLE_SHUTDOWN_SERVICE_URL | default('') != '' %}
ORACLE_SHUTDOWN_SERVICE_URL={{ ORACLE_SHUTDOWN_SERVICE_URL }}
{% endif %}
{% if ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | default('') != '' %}
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL={{ ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL }}
{% endif %}
{% if ORACLE_SIDE_RPC_URL | default('') != '' %}
ORACLE_SIDE_RPC_URL={{ ORACLE_SIDE_RPC_URL }}
{% endif %}
{% if ORACLE_SHUTDOWN_CONTRACT_ADDRESS | default('') != '' %}
ORACLE_SHUTDOWN_CONTRACT_ADDRESS={{ ORACLE_SHUTDOWN_CONTRACT_ADDRESS }}
{% endif %}
{% if ORACLE_SHUTDOWN_CONTRACT_METHOD | default('') != '' %}
ORACLE_SHUTDOWN_CONTRACT_METHOD={{ ORACLE_SHUTDOWN_CONTRACT_METHOD }}
{% endif %} {% endif %}
{% if ORACLE_HOME_START_BLOCK | default('') != '' %} {% if ORACLE_HOME_START_BLOCK | default('') != '' %}

View File

@@ -1,11 +0,0 @@
$FileCreateMode 0644
template(name="DockerLogFileName_Redis" type="list") {
constant(value="/var/log/docker/")
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*redis.*)\\/[a-zA-Z0-9]+\\[")
constant(value="/docker.log")
}
if $programname contains 'oracle' and $programname contains 'redis' then \
?DockerLogFileName_Redis
$FileCreateMode 0600

View File

@@ -1,11 +0,0 @@
$FileCreateMode 0644
template(name="DockerLogFileName_Rabbit" type="list") {
constant(value="/var/log/docker/")
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*rabbit.*)\\/[a-zA-Z0-9]+\\[")
constant(value="/docker.log")
}
if $programname contains 'oracle' and $programname contains 'rabbit' then \
?DockerLogFileName_Rabbit
$FileCreateMode 0600

View File

@@ -1,9 +0,0 @@
---
bridge_path: "/home/{{ compose_service_user }}/bridge"
bridge_data_path: "/home/{{ compose_service_user }}/bridge_data"
ORACLE_ALLOW_HTTP_FOR_RPC: no
ORACLE_QUEUE_URL: amqp://rabbit
ORACLE_REDIS_URL: redis://redis
keyfile_path: "/root/.key"
keystore_path: "/root/.keystore.json"
oracle_image: poanetwork/tokenbridge-oracle:latest

View File

@@ -1,3 +0,0 @@
---
dependencies:
- { role: common, skip_repo: true, skip_compose: true }

View File

@@ -1,8 +0,0 @@
---
- name: Pull the containers images
community.docker.docker_image:
name: "{{ oracle_image }}"
source: pull
when: skip_pull is undefined
vars:
ansible_python_interpreter: /usr/bin/python3

View File

@@ -1,41 +0,0 @@
---
- name: Set the oracle's containers local logs configuration file
template:
src: 31-oracle-docker.conf.j2
dest: /etc/rsyslog.d/31-oracle-docker.conf
owner: root
group: root
mode: 0644
- name: Set the redis container local logs configuration file
template:
src: 32-redis-docker.conf.j2
dest: /etc/rsyslog.d/32-redis-docker.conf
owner: root
group: root
mode: 0644
- name: Set the rabbit MQ container local logs configuration file
template:
src: 33-rabbit-docker.conf.j2
dest: /etc/rsyslog.d/33-rabbit-docker.conf
owner: root
group: root
mode: 0644
- name: Set the log configuration file to send container logs to remote server
template:
src: 36-oracle-remote-logging.conf.j2
dest: /etc/rsyslog.d/36-oracle-remote-logging.conf
owner: root
group: root
mode: 0644
when: syslog_server_port is defined
- name: Discarding unwanted messages in rsyslog
blockinfile:
path: /etc/rsyslog.conf
insertbefore: "# Where to place spool and state files"
marker: "#{mark} add string to discarding unwanted messages"
content: ':msg, contains, "ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY" ~'
notify: restart rsyslog

View File

@@ -1,6 +0,0 @@
---
- include_tasks: pre_config.yml
- include_tasks: logging.yml
- include_tasks: jumpbox.yml
- include_tasks: post_config.yml
- include_tasks: servinstall.yml

View File

@@ -1,46 +0,0 @@
---
- name: Get blocks
become_user: "{{ compose_service_user }}"
shell: docker run --env-file .env --rm {{ oracle_image }} scripts/getValidatorStartBlocks.js
args:
chdir: "{{ bridge_path }}/oracle"
register: BLOCKS
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
- name: Write blocks
blockinfile:
path: "{{ bridge_path }}/oracle/.env"
marker: "## {mark} Calculated by scripts/getValidatorStartBlocks.js"
block: |
ORACLE_HOME_START_BLOCK={{ (BLOCKS.stdout | from_json).homeStartBlock }}
ORACLE_FOREIGN_START_BLOCK={{ (BLOCKS.stdout | from_json).foreignStartBlock }}
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
- name: Copy keystore file
copy:
src: "{{ ORACLE_VALIDATOR_KEYSTORE_PATH }}"
dest: "{{ keystore_path }}"
owner: root
group: root
mode: 0600
- name: Create swarm secret
community.docker.docker_secret:
name: oracle_keystore
state: present
data_src: "{{ keystore_path }}"
vars:
ansible_python_interpreter: /usr/bin/python3
- name: Remove unencrypted keystore file
file:
path: "{{ keystore_path }}"
state: absent
- name: Install .key config
template:
src: key.j2
dest: "{{ keyfile_path }}"
owner: root
group: root
mode: 0600

View File

@@ -1,47 +0,0 @@
---
- name: Init docker swarm
community.docker.docker_swarm:
state: present
autolock_managers: yes
listen_addr: 127.0.0.1:2377
vars:
ansible_python_interpreter: /usr/bin/python3
- name: Get unlock token
community.docker.docker_swarm_info:
unlock_key: yes
register: result
vars:
ansible_python_interpreter: /usr/bin/python3
- name: Print unlock token
debug:
var: result.swarm_unlock_key
- name: Create oracle directory
file:
path: "{{ bridge_path }}/oracle"
state: directory
mode: '0755'
- name: Create rabbitmq directory
file:
path: "{{ bridge_data_path }}/{{ item }}"
state: directory
mode: '0775'
loop:
- rabbitmq
- redis
- name: Install .env config
template:
src: .env.j2
dest: "{{ bridge_path }}/oracle/.env"
owner: "{{ compose_service_user }}"
mode: '0640'
- name: Install docker-compose file
template:
src: docker-compose.yml.j2
dest: "{{ bridge_path }}/oracle/docker-compose.yml"
mode: '0755'

View File

@@ -1,19 +0,0 @@
# This role creates a poabridge service which is designed to manage docker-compose bridge deployment.
# /etc/init.d/poabridge start, status, stop, restart - does what the services usually do in such cases.
---
- name: "Set poabridge service"
template:
src: poabridge.j2
dest: "/etc/init.d/poabridge"
owner: root
mode: 755
- name: "Enable the service"
service:
name: "poabridge"
state: started
enabled: yes
use: service
- name: Start the service
shell: service poabridge start

View File

@@ -1,86 +0,0 @@
## General settings
ORACLE_BRIDGE_MODE={{ ORACLE_BRIDGE_MODE }}
{% if ORACLE_LOG_LEVEL | default('') != '' %}
ORACLE_LOG_LEVEL={{ ORACLE_LOG_LEVEL }}
{% endif %}
## Home contract
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
ORACLE_HOME_RPC_POLLING_INTERVAL={{ ORACLE_HOME_RPC_POLLING_INTERVAL }}
## Foreign contract
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
{% if ORACLE_FOREIGN_ARCHIVE_RPC_URL | default('') != '' %}
ORACLE_FOREIGN_ARCHIVE_RPC_URL={{ ORACLE_FOREIGN_ARCHIVE_RPC_URL }}
{% endif %}
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
ORACLE_FOREIGN_RPC_POLLING_INTERVAL={{ ORACLE_FOREIGN_RPC_POLLING_INTERVAL }}
{% if ORACLE_TX_REDUNDANCY | default('') != '' %}
ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
{% endif %}
## Gasprice
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
{% endif %}
{% if COMMON_HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
{% endif %}
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
{% if ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL | default('') != '' %}
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL={{ ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL }}
{% endif %}
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
{% endif %}
{% if COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
{% endif %}
{% if COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE | default('') != '' %}
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
{% endif %}
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
{% if ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | default('') != '' %}
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL={{ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL }}
{% endif %}
{% if COMMON_FOREIGN_GAS_PRICE_FACTOR | default('') != '' %}
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
{% endif %}
## Transport configuration
ORACLE_ALLOW_HTTP_FOR_RPC={{ "yes" if ORACLE_ALLOW_HTTP_FOR_RPC else "no" }}
ORACLE_QUEUE_URL={{ ORACLE_QUEUE_URL }}
ORACLE_REDIS_URL={{ ORACLE_REDIS_URL }}
{% if ORACLE_FOREIGN_TX_RESEND_INTERVAL | default('') != '' %}
ORACLE_FOREIGN_TX_RESEND_INTERVAL={{ ORACLE_FOREIGN_TX_RESEND_INTERVAL }}
{% endif %}
{% if ORACLE_HOME_TX_RESEND_INTERVAL | default('') != '' %}
ORACLE_HOME_TX_RESEND_INTERVAL={{ ORACLE_HOME_TX_RESEND_INTERVAL }}
{% endif %}
## Emergency shutdown configuration
{% if ORACLE_SHUTDOWN_SERVICE_URL | default('') != '' %}
ORACLE_SHUTDOWN_SERVICE_URL={{ ORACLE_SHUTDOWN_SERVICE_URL }}
{% endif %}
{% if ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | default('') != '' %}
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL={{ ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL }}
{% endif %}
{% if ORACLE_SIDE_RPC_URL | default('') != '' %}
ORACLE_SIDE_RPC_URL={{ ORACLE_SIDE_RPC_URL }}
{% endif %}
{% if ORACLE_SHUTDOWN_CONTRACT_ADDRESS | default('') != '' %}
ORACLE_SHUTDOWN_CONTRACT_ADDRESS={{ ORACLE_SHUTDOWN_CONTRACT_ADDRESS }}
{% endif %}
{% if ORACLE_SHUTDOWN_CONTRACT_METHOD | default('') != '' %}
ORACLE_SHUTDOWN_CONTRACT_METHOD={{ ORACLE_SHUTDOWN_CONTRACT_METHOD }}
{% endif %}
{% if ORACLE_HOME_START_BLOCK | default('') != '' %}
ORACLE_HOME_START_BLOCK={{ ORACLE_HOME_START_BLOCK }}
{% endif %}
{% if ORACLE_FOREIGN_START_BLOCK | default('') != '' %}
ORACLE_FOREIGN_START_BLOCK={{ ORACLE_FOREIGN_START_BLOCK }}
{% endif %}

View File

@@ -1,11 +0,0 @@
$FileCreateMode 0644
template(name="DockerLogFileName_Oracle" type="list") {
constant(value="/var/log/docker/")
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="bridge_(.*)\\/[a-zA-Z0-9]+\\[")
constant(value="/docker.log")
}
if $programname startswith 'oracle_bridge_' then \
?DockerLogFileName_Oracle
$FileCreateMode 0600

View File

@@ -1,11 +0,0 @@
$FileCreateMode 0644
template(name="DockerLogFileName_Redis" type="list") {
constant(value="/var/log/docker/")
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*redis.*)\\/[a-zA-Z0-9]+\\[")
constant(value="/docker.log")
}
if $programname contains 'oracle' and $programname contains 'redis' then \
?DockerLogFileName_Redis
$FileCreateMode 0600

View File

@@ -1,11 +0,0 @@
$FileCreateMode 0644
template(name="DockerLogFileName_Rabbit" type="list") {
constant(value="/var/log/docker/")
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*rabbit.*)\\/[a-zA-Z0-9]+\\[")
constant(value="/docker.log")
}
if $programname contains 'oracle' and $programname contains 'rabbit' then \
?DockerLogFileName_Rabbit
$FileCreateMode 0600

View File

@@ -1,15 +0,0 @@
if $programname startswith 'oracle_bridge_' then {
action(
type="omfwd"
protocol="{{ syslog_server_port.split(":")[0] }}"
target="{{ (syslog_server_port.split(":")[1])[2:] }}"
port="{{ syslog_server_port.split(":")[2] }}"
template="RemoteForwardFormat"
queue.SpoolDirectory="/var/spool/rsyslog"
queue.FileName="remote"
queue.MaxDiskSpace="1g"
queue.SaveOnShutdown="on"
queue.Type="LinkedList"
ResendLastMSGOnReconnect="on"
)
}

View File

@@ -1,144 +0,0 @@
version: '3.9'
x-deploy: &x-deploy
resources:
limits:
cpus: '0.3'
memory: 500M
reservations:
memory: 100M
x-keystore-access: &x-keystore-access
environment:
ORACLE_VALIDATOR_KEYSTORE_PATH: /run/secrets/oracle_keystore
ORACLE_VALIDATOR_KEYSTORE_PASSWORD:
secrets:
- oracle_keystore
x-logging: &x-logging
driver: 'syslog'
options: {tag: '{{ '{{.Name}}/{{.ID}}' }}' }
services:
rabbit:
image: rabbitmq:3
hostname: rabbit
deploy: *x-deploy
logging: *x-logging
environment: [ 'RABBITMQ_NODENAME=node@rabbit' ]
networks:
- net_rabbit_bridge_request
- net_rabbit_bridge_collected
- net_rabbit_bridge_affirmation
- net_rabbit_bridge_senderhome
- net_rabbit_bridge_senderforeign
volumes: [ '{{ bridge_data_path }}/rabbitmq:/var/lib/rabbitmq/mnesia' ]
redis:
image: redis:4
hostname: redis
deploy: *x-deploy
logging: *x-logging
command: [ redis-server, --appendonly, 'yes' ]
networks:
- net_db_bridge_request
- net_db_bridge_collected
- net_db_bridge_affirmation
- net_db_bridge_senderhome
- net_db_bridge_senderforeign
- net_db_bridge_shutdown
volumes: [ '{{ bridge_data_path }}/redis:/data' ]
bridge_request:
image: {{ oracle_image }}
deploy: *x-deploy
logging: *x-logging
env_file: ./.env
<<: *x-keystore-access
entrypoint: yarn watcher:signature-request
networks:
- net_db_bridge_request
- net_rabbit_bridge_request
bridge_collected:
image: {{ oracle_image }}
deploy: *x-deploy
env_file: ./.env
entrypoint: yarn watcher:collected-signatures
networks:
- net_db_bridge_collected
- net_rabbit_bridge_collected
bridge_affirmation:
image: {{ oracle_image }}
deploy: *x-deploy
logging: *x-logging
env_file: ./.env
entrypoint: yarn watcher:affirmation-request
networks:
- net_db_bridge_affirmation
- net_rabbit_bridge_affirmation
bridge_senderhome:
image: {{ oracle_image }}
deploy: *x-deploy
env_file: ./.env
<<: *x-keystore-access
entrypoint: yarn sender:home
networks:
- net_db_bridge_senderhome
- net_rabbit_bridge_senderhome
bridge_senderforeign:
image: {{ oracle_image }}
deploy: *x-deploy
logging: *x-logging
env_file: ./.env
<<: *x-keystore-access
entrypoint: yarn sender:foreign
networks:
- net_db_bridge_senderforeign
- net_rabbit_bridge_senderforeign
bridge_shutdown:
image: {{ oracle_image }}
deploy: *x-deploy
env_file: ./.env
entrypoint: yarn manager:shutdown
networks:
- net_db_bridge_shutdown
{% if ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" %}
bridge_transfer:
image: {{ oracle_image }}
deploy: *x-deploy
logging: *x-logging
env_file: ./.env
entrypoint: yarn watcher:transfer
networks:
- net_db_bridge_transfer
- net_rabbit_bridge_transfer
{% endif %}
{% if ORACLE_BRIDGE_MODE == "ARBITRARY_MESSAGE" %}
bridge_information:
image: {{ oracle_image }}
deploy: *x-deploy
logging: *x-logging
env_file: ./.env
entrypoint: yarn watcher:information-request
networks:
- net_db_bridge_information
- net_rabbit_bridge_information
{% endif %}
networks:
net_db_bridge_request:
net_db_bridge_collected:
net_db_bridge_affirmation:
net_db_bridge_senderhome:
net_db_bridge_senderforeign:
net_db_bridge_shutdown:
{% if ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" %}
net_db_bridge_transfer:
net_rabbit_bridge_transfer:
{% endif %}
{% if ORACLE_BRIDGE_MODE == "ARBITRARY_MESSAGE" %}
net_db_bridge_information:
net_rabbit_bridge_information:
{% endif %}
net_rabbit_bridge_request:
net_rabbit_bridge_collected:
net_rabbit_bridge_affirmation:
net_rabbit_bridge_senderhome:
net_rabbit_bridge_senderforeign:
secrets:
oracle_keystore:
external: true

View File

@@ -1,2 +0,0 @@
## Validator-specific options
ORACLE_VALIDATOR_KEYSTORE_PASSWORD={{ ORACLE_VALIDATOR_KEYSTORE_PASSWORD }}

View File

@@ -1,66 +0,0 @@
#! /bin/bash
### BEGIN INIT INFO
# Provides: poabridge
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridge_path + '/oracle' if bridge_path[:1] != "/" else bridge_path + '/oracle' }}"
#Getting path to private key file and variable name for parsing key file
source {{ keyfile_path }}
start(){
echo "Starting bridge.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" docker stack rm oracle
sudo -u "{{ compose_service_user }}" "ORACLE_VALIDATOR_KEYSTORE_PASSWORD=$ORACLE_VALIDATOR_KEYSTORE_PASSWORD" docker stack deploy oracle -c docker-compose.yml
}
stop(){
echo "Stopping bridge.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" docker stack rm oracle
sleep 2
}
status(){
echo "Bridge status:"
cd $WORKDIR
sudo -u "{{ compose_service_user }}" docker service ls
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
echo "Restarting bridge.."
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View File

@@ -4,11 +4,6 @@
become: true become: true
roles: roles:
- { role: oracle } - { role: oracle }
- name: Install Oracle as a Docker Swarm service
hosts: oracle_swarm
become: true
roles:
- { role: oracle_swarm }
- name: Install Monitor - name: Install Monitor
hosts: monitor hosts: monitor
become: true become: true

View File

@@ -6,12 +6,14 @@ COMMON_HOME_RPC_URL=http://parity1:8545
COMMON_FOREIGN_RPC_URL=http://parity2:8545 COMMON_FOREIGN_RPC_URL=http://parity2:8545
COMMON_HOME_BRIDGE_ADDRESS=0x8397be90BCF57b0B71219f555Fe121b22e5a994C COMMON_HOME_BRIDGE_ADDRESS=0x8397be90BCF57b0B71219f555Fe121b22e5a994C
COMMON_FOREIGN_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E COMMON_FOREIGN_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E
COMMON_HOME_GAS_PRICE_SUPPLIER_URL= ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=1000000000 COMMON_HOME_GAS_PRICE_FALLBACK=1000000000
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000 ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000
COMMON_HOME_GAS_PRICE_FACTOR=1 COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL= COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=10000000000 COMMON_FOREIGN_GAS_PRICE_FALLBACK=10000000000
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000 ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000

View File

@@ -6,6 +6,8 @@ COMMON_HOME_RPC_URL=http://parity1:8545
COMMON_FOREIGN_RPC_URL=http://parity2:8545 COMMON_FOREIGN_RPC_URL=http://parity2:8545
COMMON_HOME_BRIDGE_ADDRESS=0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c COMMON_HOME_BRIDGE_ADDRESS=0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c
COMMON_FOREIGN_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624 COMMON_FOREIGN_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624
ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=1 COMMON_HOME_GAS_PRICE_FALLBACK=1

View File

@@ -38,7 +38,7 @@
"ercToNativeBridge": { "ercToNativeBridge": {
"home": "0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c", "home": "0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c",
"foreign": "0x32198D570fffC7033641F8A9094FFDCaAEF42624", "foreign": "0x32198D570fffC7033641F8A9094FFDCaAEF42624",
"foreignToken": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
"monitor": "http://monitor-erc20-native:3012/bridge" "monitor": "http://monitor-erc20-native:3012/bridge"
}, },
"amb": { "amb": {
@@ -49,12 +49,6 @@
"blockedHomeBox": "0xF9698Eb93702dfdd0e2d802088d4c21822a8A977", "blockedHomeBox": "0xF9698Eb93702dfdd0e2d802088d4c21822a8A977",
"monitor": "http://monitor-amb:3013/bridge" "monitor": "http://monitor-amb:3013/bridge"
}, },
"amb2": {
"home": "0x5A42E119990c3F3A80Fea20aAF4c3Ff4DB240Cc9",
"foreign": "0x897527391ad3837604973d78D3514f44c36AB9FC",
"homeBox": "0xb008E9076fCbDB2C3AF84225Bc07Eb35B2bE5ECD",
"foreignBox": "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127"
},
"homeRPC": { "homeRPC": {
"URL": "http://parity1:8545", "URL": "http://parity1:8545",
"ID": "77" "ID": "77"

View File

@@ -32,7 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
FOREIGN_REWARDABLE=false FOREIGN_REWARDABLE=false
BLOCK_REWARD_ADDRESS=0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B BLOCK_REWARD_ADDRESS=0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B
ERC20_TOKEN_ADDRESS=0x6B175474E89094C44Da98b954EedeAC495271d0F ERC20_TOKEN_ADDRESS=0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9
REQUIRED_NUMBER_OF_VALIDATORS=1 REQUIRED_NUMBER_OF_VALIDATORS=1
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d" VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"

View File

@@ -1 +0,0 @@
[{"version":3,"id":"e7e64a1b-5e61-4c17-a473-963d2bbb59e5","address":"d138a69eb2da1c3518e792737c820b23cce62e4b","crypto":{"ciphertext":"f6ddf0b2638fb9fd5777de2aa07937b5ee9bc17acc74c8e6e6580e2dfd0d3de6","cipherparams":{"iv":"bcdbc5af4582887e5cdcf264e8d5b80d"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"f9e621918990e64e278e0fb8cf0343219e1cceaea8547d50fae452ad8f42f231","n":8192,"r":8,"p":1},"mac":"34149cd0b3ddea52588825d403fb75cfb8b864b616d455f75f2de001cc2601ed"}}]

View File

@@ -35,9 +35,3 @@ echo -e "\n\n############ Deploying one more test contract for amb ############\
cd "$DEPLOY_PATH" cd "$DEPLOY_PATH"
node src/utils/deployTestBox.js node src/utils/deployTestBox.js
cd - > /dev/null cd - > /dev/null
echo -e "\n\n############ Deploying one more amb without oracle for confirm relay tests ############\n"
cp "$ENVS_PATH/amb.env" "$DEPLOY_PATH/.env"
cd "$DEPLOY_PATH"
node deploy.js
cd - > /dev/null

View File

@@ -15,46 +15,42 @@ docker network create --driver bridge ultimate || true
docker-compose up -d parity1 parity2 e2e docker-compose up -d parity1 parity2 e2e
startValidator () { startValidator () {
db_env="-e ORACLE_QUEUE_URL=amqp://$3 -e ORACLE_REDIS_URL=redis://$2" db_env="-e ORACLE_QUEUE_URL=amqp://$4 -e ORACLE_REDIS_URL=redis://$3"
docker-compose $1 run -d --name $2 redis docker-compose $1 run -d --name $3 redis
docker-compose $1 run -d --name $3 rabbit docker-compose $1 run -d --name $4 rabbit
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
docker-compose $1 run $oraclePK $db_env -d oracle-erc20-native yarn watcher:signature-request docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:signature-request
docker-compose $1 run $oracleAddr $db_env -d oracle-erc20-native yarn watcher:collected-signatures docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:collected-signatures
docker-compose $1 run $oracleAddr $db_env -d oracle-erc20-native yarn watcher:affirmation-request docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:affirmation-request
docker-compose $1 run $oracleAddr $db_env -d oracle-erc20-native yarn watcher:transfer docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:transfer
fi fi
if [[ -z "$MODE" || "$MODE" == amb ]]; then if [[ -z "$MODE" || "$MODE" == amb ]]; then
docker-compose $1 run $oraclePK $db_env -d oracle-amb yarn watcher:signature-request docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:signature-request
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:collected-signatures docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:collected-signatures
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:affirmation-request docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:affirmation-request
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:information-request docker-compose $1 run $2 $db_env -d oracle-amb yarn watcher:information-request
fi fi
docker-compose $1 run $oraclePK $db_env -d oracle-amb yarn sender:home docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:home
docker-compose $1 run $oraclePK $db_env -d oracle-amb yarn sender:foreign docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:foreign
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn manager:shutdown docker-compose $1 run $2 $db_env -d oracle-amb yarn manager:shutdown
} }
while [ "$1" != "" ]; do while [ "$1" != "" ]; do
if [ "$1" == "oracle" ]; then if [ "$1" == "oracle" ]; then
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" startValidator "-p validator1" "" redis rabbit
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
startValidator "-p validator1" redis rabbit
fi fi
if [ "$1" == "oracle-validator-2" ]; then if [ "$1" == "oracle-validator-2" ]; then
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4" oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513" startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
startValidator "-p validator2" redis2 rabbit2
fi fi
if [ "$1" == "oracle-validator-3" ]; then if [ "$1" == "oracle-validator-3" ]; then
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d" oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1" startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
startValidator "-p validator3" redis3 rabbit3
fi fi
if [ "$1" == "alm" ]; then if [ "$1" == "alm" ]; then
@@ -88,38 +84,13 @@ while [ "$1" != "" ]; do
if [ "$1" == "alm-e2e" ]; then if [ "$1" == "alm-e2e" ]; then
MODE=amb MODE=amb
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" startValidator "-p validator1" "" redis rabbit
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
startValidator "-p validator1" redis rabbit
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4" oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513" startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
startValidator "-p validator2" redis2 rabbit2
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d" oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1" startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
startValidator "-p validator3" redis3 rabbit3
fi
if [ "$1" == "generate-amb-tx" ]; then
docker-compose run e2e yarn workspace oracle-e2e run generate-amb-tx
fi
if [ "$1" == "manual-amb-relay" ]; then
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
env="-e COMMON_HOME_BRIDGE_ADDRESS=0x5A42E119990c3F3A80Fea20aAF4c3Ff4DB240Cc9 -e COMMON_FOREIGN_BRIDGE_ADDRESS=0x897527391ad3837604973d78D3514f44c36AB9FC"
# these tx hash are hardcoded and need to be updated manually
# once e2e environment setup process is changed
echo '0xea625a823bc5018dc3a4efe349f623e5ebb8c987b55f44d50d6556f42af9a400' > txHashes.txt
docker-compose -p validator1 run -v $(pwd)/txHashes.txt:/tmp/txHashes.txt $oraclePK $env oracle-amb yarn confirm:affirmation-request \
/tmp/txHashes.txt \
0x031c42e44485002c9215a5b1b75e9516131485ce29884a58765bf7a0038538f9
docker-compose -p validator1 run $oraclePK $env oracle-amb yarn confirm:signature-request \
0x1506a18af91afe732167ccbc178b55fc2547da4a814d13c015b6f496cf171754 | tee .tmp.log
tx_hash=$(cat .tmp.log | grep generatedTransactionHash | jq -r .generatedTransactionHash)
rm .tmp.log
rm txHashes.txt
docker-compose -p validator1 run $oraclePK $env oracle-amb yarn confirm:collected-signatures $tx_hash
fi fi
shift # Shift all the parameters down by one shift # Shift all the parameters down by one

View File

@@ -1,9 +1,10 @@
require('dotenv').config() require('dotenv').config()
const logger = require('./logger')('detectMediators.js') const logger = require('./logger')('detectMediators.js')
const { isHomeContract, isForeignContract } = require('./utils/web3Cache')
const eventsInfo = require('./utils/events') const eventsInfo = require('./utils/events')
const { getHomeTxSender, getForeignTxSender, isHomeContract, isForeignContract } = require('./utils/web3Cache') const { getHomeTxSender, getForeignTxSender } = require('./utils/web3Cache')
const { addExecutionStatus, addRetrievalStatus } = require('./utils/message') const { addExecutionStatus } = require('./utils/message')
const { normalizeAMBMessageEvent, normalizeAMBInfoRequest } = require('../commons') const { normalizeAMBMessageEvent } = require('../commons')
function countInteractions(requests) { function countInteractions(requests) {
const stats = {} const stats = {}
@@ -29,41 +30,6 @@ function countInteractions(requests) {
return stats return stats
} }
function countInfoRequests(requests) {
const stats = {}
requests.forEach(msg => {
if (!stats[msg.sender]) {
stats[msg.sender] = {}
}
if (!stats[msg.sender][msg.requestSelector]) {
stats[msg.sender][msg.requestSelector] = {
callSucceeded: {
callbackSucceeded: 0,
callbackFailed: 0
},
callFailed: {
callbackSucceeded: 0,
callbackFailed: 0
},
pending: 0
}
}
const stat = stats[msg.sender][msg.requestSelector]
if (msg.callStatus === true && msg.callbackStatus === true) {
stat.callSucceeded.callbackSucceeded += 1
} else if (msg.callStatus === true && msg.callbackStatus === false) {
stat.callSucceeded.callbackFailed += 1
} else if (msg.callStatus === false && msg.callbackStatus === true) {
stat.callFailed.callbackSucceeded += 1
} else if (msg.callStatus === false && msg.callbackStatus === false) {
stat.callFailed.callbackFailed += 1
} else {
stat.pending += 1
}
})
return stats
}
const normalize = event => ({ const normalize = event => ({
...normalizeAMBMessageEvent(event), ...normalizeAMBMessageEvent(event),
txHash: event.transactionHash, txHash: event.transactionHash,
@@ -122,13 +88,10 @@ async function main(mode) {
homeToForeignRequests, homeToForeignRequests,
foreignToHomeRequests, foreignToHomeRequests,
homeToForeignConfirmations, homeToForeignConfirmations,
foreignToHomeConfirmations, foreignToHomeConfirmations
informationRequests,
informationResponses
} = await eventsInfo(mode) } = await eventsInfo(mode)
const homeToForeign = homeToForeignRequests.map(normalize).map(addExecutionStatus(homeToForeignConfirmations)) const homeToForeign = homeToForeignRequests.map(normalize).map(addExecutionStatus(homeToForeignConfirmations))
const foreignToHome = foreignToHomeRequests.map(normalize).map(addExecutionStatus(foreignToHomeConfirmations)) const foreignToHome = foreignToHomeRequests.map(normalize).map(addExecutionStatus(foreignToHomeConfirmations))
const infoRequests = informationRequests.map(normalizeAMBInfoRequest).map(addRetrievalStatus(informationResponses))
for (const event of homeToForeign) { for (const event of homeToForeign) {
// AMB contract emits a single UserRequestForSignature event for every home->foreign request. // AMB contract emits a single UserRequestForSignature event for every home->foreign request.
@@ -183,7 +146,6 @@ async function main(mode) {
floatingMediators, floatingMediators,
remotelyControlledMediators, remotelyControlledMediators,
unknown, unknown,
informationReceivers: countInfoRequests(infoRequests),
lastChecked: Math.floor(Date.now() / 1000) lastChecked: Math.floor(Date.now() / 1000)
} }
} }

View File

@@ -46,19 +46,29 @@ async function main(bridgeMode, eventsInfo) {
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const erc20Address = await foreignBridge.methods.erc20token().call() const erc20Address = await foreignBridge.methods.erc20token().call()
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address) const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
let investedAmountInDai = 0
let bridgeDsrBalance = 0
let displayChaiToken = false
try {
logger.debug('calling foreignBridge.methods.isChaiTokenEnabled')
if (await foreignBridge.methods.isChaiTokenEnabled().call()) {
displayChaiToken = true
logger.debug('calling foreignBridge.methods.investedAmountInDai')
investedAmountInDai = await foreignBridge.methods.investedAmountInDai().call()
logger.debug('calling foreignBridge.methods.dsrBalance')
bridgeDsrBalance = await foreignBridge.methods.dsrBalance().call()
} else {
logger.debug('Chai token is currently disabled')
}
} catch (e) {
logger.debug('Methods for chai token are not present')
}
logger.debug('calling erc20Contract.methods.balanceOf') logger.debug('calling erc20Contract.methods.balanceOf')
const foreignErc20Balance = await erc20Contract.methods const foreignErc20Balance = await erc20Contract.methods
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS) .balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
.call({}, foreignDelayedBlockNumber) .call({}, foreignDelayedBlockNumber)
let foreignErc20BalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue)
try {
logger.debug('calling foreignBridge.methods.investedAmount')
const invested = await foreignBridge.methods.investedAmount(erc20Address).call({}, foreignDelayedBlockNumber)
foreignErc20BalanceBN = foreignErc20BalanceBN.plus(invested)
} catch (_) {
logger.debug('compounding related methods are not present in the foreign bridge')
}
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS) const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS)
logger.debug('calling homeBridge.methods.blockRewardContract') logger.debug('calling homeBridge.methods.blockRewardContract')
@@ -74,16 +84,30 @@ async function main(bridgeMode, eventsInfo) {
const mintedCoinsBN = new BN(mintedCoins) const mintedCoinsBN = new BN(mintedCoins)
const burntCoinsBN = new BN(burntCoins) const burntCoinsBN = new BN(burntCoins)
const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN) const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN)
const foreignErc20BalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue)
const investedAmountInDaiBN = new BN(investedAmountInDai)
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
const diff = foreignErc20BalanceBN
.plus(investedAmountInDaiBN)
.minus(totalSupplyBN)
.toFixed()
const foreign = {
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
}
if (displayChaiToken) {
foreign.investedErc20Balance = Web3Utils.fromWei(investedAmountInDai)
foreign.accumulatedInterest = Web3Utils.fromWei(bridgeDsrBalanceBN.minus(investedAmountInDaiBN).toString(10))
}
const diff = foreignErc20BalanceBN.minus(totalSupplyBN).toFixed()
logger.debug('Done') logger.debug('Done')
return { return {
home: { home: {
totalSupply: Web3Utils.fromWei(totalSupplyBN.toFixed()) totalSupply: Web3Utils.fromWei(totalSupplyBN.toFixed())
}, },
foreign: { foreign,
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
},
balanceDiff: Number(Web3Utils.fromWei(diff)), balanceDiff: Number(Web3Utils.fromWei(diff)),
...blockRanges, ...blockRanges,
lastChecked: Math.floor(Date.now() / 1000) lastChecked: Math.floor(Date.now() / 1000)

View File

@@ -21,9 +21,7 @@ async function main(bridgeMode, eventsInfo) {
homeToForeignConfirmations, homeToForeignConfirmations,
homeToForeignRequests, homeToForeignRequests,
foreignToHomeConfirmations, foreignToHomeConfirmations,
foreignToHomeRequests, foreignToHomeRequests
informationRequests,
informationResponses
} = eventsInfo } = eventsInfo
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) { if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
@@ -36,9 +34,7 @@ async function main(bridgeMode, eventsInfo) {
fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length, fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
home: { home: {
toForeign: homeToForeignRequests.length, toForeign: homeToForeignRequests.length,
fromForeign: foreignToHomeConfirmations.length, fromForeign: foreignToHomeConfirmations.length
informationRequests: informationRequests.length,
informationResponses: informationResponses.length
}, },
foreign: { foreign: {
fromHome: homeToForeignConfirmations.length, fromHome: homeToForeignConfirmations.length,

View File

@@ -11,6 +11,7 @@ const {
OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI
} = require('../../commons') } = require('../../commons')
const { normalizeEventInformation } = require('./message') const { normalizeEventInformation } = require('./message')
const { filterTransferBeforeES } = require('./tokenUtils')
const { writeFile, readCacheFile } = require('./file') const { writeFile, readCacheFile } = require('./file')
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./web3') const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./web3')
const { getPastEvents } = require('./web3Cache') const { getPastEvents } = require('./web3Cache')
@@ -100,6 +101,21 @@ async function main(mode) {
chain: 'home' chain: 'home'
})).map(normalizeEvent) })).map(normalizeEvent)
homeToForeignRequests = [...homeToForeignRequests, ...homeToForeignRequestsNew] homeToForeignRequests = [...homeToForeignRequests, ...homeToForeignRequestsNew]
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
const used = {}
const total = homeToForeignRequests.length
const onlyFirstUniqueFilter = e => {
const msgId = e.returnValues.messageId || e.transactionHash
if (used[msgId]) {
return false
}
used[msgId] = true
return true
}
homeToForeignRequests = homeToForeignRequests.filter(onlyFirstUniqueFilter)
const dropped = total - homeToForeignRequests.length
logger.debug(`Dropped ${dropped}/${total} UserRequestForSignature events with same message id`)
}
logger.debug("calling foreignBridge.getPastEvents('RelayedMessage')") logger.debug("calling foreignBridge.getPastEvents('RelayedMessage')")
const homeToForeignConfirmations = (await getPastEvents(foreignBridge, { const homeToForeignConfirmations = (await getPastEvents(foreignBridge, {
@@ -128,27 +144,6 @@ async function main(mode) {
})).map(normalizeEvent) })).map(normalizeEvent)
foreignToHomeRequests = [...foreignToHomeRequests, ...foreignToHomeRequestsNew] foreignToHomeRequests = [...foreignToHomeRequests, ...foreignToHomeRequestsNew]
let informationRequests
let informationResponses
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
logger.debug("calling homeBridge.getPastEvents('UserRequestForInformation')")
informationRequests = (await getPastEvents(homeBridge, {
event: 'UserRequestForInformation',
fromBlock: MONITOR_HOME_START_BLOCK,
toBlock: homeDelayedBlockNumber,
chain: 'home'
})).map(normalizeEvent)
logger.debug("calling foreignBridge.getPastEvents('InformationRetrieved')")
informationResponses = (await getPastEvents(homeBridge, {
event: 'InformationRetrieved',
fromBlock: MONITOR_HOME_START_BLOCK,
toBlock: homeBlockNumber,
safeToBlock: homeDelayedBlockNumber,
chain: 'home'
})).map(normalizeEvent)
}
if (isExternalErc20) { if (isExternalErc20) {
logger.debug("calling erc20Contract.getPastEvents('Transfer')") logger.debug("calling erc20Contract.getPastEvents('Transfer')")
let transferEvents = (await getPastEvents(erc20Contract, { let transferEvents = (await getPastEvents(erc20Contract, {
@@ -159,32 +154,80 @@ async function main(mode) {
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS } filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
}, },
chain: 'foreign' chain: 'foreign'
}))
.map(normalizeEvent)
.filter(e => e.recipient !== ZERO_ADDRESS) // filter mint operation during SCD-to-MCD swaps
.filter(e => e.recipient.toLowerCase() !== '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643') // filter cDai withdraws during compounding
// Get transfer events for each previously used Sai token
const saiTokenAddress = '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, saiTokenAddress)
logger.debug('Half duplex token:', saiTokenAddress)
logger.debug("calling halfDuplexTokenContract.getPastEvents('Transfer')")
// https://etherscan.io/tx/0xd0c3c92c94e05bc71256055ce8c4c993e047f04e04f3283a04e4cb077b71f6c6
const blockNumberHalfDuplexDisabled = 9884448
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
event: 'Transfer',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: Math.min(blockNumberHalfDuplexDisabled, foreignDelayedBlockNumber),
options: {
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
},
chain: 'foreign'
})).map(normalizeEvent) })).map(normalizeEvent)
transferEvents = [...halfDuplexTransferEvents, ...transferEvents] let directTransfers = transferEvents
const tokensSwappedAbiExists = FOREIGN_ABI.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
if (tokensSwappedAbiExists) {
logger.debug('collecting half duplex tokens participated in the bridge balance')
logger.debug("calling foreignBridge.getPastEvents('TokensSwapped')")
const tokensSwappedEvents = await getPastEvents(foreignBridge, {
event: 'TokensSwapped',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignBlockNumber,
chain: 'foreign',
safeToBlock: foreignDelayedBlockNumber
})
// Get token swap events emitted by foreign bridge
const bridgeTokensSwappedEvents = tokensSwappedEvents.filter(e => e.address === COMMON_FOREIGN_BRIDGE_ADDRESS)
// Get transfer events for each previous erc20
const uniqueTokenAddressesSet = new Set(bridgeTokensSwappedEvents.map(e => e.returnValues.from))
// Exclude chai token from previous erc20
try {
logger.debug('calling foreignBridge.chaiToken() to remove it from half duplex tokens list')
const chaiToken = await foreignBridge.methods.chaiToken().call()
uniqueTokenAddressesSet.delete(chaiToken)
} catch (e) {
logger.debug('call to foreignBridge.chaiToken() failed')
}
// Exclude dai token from previous erc20
try {
logger.debug('calling foreignBridge.erc20token() to remove it from half duplex tokens list')
const daiToken = await foreignBridge.methods.erc20token().call()
uniqueTokenAddressesSet.delete(daiToken)
} catch (e) {
logger.debug('call to foreignBridge.erc20token() failed')
}
const uniqueTokenAddresses = [...uniqueTokenAddressesSet]
await Promise.all(
uniqueTokenAddresses.map(async tokenAddress => {
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
logger.debug('Half duplex token:', tokenAddress)
logger.debug("calling halfDuplexTokenContract.getPastEvents('Transfer')")
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
event: 'Transfer',
fromBlock: MONITOR_FOREIGN_START_BLOCK,
toBlock: foreignDelayedBlockNumber,
options: {
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
},
chain: 'foreign'
})).map(normalizeEvent)
// Remove events after the ES
logger.debug('filtering half duplex transfers happened before ES')
const validHalfDuplexTransfers = await filterTransferBeforeES(halfDuplexTransferEvents)
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
})
)
// filter transfer that is part of a token swap
directTransfers = transferEvents.filter(
e =>
bridgeTokensSwappedEvents.findIndex(
t => t.transactionHash === e.referenceTx && e.recipient === ZERO_ADDRESS
) === -1
)
}
// Get transfer events that didn't have a UserRequestForAffirmation event in the same transaction // Get transfer events that didn't have a UserRequestForAffirmation event in the same transaction
const directTransfers = transferEvents.filter( directTransfers = directTransfers.filter(
e => foreignToHomeRequests.findIndex(t => t.referenceTx === e.referenceTx) === -1 e => foreignToHomeRequests.findIndex(t => t.referenceTx === e.referenceTx) === -1
) )
@@ -197,8 +240,6 @@ async function main(mode) {
homeToForeignConfirmations, homeToForeignConfirmations,
foreignToHomeConfirmations, foreignToHomeConfirmations,
foreignToHomeRequests, foreignToHomeRequests,
informationRequests,
informationResponses,
isExternalErc20, isExternalErc20,
bridgeMode, bridgeMode,
homeBlockNumber, homeBlockNumber,

View File

@@ -8,8 +8,8 @@ function readFile(filePath, parseJson = true) {
const json = JSON.parse(content) const json = JSON.parse(content)
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
return Object.assign({}, json, { timeDiff }) return Object.assign({}, json, { timeDiff })
} catch (_) { } catch (e) {
console.error(`File ${filePath} does not exist`) console.error('readFlle', e)
return { return {
error: 'the bridge statistics are not available' error: 'the bridge statistics are not available'
} }

View File

@@ -28,21 +28,6 @@ function addExecutionStatus(processedList) {
} }
} }
function addRetrievalStatus(retrievedInfoList) {
const statuses = {}
retrievedInfoList.forEach(e => {
statuses[e.returnValues.messageId] = {
callStatus: e.returnValues.status,
callbackStatus: e.returnValues.callbackStatus
}
})
return deliveredMsg => {
deliveredMsg.callStatus = statuses[deliveredMsg.messageId].callStatus
deliveredMsg.callbackStatus = statuses[deliveredMsg.messageId].callbackStatus
return deliveredMsg
}
}
/** /**
* Normalizes the different event objects to facilitate data processing * Normalizes the different event objects to facilitate data processing
* @param {Object} event * @param {Object} event
@@ -104,7 +89,6 @@ module.exports = {
deliveredMsgNotProcessed, deliveredMsgNotProcessed,
processedMsgNotDelivered, processedMsgNotDelivered,
addExecutionStatus, addExecutionStatus,
addRetrievalStatus,
normalizeEventInformation, normalizeEventInformation,
eventWithoutReference, eventWithoutReference,
unclaimedHomeToForeignRequests, unclaimedHomeToForeignRequests,

View File

@@ -0,0 +1,27 @@
// https://etherscan.io/tx/0xd0c3c92c94e05bc71256055ce8c4c993e047f04e04f3283a04e4cb077b71f6c6
const blockNumberHalfDuplexDisabled = 9884448
/**
* Returns true if the event was before the bridge stopped supporting half duplex transfers.
*/
async function transferBeforeES(event) {
return event.blockNumber < blockNumberHalfDuplexDisabled
}
async function filterTransferBeforeES(array) {
const newArray = []
// Iterate events from newer to older
for (let i = array.length - 1; i >= 0; i--) {
const beforeES = await transferBeforeES(array[i])
if (beforeES) {
// add element to first position so the new array will have the same order
newArray.unshift(array[i])
}
}
return newArray
}
module.exports = {
filterTransferBeforeES,
blockNumberHalfDuplexDisabled
}

View File

@@ -1,5 +1,6 @@
require('dotenv').config() require('dotenv').config()
const Web3Utils = require('web3').utils const Web3Utils = require('web3').utils
const fetch = require('node-fetch')
const logger = require('./logger')('validators') const logger = require('./logger')('validators')
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromSupplier } = require('../commons') const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromSupplier } = require('../commons')
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3') const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')
@@ -80,7 +81,7 @@ async function main(bridgeMode) {
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) { if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
logger.debug('calling home getGasPrices') logger.debug('calling home getGasPrices')
homeGasPrice = homeGasPrice =
(await gasPriceFromSupplier(COMMON_HOME_GAS_PRICE_SUPPLIER_URL, homeGasPriceSupplierOpts)) || (await gasPriceFromSupplier(() => fetch(COMMON_HOME_GAS_PRICE_SUPPLIER_URL), homeGasPriceSupplierOpts)) ||
Web3Utils.toBN(COMMON_HOME_GAS_PRICE_FALLBACK) Web3Utils.toBN(COMMON_HOME_GAS_PRICE_FALLBACK)
homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei') homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT)) homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT))
@@ -92,9 +93,13 @@ async function main(bridgeMode) {
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) { if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
logger.debug('calling foreign getGasPrices') logger.debug('calling foreign getGasPrices')
const fetchFn =
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL === 'gas-price-oracle'
? null
: () => fetch(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL)
foreignGasPrice = foreignGasPrice =
(await gasPriceFromSupplier(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL, foreignGasPriceSupplierOpts)) || (await gasPriceFromSupplier(fetchFn, foreignGasPriceSupplierOpts)) ||
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK) Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK)
foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei') foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT)) foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT))

View File

@@ -5,7 +5,6 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "mocha", "start": "mocha",
"generate-amb-tx": "node ./scripts/generate-amb-tx.js",
"lint": "eslint . --ignore-path ../.eslintignore", "lint": "eslint . --ignore-path ../.eslintignore",
"amb": "mocha test/amb.js", "amb": "mocha test/amb.js",
"erc-to-native": "mocha test/ercToNative.js", "erc-to-native": "mocha test/ercToNative.js",

View File

@@ -10,7 +10,7 @@ case "$mode" in
;; ;;
esac esac
MODE="$mode" ../e2e-commons/up.sh deploy generate-amb-tx manual-amb-relay blocks oracle oracle-validator-2 oracle-validator-3 MODE="$mode" ../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3
docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start $script docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start $script
rc=$? rc=$?

View File

@@ -1,29 +0,0 @@
const Web3 = require('web3')
const { user, homeRPC, foreignRPC, amb2: amb } = require('../../e2e-commons/constants.json')
const { BOX_ABI } = require('../../commons')
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL))
homeWeb3.eth.accounts.wallet.add(user.privateKey)
foreignWeb3.eth.accounts.wallet.add(user.privateKey)
const opts = {
from: user.address,
gas: 400000,
gasPrice: '1'
}
const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox, opts)
const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox, opts)
async function main() {
const res1 = await homeBox.methods.setValueOnOtherNetwork(123, amb.home, amb.foreignBox).send()
const res2 = await foreignBox.methods.setValueOnOtherNetwork(456, amb.foreign, amb.homeBox).send()
const res3 = await foreignBox.methods.setValueOnOtherNetwork(789, amb.foreign, amb.homeBox).send()
console.log(res1.transactionHash)
console.log(res2.transactionHash)
console.log(res3.transactionHash)
}
main()

View File

@@ -1,9 +1,8 @@
const Web3 = require('web3') const Web3 = require('web3')
const assert = require('assert') const assert = require('assert')
const { ASYNC_CALL_ERRORS } = require('../../oracle/src/utils/constants')
const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json') const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
const { uniformRetry } = require('../../e2e-commons/utils') const { uniformRetry } = require('../../e2e-commons/utils')
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI, ambInformationSignatures } = require('../../commons') const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI } = require('../../commons')
const { delay, setRequiredSignatures } = require('./utils') const { delay, setRequiredSignatures } = require('./utils')
const { toBN } = Web3.utils const { toBN } = Web3.utils
@@ -27,49 +26,27 @@ const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox, opts)
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, amb.home, opts) const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, amb.home, opts)
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, amb.foreign, opts) const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, amb.foreign, opts)
function validateBlock(web3, serialized, block) {
assert.strictEqual(serialized.length, 2 + 64 * 12)
const values = web3.eth.abi.decodeParameter(
'(uint256,bytes32,address,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256)',
serialized
)
assert.strictEqual(values[0], block.number.toString(), 'wrong block number returned')
assert.strictEqual(values[1], block.hash, 'wrong block hash returned')
assert.strictEqual(values[2], block.miner, 'wrong block miner returned')
assert.strictEqual(values[3], block.gasUsed.toString(), 'wrong block gasUsed returned')
assert.strictEqual(values[4], block.gasLimit.toString(), 'wrong block gasLimit returned')
assert.strictEqual(values[5], block.parentHash, 'wrong block parentHash returned')
assert.strictEqual(values[6], block.receiptsRoot, 'wrong block receiptsRoot returned')
assert.strictEqual(values[7], block.stateRoot, 'wrong block stateRoot returned')
assert.strictEqual(values[8], block.transactionsRoot, 'wrong block transactionsRoot returned')
assert.strictEqual(values[9], block.timestamp.toString(), 'wrong block timestamp returned')
assert.strictEqual(values[10], block.difficulty, 'wrong block difficulty returned')
assert.strictEqual(values[11], block.totalDifficulty, 'wrong block totalDifficulty returned')
}
function validateTransaction(web3, serialized, tx) {
assert.strictEqual(serialized.length, 64 * 13 + tx.input.length + 56)
const values = web3.eth.abi.decodeParameter(
'(bytes32,uint256,bytes32,uint256,address,address,uint256,uint256,uint256,uint256,bytes)',
serialized
)
assert.strictEqual(values[0], tx.hash, 'wrong txHash returned')
assert.strictEqual(values[1], tx.blockNumber.toString(), 'wrong tx blockNumber returned')
assert.strictEqual(values[2], tx.blockHash.toString(), 'wrong tx blockHash returned')
assert.strictEqual(values[3], tx.transactionIndex.toString(), 'wrong tx transactionIndex returned')
assert.strictEqual(values[4], tx.from, 'wrong tx from returned')
assert.strictEqual(values[5], tx.to, 'wrong tx to returned')
assert.strictEqual(values[6], tx.value, 'wrong tx value returned')
assert.strictEqual(values[7], tx.nonce.toString(), 'wrong tx nonce returned')
assert.strictEqual(values[8], tx.gas.toString(), 'wrong tx gas returned')
assert.strictEqual(values[9], tx.gasPrice, 'wrong tx gasPrice returned')
assert.strictEqual(values[10], tx.input, 'wrong tx data returned')
}
describe('arbitrary message bridging', () => { describe('arbitrary message bridging', () => {
let requiredSignatures = 1 let requiredSignatures = 1
before(async () => { before(async () => {
for (const method of ambInformationSignatures) { const allowedMethods = [
'eth_call(address,bytes)',
'eth_call(address,bytes,uint256)',
'eth_call(address,address,uint256,bytes)',
'eth_blockNumber()',
'eth_getBlockByNumber()',
'eth_getBlockByNumber(uint256)',
'eth_getBlockByHash(bytes32)',
'eth_getBalance(address)',
'eth_getBalance(address,uint256)',
'eth_getTransactionCount(address)',
'eth_getTransactionCount(address,uint256)',
'eth_getTransactionByHash(bytes32)',
'eth_getTransactionReceipt(bytes32)',
'eth_getStorageAt(address,bytes32)',
'eth_getStorageAt(address,bytes32,uint256)'
]
for (const method of allowedMethods) {
const selector = homeWeb3.utils.soliditySha3(method) const selector = homeWeb3.utils.soliditySha3(method)
await homeBridge.methods.enableAsyncRequestSelector(selector, true).send({ from: validator.address }) await homeBridge.methods.enableAsyncRequestSelector(selector, true).send({ from: validator.address })
} }
@@ -103,19 +80,6 @@ describe('arbitrary message bridging', () => {
} }
}) })
}) })
if (process.env.ULTIMATE !== 'true') {
describe('Confirm Relay', () => {
it('should process lost affirmation-request via confirm relay', async () => {
const value = await homeBox.methods.value().call()
assert(value === '789', 'incorrect value')
})
it('should process lost signature-request & collected-signatures via confirm relay', async () => {
const value = await foreignBox.methods.value().call()
assert(value === '123', 'incorrect value')
})
})
}
describe('Home to Foreign', () => { describe('Home to Foreign', () => {
describe('Subsidized Mode', () => { describe('Subsidized Mode', () => {
it('should bridge message', async () => { it('should bridge message', async () => {
@@ -124,13 +88,12 @@ describe('arbitrary message bridging', () => {
const initialValue = await foreignBox.methods.value().call() const initialValue = await foreignBox.methods.value().call()
assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value') assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value')
const res = await homeBox.methods await homeBox.methods
.setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox) .setValueOnOtherNetwork(newValue, amb.home, amb.foreignBox)
.send() .send()
.catch(e => { .catch(e => {
console.error(e) console.error(e)
}) })
console.log(res.transactionHash)
// check that value changed and balance decreased // check that value changed and balance decreased
await uniformRetry(async retry => { await uniformRetry(async retry => {
@@ -177,6 +140,73 @@ describe('arbitrary message bridging', () => {
const value = await foreignBox.methods.value().call() const value = await foreignBox.methods.value().call()
assert(!toBN(value).eq(toBN(newValue)), 'Message should not be relayed by oracle automatically') assert(!toBN(value).eq(toBN(newValue)), 'Message should not be relayed by oracle automatically')
}) })
it('should reconfirm message in case of validators change', async () => {
const newValue = 15
const initialValue = await foreignBox.methods.value().call()
assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value')
const signatures = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
const { events } = await homeBox.methods
.setValueOnOtherNetworkUsingManualLane(newValue, amb.home, amb.foreignBox)
.send()
.catch(e => {
console.error(e)
})
const message = homeWeb3.eth.abi.decodeParameter('bytes', events['0'].raw.data)
await delay(10000)
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 3,
options: {
from: validator.address,
gas: '4000000'
}
})
const signatures2 = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
assert(
signatures2.length === signatures.length + requiredSignatures,
`Incorrect amount of signatures submitted, got ${signatures2.length}, expected ${signatures.length +
requiredSignatures}`
)
await homeBridge.methods.requestMessageReconfirm(message).send()
await delay(10000)
const signatures3 = await homeBridge.getPastEvents('SignedForUserRequest', {
fromBlock: 0,
toBlock: 'latest'
})
assert(
signatures3.length === signatures.length + 3,
`Incorrect amount of signatures submitted, got ${signatures3.length}, expected ${signatures.length + 3}`
)
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
})
} }
it('should confirm but not relay message from manual lane', async () => { it('should confirm but not relay message from manual lane', async () => {
@@ -223,13 +253,12 @@ describe('arbitrary message bridging', () => {
const initialValue = await homeBox.methods.value().call() const initialValue = await homeBox.methods.value().call()
assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value') assert(!toBN(initialValue).eq(toBN(newValue)), 'initial value should be different from new value')
const res = await foreignBox.methods await foreignBox.methods
.setValueOnOtherNetwork(newValue, amb.foreign, amb.homeBox) .setValueOnOtherNetwork(newValue, amb.foreign, amb.homeBox)
.send() .send()
.catch(e => { .catch(e => {
console.error(e) console.error(e)
}) })
console.log(res.transactionHash)
// check that value changed and balance decreased // check that value changed and balance decreased
await uniformRetry(async retry => { await uniformRetry(async retry => {
@@ -304,7 +333,7 @@ describe('arbitrary message bridging', () => {
await makeAsyncCall(selector, data2) await makeAsyncCall(selector, data2)
assert(!(await homeBox.methods.status().call()), 'status is true') assert(!(await homeBox.methods.status().call()), 'status is true')
assert.strictEqual(await homeBox.methods.data().call(), ASYNC_CALL_ERRORS.REVERT, 'returned data is incorrect') assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
const data3 = homeWeb3.eth.abi.encodeParameters( const data3 = homeWeb3.eth.abi.encodeParameters(
['address', 'address', 'uint256', 'bytes'], ['address', 'address', 'uint256', 'bytes'],
@@ -314,7 +343,7 @@ describe('arbitrary message bridging', () => {
await makeAsyncCall(selector, data3) await makeAsyncCall(selector, data3)
assert(!(await homeBox.methods.status().call()), 'status is true') assert(!(await homeBox.methods.status().call()), 'status is true')
assert.strictEqual(await homeBox.methods.data().call(), ASYNC_CALL_ERRORS.REVERT, 'returned data is incorrect') assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
}) })
it('should make async eth_call for specific block', async () => { it('should make async eth_call for specific block', async () => {
@@ -323,7 +352,7 @@ describe('arbitrary message bridging', () => {
const selector = homeWeb3.utils.soliditySha3('eth_call(address,bytes,uint256)') const selector = homeWeb3.utils.soliditySha3('eth_call(address,bytes,uint256)')
const data1 = homeWeb3.eth.abi.encodeParameters( const data1 = homeWeb3.eth.abi.encodeParameters(
['address', 'bytes', 'uint256'], ['address', 'bytes', 'uint256'],
[amb.foreignBox, foreignBox.methods.value().encodeABI(), 25] [amb.foreignBox, foreignBox.methods.value().encodeABI(), 60]
) )
const data2 = homeWeb3.eth.abi.encodeParameters( const data2 = homeWeb3.eth.abi.encodeParameters(
['address', 'bytes', 'uint256'], ['address', 'bytes', 'uint256'],
@@ -355,11 +384,6 @@ describe('arbitrary message bridging', () => {
await makeAsyncCall(selector, data3) await makeAsyncCall(selector, data3)
assert(!(await homeBox.methods.status().call()), 'status is true') assert(!(await homeBox.methods.status().call()), 'status is true')
assert.strictEqual(
await homeBox.methods.data().call(),
ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE,
'returned data is incorrect'
)
}) })
it('should make async eth_blockNumber', async () => { it('should make async eth_blockNumber', async () => {
@@ -379,8 +403,15 @@ describe('arbitrary message bridging', () => {
assert(await homeBox.methods.status().call(), 'status is false') assert(await homeBox.methods.status().call(), 'status is false')
const data = await homeBox.methods.data().call() const data = await homeBox.methods.data().call()
assert.strictEqual(data.length, 2 + 64 * 3)
const { 0: number, 1: hash, 2: miner } = homeWeb3.eth.abi.decodeParameters(
['uint256', 'bytes32', 'address'],
data
)
const block = await foreignWeb3.eth.getBlock(blockNumber) const block = await foreignWeb3.eth.getBlock(blockNumber)
validateBlock(homeWeb3, data, block) assert.strictEqual(number, blockNumber, 'wrong block number returned')
assert.strictEqual(hash, block.hash, 'wrong block hash returned')
assert.strictEqual(miner, block.miner, 'wrong block miner returned')
}) })
it('should make async eth_getBlockByNumber and return latest block', async () => { it('should make async eth_getBlockByNumber and return latest block', async () => {
@@ -390,7 +421,7 @@ describe('arbitrary message bridging', () => {
assert(await homeBox.methods.status().call(), 'status is false') assert(await homeBox.methods.status().call(), 'status is false')
const data = await homeBox.methods.data().call() const data = await homeBox.methods.data().call()
assert.strictEqual(data.length, 2 + 64 * 12) assert.strictEqual(data.length, 2 + 64 * 3)
}) })
it('should make async eth_getBlockByHash', async () => { it('should make async eth_getBlockByHash', async () => {
@@ -402,7 +433,16 @@ describe('arbitrary message bridging', () => {
assert(await homeBox.methods.status().call(), 'status is false') assert(await homeBox.methods.status().call(), 'status is false')
const data = await homeBox.methods.data().call() const data = await homeBox.methods.data().call()
validateBlock(homeWeb3, data, block) assert.strictEqual(data.length, 2 + 64 * 3)
const { 0: number, 1: hash, 2: miner } = homeWeb3.eth.abi.decodeParameters(
['uint256', 'bytes32', 'address'],
data
)
assert.strictEqual(number, blockNumber, 'wrong block number returned')
assert.strictEqual(hash, block.hash, 'wrong block hash returned')
assert.strictEqual(miner, block.miner, 'wrong block miner returned')
}) })
it('should make async eth_getBalance', async () => { it('should make async eth_getBalance', async () => {
@@ -490,7 +530,7 @@ describe('arbitrary message bridging', () => {
}) })
it('should make async eth_getTransactionByHash', async () => { it('should make async eth_getTransactionByHash', async () => {
const txHash = '0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd' const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
const tx = await foreignWeb3.eth.getTransaction(txHash) const tx = await foreignWeb3.eth.getTransaction(txHash)
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionByHash(bytes32)') const selector = homeWeb3.utils.soliditySha3('eth_getTransactionByHash(bytes32)')
@@ -498,11 +538,32 @@ describe('arbitrary message bridging', () => {
assert(await homeBox.methods.status().call(), 'status is false') assert(await homeBox.methods.status().call(), 'status is false')
const data = await homeBox.methods.data().call() const data = await homeBox.methods.data().call()
validateTransaction(homeWeb3, data, tx) const dataTypes = [
'bytes32',
'uint256',
'address',
'address',
'uint256',
'uint256',
'uint256',
'uint256',
'bytes'
]
const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
assert.strictEqual(values[1], tx.blockNumber.toString(), 'wrong tx blockNumber returned')
assert.strictEqual(values[2], tx.from, 'wrong tx from returned')
assert.strictEqual(values[3], tx.to, 'wrong tx to returned')
assert.strictEqual(values[4], tx.value, 'wrong tx value returned')
assert.strictEqual(values[5], tx.nonce.toString(), 'wrong tx nonce returned')
assert.strictEqual(values[6], tx.gas.toString(), 'wrong tx gas returned')
assert.strictEqual(values[7], tx.gasPrice, 'wrong tx gasPrice returned')
assert.strictEqual(values[8], tx.input, 'wrong tx data returned')
}) })
it('should make async eth_getTransactionReceipt', async () => { it('should make async eth_getTransactionReceipt', async () => {
const txHash = '0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd' const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
const receipt = await foreignWeb3.eth.getTransactionReceipt(txHash) const receipt = await foreignWeb3.eth.getTransactionReceipt(txHash)
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionReceipt(bytes32)') const selector = homeWeb3.utils.soliditySha3('eth_getTransactionReceipt(bytes32)')
@@ -510,25 +571,18 @@ describe('arbitrary message bridging', () => {
assert(await homeBox.methods.status().call(), 'status is false') assert(await homeBox.methods.status().call(), 'status is false')
const data = await homeBox.methods.data().call() const data = await homeBox.methods.data().call()
const values = homeWeb3.eth.abi.decodeParameter( const dataTypes = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
'(bytes32,uint256,bytes32,uint256,address,address,uint256,bool,(address,bytes32[],bytes)[])', const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
data
)
assert.strictEqual(values[0], txHash, 'wrong txHash returned') assert.strictEqual(values[0], txHash, 'wrong txHash returned')
assert.strictEqual(values[1], receipt.blockNumber.toString(), 'wrong tx blockNumber returned') assert.strictEqual(values[1], receipt.blockNumber.toString(), 'wrong tx blockNumber returned')
assert.strictEqual(values[2], receipt.blockHash, 'wrong tx blockHash returned') assert.strictEqual(values[2], receipt.status, 'wrong tx status returned')
assert.strictEqual(values[3], receipt.transactionIndex.toString(), 'wrong tx transactionIndex returned') assert.strictEqual(values[3].length, 1, 'wrong logs length returned')
assert.strictEqual(values[4].toLowerCase(), receipt.from, 'wrong tx from returned') assert.strictEqual(values[3][0][0], receipt.logs[0].address, 'wrong log address returned')
assert.strictEqual(values[5].toLowerCase(), receipt.to, 'wrong tx to returned') assert.strictEqual(values[3][0][1].length, 2, 'wrong log topics length returned')
assert.strictEqual(values[6], receipt.gasUsed.toString(), 'wrong gasUsed to returned') assert.strictEqual(values[3][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
assert.strictEqual(values[7], receipt.status, 'wrong tx status returned') assert.strictEqual(values[3][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
assert.strictEqual(values[8].length, 1, 'wrong logs length returned') assert.strictEqual(values[3][0][2], receipt.logs[0].data, 'wrong log data returned')
assert.strictEqual(values[8][0][0], receipt.logs[0].address, 'wrong log address returned')
assert.strictEqual(values[8][0][1].length, 2, 'wrong log topics length returned')
assert.strictEqual(values[8][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
assert.strictEqual(values[8][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
assert.strictEqual(values[8][0][2], receipt.logs[0].data, 'wrong log data returned')
}) })
it('should make async eth_getStorageAt', async () => { it('should make async eth_getStorageAt', async () => {

View File

@@ -33,10 +33,6 @@ const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME
describe('erc to native', () => { describe('erc to native', () => {
before(async () => { before(async () => {
console.log('Initializing interest')
await foreignBridge.methods
.initializeInterest(ercToNativeBridge.foreignToken, 1, 1, validator.address)
.send({ from: validator.address, gas: '4000000' })
if (process.env.ULTIMATE === 'true') { if (process.env.ULTIMATE === 'true') {
return return
} }
@@ -104,8 +100,6 @@ describe('erc to native', () => {
const transferValue = homeWeb3.utils.toWei('0.05') const transferValue = homeWeb3.utils.toWei('0.05')
// transfer that should not be processed by the filter
await erc20Token.methods.transfer(secondUser.address, transferValue).send({ from: user.address, gas: 100000 })
// send tokens to foreign bridge // send tokens to foreign bridge
await erc20Token.methods await erc20Token.methods
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue) .transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
@@ -116,7 +110,6 @@ describe('erc to native', () => {
.catch(e => { .catch(e => {
console.error(e) console.error(e)
}) })
await foreignBridge.methods.investDai().send({ from: validator.address, gas: '4000000' })
// check that balance increases // check that balance increases
await uniformRetry(async retry => { await uniformRetry(async retry => {

View File

@@ -6,7 +6,6 @@ module.exports = {
...baseConfig, ...baseConfig,
main: baseConfig.foreign, main: baseConfig.foreign,
event: 'UserRequestForAffirmation', event: 'UserRequestForAffirmation',
sender: 'home',
queue: 'home-prioritized', queue: 'home-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id

View File

@@ -8,15 +8,12 @@ const {
FOREIGN_AMB_ABI FOREIGN_AMB_ABI
} = require('../../commons') } = require('../../commons')
const { web3Home, web3Foreign } = require('../src/services/web3') const { web3Home, web3Foreign } = require('../src/services/web3')
const { add0xPrefix, privateKeyToAddress, loadKeystore } = require('../src/utils/utils') const { privateKeyToAddress } = require('../src/utils/utils')
const { EXIT_CODES } = require('../src/utils/constants')
const { const {
ORACLE_BRIDGE_MODE, ORACLE_BRIDGE_MODE,
ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS,
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
ORACLE_VALIDATOR_KEYSTORE_PATH,
ORACLE_VALIDATOR_KEYSTORE_PASSWORD,
ORACLE_MAX_PROCESSING_TIME, ORACLE_MAX_PROCESSING_TIME,
COMMON_HOME_BRIDGE_ADDRESS, COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS, COMMON_FOREIGN_BRIDGE_ADDRESS,
@@ -82,33 +79,9 @@ const foreignConfig = {
const maxProcessingTime = const maxProcessingTime =
parseInt(ORACLE_MAX_PROCESSING_TIME, 10) || 4 * Math.max(homeConfig.pollingInterval, foreignConfig.pollingInterval) parseInt(ORACLE_MAX_PROCESSING_TIME, 10) || 4 * Math.max(homeConfig.pollingInterval, foreignConfig.pollingInterval)
let validatorPrivateKey
let validatorAddress = ORACLE_VALIDATOR_ADDRESS
if (ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY) {
validatorPrivateKey = add0xPrefix(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY)
const derived = privateKeyToAddress(validatorPrivateKey)
if (ORACLE_VALIDATOR_ADDRESS && derived.toLowerCase() !== ORACLE_VALIDATOR_ADDRESS.toLowerCase()) {
console.error(
`Derived address from private key - ${derived} is different from ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}`
)
process.exit(EXIT_CODES.INCOMPATIBILITY)
}
validatorAddress = derived
} else if (ORACLE_VALIDATOR_KEYSTORE_PATH) {
try {
const keystore = loadKeystore(ORACLE_VALIDATOR_KEYSTORE_PATH, ORACLE_VALIDATOR_KEYSTORE_PASSWORD)
validatorPrivateKey = keystore.privateKey
validatorAddress = keystore.address
} catch (e) {
console.error(`Can't load keystore file: ${e.message}`)
process.exit(EXIT_CODES.INCOMPATIBILITY)
}
}
module.exports = { module.exports = {
eventFilter: {}, eventFilter: {},
validatorPrivateKey, validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
validatorAddress,
maxProcessingTime, maxProcessingTime,
shutdownKey: 'oracle-shutdown', shutdownKey: 'oracle-shutdown',
home: homeConfig, home: homeConfig,

View File

@@ -6,7 +6,6 @@ module.exports = {
...baseConfig, ...baseConfig,
main: baseConfig.home, main: baseConfig.home,
event: 'CollectedSignatures', event: 'CollectedSignatures',
sender: 'foreign',
queue: 'foreign-prioritized', queue: 'foreign-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id

View File

@@ -8,7 +8,6 @@ module.exports = {
web3ForeignArchive: web3ForeignArchive || baseConfig.foreign.web3, web3ForeignArchive: web3ForeignArchive || baseConfig.foreign.web3,
main: baseConfig.home, main: baseConfig.home,
event: 'UserRequestForInformation', event: 'UserRequestForInformation',
sender: 'home',
queue: 'home-prioritized', queue: 'home-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id

View File

@@ -6,7 +6,6 @@ module.exports = {
...baseConfig, ...baseConfig,
main: baseConfig.home, main: baseConfig.home,
event: 'UserRequestForSignature', event: 'UserRequestForSignature',
sender: 'home',
queue: 'home-prioritized', queue: 'home-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id

View File

@@ -1,7 +1,20 @@
const baseConfig = require('./base.config') const baseConfig = require('./base.config')
const { ERC20_ABI, ZERO_ADDRESS } = require('../../commons') const { ERC20_ABI } = require('../../commons')
const { EXIT_CODES } = require('../src/utils/constants') const { EXIT_CODES } = require('../src/utils/constants')
const initialChecksJson = process.argv[3]
if (!initialChecksJson) {
throw new Error('initial check parameter was not provided.')
}
let initialChecks
try {
initialChecks = JSON.parse(initialChecksJson)
} catch (e) {
throw new Error('Error on decoding values from initial checks.')
}
const id = `${baseConfig.id}-transfer` const id = `${baseConfig.id}-transfer`
if (baseConfig.id !== 'erc-native') { if (baseConfig.id !== 'erc-native') {
@@ -9,15 +22,14 @@ if (baseConfig.id !== 'erc-native') {
process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED) process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED)
} }
// exact address of the token contract is set in the watcher.js checkConditions() function
baseConfig.foreign.eventContract = new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, ZERO_ADDRESS)
module.exports = { module.exports = {
...baseConfig, ...baseConfig,
main: baseConfig.foreign, main: {
...baseConfig.foreign,
eventContract: new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, initialChecks.bridgeableTokenAddress)
},
event: 'Transfer', event: 'Transfer',
eventFilter: { to: baseConfig.foreign.bridgeAddress }, eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
sender: 'home',
queue: 'home-prioritized', queue: 'home-prioritized',
name: `watcher-${id}`, name: `watcher-${id}`,
id id

View File

@@ -1,15 +0,0 @@
---
version: '2.4'
services:
interestFetcher:
cpus: 0.1
mem_limit: 500m
image: poanetwork/tokenbridge-oracle:latest
env_file: ./.env
environment:
NODE_ENV: production
INTEREST_FETCHER_PRIVATE_KEY: ${INTEREST_FETCHER_PRIVATE_KEY}
INTEREST_FETCH_CONTRACT_ADDRESS: '0xCd152c7Bd4189Ddee97EaBb783FC5cD93CF2D230'
INTERVAL: 300000
restart: unless-stopped
entrypoint: yarn helper:interestFether

View File

@@ -13,12 +13,7 @@
"sender:home": "./scripts/start-worker.sh sender home-sender", "sender:home": "./scripts/start-worker.sh sender home-sender",
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender", "sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
"confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher", "confirm:transfer": "./scripts/start-worker.sh confirmRelay transfer-watcher",
"confirm:affirmation-request": "./scripts/start-worker.sh confirmRelay affirmation-request-watcher",
"confirm:signature-request": "./scripts/start-worker.sh confirmRelay signature-request-watcher",
"confirm:collected-signatures": "./scripts/start-worker.sh confirmRelay collected-signatures-watcher",
"confirm:information-request": "./scripts/start-worker.sh confirmRelay information-request-watcher",
"manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager", "manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager",
"helper:interestFether": "node ./scripts/interestFetcher.js",
"dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'", "dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'",
"test": "NODE_ENV=test mocha", "test": "NODE_ENV=test mocha",
"test:watch": "NODE_ENV=test mocha --watch --reporter=min", "test:watch": "NODE_ENV=test mocha --watch --reporter=min",

View File

@@ -0,0 +1,26 @@
require('../env')
const { getTokensState } = require('../src/utils/tokenState')
const { FOREIGN_ERC_TO_NATIVE_ABI } = require('../../commons')
const { web3Foreign } = require('../src/services/web3')
const emptyLogger = {
debug: () => {},
info: () => {}
}
async function initialChecks() {
const { ORACLE_BRIDGE_MODE, COMMON_FOREIGN_BRIDGE_ADDRESS } = process.env
let result = {}
if (ORACLE_BRIDGE_MODE === 'ERC_TO_NATIVE') {
const bridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
result = await getTokensState(bridge, emptyLogger)
}
console.log(JSON.stringify(result))
return result
}
const result = initialChecks()
module.exports = result

View File

@@ -1,65 +0,0 @@
require('../env')
const { isAddress } = require('web3').utils
const { privateKeyToAddress, setIntervalAndRun } = require('../src/utils/utils')
const { EXIT_CODES } = require('../src/utils/constants')
const { web3Home } = require('../src/services/web3')
const { sendTx } = require('../src/tx/sendTx')
const privateKey = process.env.INTEREST_FETCHER_PRIVATE_KEY
const interval = process.env.INTERVAL ? parseInt(process.env.INTERVAL, 10) : 3600 * 1000
const contractAddress = process.env.INTEREST_FETCH_CONTRACT_ADDRESS
if (!privateKey) {
console.error('Environment variable INTEREST_FETCHER_PRIVATE_KEY is not set')
process.exit(EXIT_CODES.GENERAL_ERROR)
}
if (interval < 300 * 1000) {
console.error('Interval is to small, should be at least 5 minutes')
process.exit(EXIT_CODES.GENERAL_ERROR)
}
if (!isAddress(contractAddress)) {
console.error('Invalid contract address provided', contractAddress)
process.exit(EXIT_CODES.GENERAL_ERROR)
}
const gasPrice = process.env.COMMON_HOME_GAS_PRICE_FALLBACK || '1000000000'
async function main() {
// assuming that we are using this contract - https://github.com/omni/interest-fetcher-contract
const contract = new web3Home.eth.Contract([{ name: 'fetchInterest', type: 'function', inputs: [] }], contractAddress)
const chainId = await web3Home.eth.getChainId()
const data = contract.methods.fetchInterest().encodeABI()
const senderAddress = privateKeyToAddress(privateKey)
console.log(
`Initialized, chainId=${chainId}, data=${data}, contract=${contractAddress}, interval=${interval / 1000}s`
)
await setIntervalAndRun(async () => {
let gasLimit
try {
gasLimit = await contract.methods.fetchInterest().estimateGas()
} catch (e) {
console.log('Gas limit estimation failed, will retry later', new Date())
return
}
const nonce = await web3Home.eth.getTransactionCount(senderAddress)
const txHash = await sendTx({
privateKey,
to: contractAddress,
data,
nonce,
gasPrice,
gasLimit: Math.round(gasLimit * 1.5),
amount: '0',
chainId,
web3: web3Home
})
console.log('Sent transaction with fetch interest', txHash, new Date())
}, interval)
}
main()

View File

@@ -8,10 +8,12 @@ LOGS_DIR="logs/"
WORKER="${WORKERS_DIR}${1}.js" WORKER="${WORKERS_DIR}${1}.js"
CONFIG="${2}.config.js" CONFIG="${2}.config.js"
LOG="${LOGS_DIR}${2}.txt" LOG="${LOGS_DIR}${2}.txt"
TX_HASH=${@:3} TX_HASH="${3}"
CHECKS=$(node scripts/initialChecks.js)
if [ "${NODE_ENV}" = "production" ]; then if [ "${NODE_ENV}" = "production" ]; then
exec node "${WORKER}" "${CONFIG}" $TX_HASH exec node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH"
else else
node "${WORKER}" "${CONFIG}" $TX_HASH | tee -a "${LOG}" | pino-pretty node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH" | tee -a "${LOG}" | pino-pretty
fi fi

View File

@@ -1,39 +1,22 @@
require('../env') require('../env')
const path = require('path') const path = require('path')
const fs = require('fs')
const { isAttached, connectWatcherToQueue, connection } = require('./services/amqpClient') const { isAttached, connectWatcherToQueue, connection } = require('./services/amqpClient')
const logger = require('./services/logger') const logger = require('./services/logger')
const GasPrice = require('./services/gasPrice') const GasPrice = require('./services/gasPrice')
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3') const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
const { sendTx } = require('./tx/sendTx') const { sendTx } = require('./tx/sendTx')
const { getTokensState } = require('./utils/tokenState')
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils') const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants') const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_ALLOW_HTTP_FOR_RPC } = process.env const { ORACLE_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
if (process.argv.length < 4) { if (process.argv.length < 5) {
logger.error('Please check the number of arguments, transaction hash is not present') logger.error('Please check the number of arguments, transaction hash is not present')
process.exit(EXIT_CODES.GENERAL_ERROR) process.exit(EXIT_CODES.GENERAL_ERROR)
} }
const config = require(path.join('../config/', process.argv[2])) const config = require(path.join('../config/', process.argv[2]))
const { web3, eventContract, chain, bridgeContract } = config.main const txHash = process.argv[4]
const isTxHash = txHash => txHash.length === 66 && web3.utils.isHexStrict(txHash)
function readTxHashes(filePath) {
return fs
.readFileSync(filePath)
.toString()
.split('\n')
.map(v => v.trim())
.filter(isTxHash)
}
const txHashesArgs = process.argv.slice(3)
const rawTxHashes = txHashesArgs.filter(isTxHash)
const txHashesFiles = txHashesArgs.filter(path => fs.existsSync(path)).flatMap(readTxHashes)
const txHashes = [...rawTxHashes, ...txHashesFiles]
const processSignatureRequests = require('./events/processSignatureRequests')(config) const processSignatureRequests = require('./events/processSignatureRequests')(config)
const processCollectedSignatures = require('./events/processCollectedSignatures')(config) const processCollectedSignatures = require('./events/processCollectedSignatures')(config)
@@ -44,13 +27,15 @@ const processAMBCollectedSignatures = require('./events/processAMBCollectedSigna
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config) const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config) const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
const { web3, eventContract } = config.main
let attached let attached
async function initialize() { async function initialize() {
try { try {
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger) const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
web3.currentProvider.urls.forEach(checkHttps(chain)) web3.currentProvider.urls.forEach(checkHttps(config.chain))
attached = await isAttached() attached = await isAttached()
if (attached) { if (attached) {
@@ -74,12 +59,12 @@ async function runMain({ sendToQueue }) {
const sendJob = attached ? sendToQueue : sendJobTx const sendJob = attached ? sendToQueue : sendJobTx
if (!attached || connection.isConnected()) { if (!attached || connection.isConnected()) {
if (config.maxProcessingTime) { if (config.maxProcessingTime) {
await watchdog(() => main({ sendJob, txHashes }), config.maxProcessingTime, () => { await watchdog(() => main({ sendJob, txHash }), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached') logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED) process.exit(EXIT_CODES.MAX_TIME_REACHED)
}) })
} else { } else {
await main({ sendJob, txHashes }) await main({ sendJob, txHash })
} }
} else { } else {
setTimeout(() => { setTimeout(() => {
@@ -114,37 +99,27 @@ function processEvents(events) {
} }
} }
async function main({ sendJob, txHashes }) { async function main({ sendJob, txHash }) {
if (config.id === 'erc-native-transfer') { try {
logger.debug('Getting token address to listen Transfer events') const events = await getEventsFromTx({
const state = await getTokensState(bridgeContract, logger) web3,
eventContract.options.address = state.bridgeableTokenAddress contract: eventContract,
} event: config.event,
txHash,
filter: config.eventFilter
})
logger.info(`Found ${events.length} ${config.event} events`)
logger.info(`Processing ${txHashes.length} input transactions`) if (events.length) {
for (const txHash of txHashes) { const job = await processEvents(events)
try { logger.info('Transactions to send:', job.length)
logger.info({ txHash }, `Processing transaction`)
const events = await getEventsFromTx({
web3,
contract: eventContract,
event: config.event,
txHash,
filter: config.eventFilter
})
logger.info({ txHash }, `Found ${events.length} ${config.event} events`)
if (events.length) { if (job.length) {
const job = await processEvents(events) await sendJob(job)
logger.info({ txHash }, 'Transactions to send:', job.length)
if (job.length) {
await sendJob(job)
}
} }
} catch (e) {
logger.error(e)
} }
} catch (e) {
logger.error(e)
} }
await connection.close() await connection.close()
@@ -153,13 +128,9 @@ async function main({ sendJob, txHashes }) {
} }
async function sendJobTx(jobs) { async function sendJobTx(jobs) {
await GasPrice.start(chain, true) const gasPrice = await GasPrice.start(config.chain, true)
const gasPrice = GasPrice.getPrice().toString(10)
const { web3 } = config.sender === 'foreign' ? config.foreign : config.home
const chainId = await getChainId(web3) const chainId = await getChainId(web3)
let nonce = await getNonce(web3, config.validatorAddress) let nonce = await getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
await syncForEach(jobs, async job => { await syncForEach(jobs, async job => {
let gasLimit let gasLimit
@@ -174,10 +145,10 @@ async function sendJobTx(jobs) {
const txHash = await sendTx({ const txHash = await sendTx({
data: job.data, data: job.data,
nonce, nonce,
gasPrice, gasPrice: gasPrice.toString(10),
amount: '0', amount: '0',
gasLimit, gasLimit,
privateKey: config.validatorPrivateKey, privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
to: job.to, to: job.to,
chainId, chainId,
web3 web3
@@ -196,7 +167,7 @@ async function sendJobTx(jobs) {
) )
if (e.message.toLowerCase().includes('insufficient funds')) { if (e.message.toLowerCase().includes('insufficient funds')) {
const currentBalance = await web3.eth.getBalance(config.validatorAddress) const currentBalance = await web3.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
const minimumBalance = gasLimit.multipliedBy(gasPrice) const minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error( logger.error(
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.` `Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`

View File

@@ -15,7 +15,7 @@ async function estimateGas({ web3, homeBridge, validatorContract, message, addre
const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({ const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({
from: address from: address
}) })
const msgGasLimit = Math.ceil((parseAMBHeader(message).gasLimit * 64) / 63) const msgGasLimit = parseAMBHeader(message).gasLimit
// message length in bytes // message length in bytes
const len = strip0x(message).length / 2 - MIN_AMB_HEADER_LENGTH const len = strip0x(message).length / 2 - MIN_AMB_HEADER_LENGTH

View File

@@ -1,7 +1,6 @@
const { toBN } = require('web3').utils const { toBN } = require('web3').utils
const { ASYNC_CALL_ERRORS, ASYNC_ETH_CALL_MAX_GAS_LIMIT } = require('../../../utils/constants') const { zipToObject } = require('../../../utils/utils')
const { zipToObject, isRevertError } = require('../../../utils/utils')
const argTypes = { const argTypes = {
to: 'address', to: 'address',
@@ -18,23 +17,14 @@ function makeCall(argNames) {
const { blockNumber, ...opts } = zipToObject(argNames, args) const { blockNumber, ...opts } = zipToObject(argNames, args)
if (blockNumber && toBN(blockNumber).gt(toBN(foreignBlock.number))) { if (blockNumber && toBN(blockNumber).gt(toBN(foreignBlock.number))) {
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] return [false, '0x']
} }
// different clients might use different default gas limits, so it makes sense to limit it by some large number const [status, result] = await web3.eth
if (!opts.gas || toBN(opts.gas).gt(toBN(ASYNC_ETH_CALL_MAX_GAS_LIMIT))) {
opts.gas = ASYNC_ETH_CALL_MAX_GAS_LIMIT
}
return web3.eth
.call(opts, blockNumber || foreignBlock.number) .call(opts, blockNumber || foreignBlock.number)
.then(result => [true, web3.eth.abi.encodeParameter('bytes', result)]) .then(result => [true, result], err => [false, err.data])
.catch(e => {
if (isRevertError(e)) { return [status, web3.eth.abi.encodeParameter('bytes', result)]
return [false, ASYNC_CALL_ERRORS.REVERT]
}
throw e
})
} }
} }

View File

@@ -1,7 +1,5 @@
const { toBN } = require('web3').utils const { toBN } = require('web3').utils
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
const address = web3.eth.abi.decodeParameter('address', data) const address = web3.eth.abi.decodeParameter('address', data)
@@ -14,7 +12,7 @@ async function callArchive(web3, data, foreignBlock) {
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data) const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] return [false, '0x']
} }
const balance = await web3.eth.getBalance(address, blockNumber) const balance = await web3.eth.getBalance(address, blockNumber)

View File

@@ -1,4 +1,3 @@
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
const { serializeBlock } = require('./serializers') const { serializeBlock } = require('./serializers')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
@@ -7,7 +6,7 @@ async function call(web3, data, foreignBlock) {
const block = await web3.eth.getBlock(blockHash) const block = await web3.eth.getBlock(blockHash)
if (block === null || block.number > foreignBlock.number) { if (block === null || block.number > foreignBlock.number) {
return [false, ASYNC_CALL_ERRORS.NOT_FOUND] return [false, '0x']
} }
return [true, serializeBlock(web3, block)] return [true, serializeBlock(web3, block)]

View File

@@ -1,13 +1,12 @@
const { toBN } = require('web3').utils const { toBN } = require('web3').utils
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
const { serializeBlock } = require('./serializers') const { serializeBlock } = require('./serializers')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
const blockNumber = web3.eth.abi.decodeParameter('uint256', data) const blockNumber = web3.eth.abi.decodeParameter('uint256', data)
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] return [false, '0x']
} }
const block = await web3.eth.getBlock(blockNumber) const block = await web3.eth.getBlock(blockNumber)

View File

@@ -1,7 +1,5 @@
const { toBN } = require('web3').utils const { toBN } = require('web3').utils
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data) const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data)
@@ -14,7 +12,7 @@ async function callArchive(web3, data, foreignBlock) {
const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data) const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data)
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] return [false, '0x']
} }
const value = await web3.eth.getStorageAt(address, slot, blockNumber) const value = await web3.eth.getStorageAt(address, slot, blockNumber)

View File

@@ -1,4 +1,3 @@
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
const { serializeTx } = require('./serializers') const { serializeTx } = require('./serializers')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
@@ -7,7 +6,7 @@ async function call(web3, data, foreignBlock) {
const tx = await web3.eth.getTransaction(hash) const tx = await web3.eth.getTransaction(hash)
if (tx === null || tx.blockNumber > foreignBlock.number) { if (tx === null || tx.blockNumber > foreignBlock.number) {
return [false, ASYNC_CALL_ERRORS.NOT_FOUND] return [false, '0x']
} }
return [true, serializeTx(web3, tx)] return [true, serializeTx(web3, tx)]

View File

@@ -1,7 +1,5 @@
const { toBN } = require('web3').utils const { toBN } = require('web3').utils
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
const address = web3.eth.abi.decodeParameter('address', data) const address = web3.eth.abi.decodeParameter('address', data)
@@ -14,7 +12,7 @@ async function callArchive(web3, data, foreignBlock) {
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data) const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] return [false, '0x']
} }
const nonce = await web3.eth.getTransactionCount(address, blockNumber) const nonce = await web3.eth.getTransactionCount(address, blockNumber)

View File

@@ -1,4 +1,3 @@
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
const { serializeReceipt } = require('./serializers') const { serializeReceipt } = require('./serializers')
async function call(web3, data, foreignBlock) { async function call(web3, data, foreignBlock) {
@@ -7,7 +6,7 @@ async function call(web3, data, foreignBlock) {
const receipt = await web3.eth.getTransactionReceipt(hash) const receipt = await web3.eth.getTransactionReceipt(hash)
if (receipt === null || receipt.blockNumber > foreignBlock.number) { if (receipt === null || receipt.blockNumber > foreignBlock.number) {
return [false, ASYNC_CALL_ERRORS.NOT_FOUND] return [false, '0x']
} }
return [true, serializeReceipt(web3, receipt)] return [true, serializeReceipt(web3, receipt)]

View File

@@ -1,30 +1,15 @@
const { ZERO_ADDRESS } = require('../../../../../commons') const { ZERO_ADDRESS } = require('../../../../../commons')
const serializeBlock = (web3, block) => { const serializeBlock = (web3, block) => {
const args = [ const args = [block.number, block.hash, block.miner]
block.number, const types = ['uint256', 'bytes32', 'address']
block.hash, return web3.eth.abi.encodeParameters(types, args)
block.miner,
block.gasUsed,
block.gasLimit,
block.parentHash,
block.receiptsRoot,
block.stateRoot,
block.transactionsRoot,
block.timestamp,
block.difficulty,
block.totalDifficulty
]
const type = '(uint256,bytes32,address,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256)'
return web3.eth.abi.encodeParameter(type, args)
} }
const serializeTx = (web3, tx) => { const serializeTx = (web3, tx) => {
const args = [ const args = [
tx.hash, tx.hash,
tx.blockNumber, tx.blockNumber,
tx.blockHash,
tx.transactionIndex,
tx.from, tx.from,
tx.to || ZERO_ADDRESS, tx.to || ZERO_ADDRESS,
tx.value, tx.value,
@@ -33,26 +18,16 @@ const serializeTx = (web3, tx) => {
tx.gasPrice, tx.gasPrice,
tx.input tx.input
] ]
const type = '(bytes32,uint256,bytes32,uint256,address,address,uint256,uint256,uint256,uint256,bytes)' const types = ['bytes32', 'uint256', 'address', 'address', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes']
return web3.eth.abi.encodeParameter(type, args) return web3.eth.abi.encodeParameters(types, args)
} }
const normalizeLog = log => [log.address, log.topics, log.data] const normalizeLog = log => [log.address, log.topics, log.data]
const serializeReceipt = (web3, receipt) => { const serializeReceipt = (web3, receipt) => {
const args = [ const args = [receipt.transactionHash, receipt.blockNumber, receipt.status, receipt.logs.map(normalizeLog)]
receipt.transactionHash, const types = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
receipt.blockNumber, return web3.eth.abi.encodeParameters(types, args)
receipt.blockHash,
receipt.transactionIndex,
receipt.from,
receipt.to || ZERO_ADDRESS,
receipt.gasUsed,
receipt.status,
receipt.logs.map(normalizeLog)
]
const type = '(bytes32,uint256,bytes32,uint256,address,address,uint256,bool,(address,bytes32[],bytes)[])'
return web3.eth.abi.encodeParameter(type, args)
} }
module.exports = { module.exports = {

View File

@@ -6,16 +6,7 @@ const logger = require('../../services/logger').child({
const { strip0x } = require('../../../../commons') const { strip0x } = require('../../../../commons')
const { AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas } = require('../../utils/constants') const { AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas } = require('../../utils/constants')
async function estimateGas({ async function estimateGas({ web3, homeBridge, validatorContract, messageId, status, result, address }) {
web3,
homeBridge,
validatorContract,
messageId,
status,
result,
address,
homeBlockNumber
}) {
try { try {
const gasEstimate = await homeBridge.methods.confirmInformation(messageId, status, result).estimateGas({ const gasEstimate = await homeBridge.methods.confirmInformation(messageId, status, result).estimateGas({
from: address from: address
@@ -24,8 +15,7 @@ async function estimateGas({
// message length in bytes // message length in bytes
const len = strip0x(result).length / 2 const len = strip0x(result).length / 2
let callbackGasLimit = parseInt(await homeBridge.methods.maxGasPerTx().call(), 10) const callbackGasLimit = parseInt(await homeBridge.methods.maxGasPerTx().call(), 10)
callbackGasLimit = Math.ceil((callbackGasLimit * 64) / 63)
return gasEstimate + callbackGasLimit + estimateExtraGas(len) return gasEstimate + callbackGasLimit + estimateExtraGas(len)
} catch (e) { } catch (e) {
@@ -61,20 +51,6 @@ async function estimateGas({
throw new InvalidValidatorError(`${address} is not a validator`) throw new InvalidValidatorError(`${address} is not a validator`)
} }
logger.debug('Check if InformationRetrieved event for this message already exists')
const logs = await homeBridge.getPastEvents('InformationRetrieved', {
fromBlock: homeBlockNumber,
toBlock: 'latest',
filter: { messageId }
})
if (logs.length > 0) {
logger.warn(
'This particular message was already signed and processed by other validators.' +
'However, evaluated async call result is different from the one recorded on-chain.'
)
throw new AlreadyProcessedError(e.message)
}
throw new Error('Unknown error while processing message') throw new Error('Unknown error while processing message')
} }
} }

View File

@@ -4,13 +4,7 @@ const { soliditySha3 } = require('web3').utils
const { HttpListProviderError } = require('../../services/HttpListProvider') const { HttpListProviderError } = require('../../services/HttpListProvider')
const rootLogger = require('../../services/logger') const rootLogger = require('../../services/logger')
const makeBlockFinder = require('../../services/blockFinder') const makeBlockFinder = require('../../services/blockFinder')
const { const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
EXIT_CODES,
MAX_CONCURRENT_EVENTS,
EXTRA_GAS_ABSOLUTE,
ASYNC_CALL_ERRORS,
MAX_ASYNC_CALL_RESULT_LENGTH
} = require('../../utils/constants')
const estimateGas = require('./estimateGas') const estimateGas = require('./estimateGas')
const { getValidatorContract, getBlock, getBlockNumber, getRequiredBlockConfirmations } = require('../../tx/web3') const { getValidatorContract, getBlock, getBlockNumber, getRequiredBlockConfirmations } = require('../../tx/web3')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
@@ -85,17 +79,12 @@ function processInformationRequestsBuilder(config) {
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request') logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
const call = asyncCalls[asyncCallMethod] const call = asyncCalls[asyncCallMethod]
let [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => { const [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
if (e instanceof HttpListProviderError) { if (e instanceof HttpListProviderError) {
throw e throw e
} }
logger.error({ error: e.message }, 'Unknown error during async call execution') return [false, '0x']
throw e
}) })
if (result.length > 2 + MAX_ASYNC_CALL_RESULT_LENGTH * 2) {
status = false
result = ASYNC_CALL_ERRORS.RESULT_IS_TOO_LONG
}
logger.info({ requestSelector, method: asyncCallMethod, status, result }, 'Request result obtained') logger.info({ requestSelector, method: asyncCallMethod, status, result }, 'Request result obtained')
let gasEstimate let gasEstimate
@@ -108,8 +97,7 @@ function processInformationRequestsBuilder(config) {
messageId, messageId,
status, status,
result, result,
address: config.validatorAddress, address: config.validatorAddress
homeBlockNumber: homeBlock.number
}) })
logger.debug({ gasEstimate }, 'Gas estimated') logger.debug({ gasEstimate }, 'Gas estimated')
} catch (e) { } catch (e) {

View File

@@ -8,6 +8,8 @@ const estimateGas = require('../processSignatureRequests/estimateGas')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
function processSignatureRequestsBuilder(config) { function processSignatureRequestsBuilder(config) {
@@ -35,7 +37,7 @@ function processSignatureRequestsBuilder(config) {
const { sender, executor } = parseAMBMessage(message) const { sender, executor } = parseAMBMessage(message)
logger.info({ sender, executor }, `Processing signatureRequest ${messageId}`) logger.info({ sender, executor }, `Processing signatureRequest ${messageId}`)
const signature = web3.eth.accounts.sign(message, config.validatorPrivateKey) const signature = web3.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
let gasEstimate let gasEstimate
try { try {

View File

@@ -8,6 +8,8 @@ const estimateGas = require('./estimateGas')
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors') const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants') const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
const limit = promiseLimit(MAX_CONCURRENT_EVENTS) const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
function processSignatureRequestsBuilder(config) { function processSignatureRequestsBuilder(config) {
@@ -46,7 +48,7 @@ function processSignatureRequestsBuilder(config) {
expectedMessageLength expectedMessageLength
}) })
const signature = web3.eth.accounts.sign(message, config.validatorPrivateKey) const signature = web3.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
let gasEstimate let gasEstimate
try { try {

View File

@@ -13,25 +13,12 @@ const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
function processTransfersBuilder(config) { function processTransfersBuilder(config) {
const { bridgeContract, web3 } = config.home const { bridgeContract, web3 } = config.home
const userRequestForAffirmationHash = web3.eth.abi.encodeEventSignature('UserRequestForAffirmation(address,uint256)') const userRequestForAffirmationAbi = config.foreign.bridgeABI.find(
const redeemHash = web3.eth.abi.encodeEventSignature('Redeem(address,uint256,uint256)') e => e.type === 'event' && e.name === 'UserRequestForAffirmation'
const transferHash = web3.eth.abi.encodeEventSignature('Transfer(address,address,uint256)') )
const tokensSwappedAbi = config.foreign.bridgeABI.find(e => e.type === 'event' && e.name === 'TokensSwapped')
const foreignBridgeAddress = config.foreign.bridgeAddress const userRequestForAffirmationHash = web3.eth.abi.encodeEventSignature(userRequestForAffirmationAbi)
const tokensSwappedHash = tokensSwappedAbi ? web3.eth.abi.encodeEventSignature(tokensSwappedAbi) : '0x'
const decodeAddress = data => web3.eth.abi.decodeParameter('address', data)
const isUserRequestForAffirmation = e =>
e.address.toLowerCase() === foreignBridgeAddress.toLowerCase() && e.topics[0] === userRequestForAffirmationHash
const isRedeem = cTokenAddress => e =>
e.address.toLowerCase() === cTokenAddress.toLowerCase() &&
e.topics[0] === redeemHash &&
decodeAddress(e.data.slice(0, 66)).toLowerCase() === foreignBridgeAddress.toLowerCase()
const isCTokenTransfer = cTokenAddress => e =>
e.address.toLowerCase() === cTokenAddress.toLowerCase() &&
e.topics[0] === transferHash &&
decodeAddress(e.topics[1]).toLowerCase() === foreignBridgeAddress.toLowerCase() &&
decodeAddress(e.topics[2]).toLowerCase() === cTokenAddress.toLowerCase()
let validatorContract = null let validatorContract = null
@@ -45,35 +32,37 @@ function processTransfersBuilder(config) {
rootLogger.debug(`Processing ${transfers.length} Transfer events`) rootLogger.debug(`Processing ${transfers.length} Transfer events`)
const callbacks = transfers const callbacks = transfers
.map(transfer => async () => { .map(transfer => async () => {
const { from, to, value } = transfer.returnValues const { from, value } = transfer.returnValues
const logger = rootLogger.child({ const logger = rootLogger.child({
eventTransactionHash: transfer.transactionHash, eventTransactionHash: transfer.transactionHash
from,
to,
value
}) })
logger.info('Processing transfer') logger.info({ from, value }, `Processing transfer ${transfer.transactionHash}`)
const receipt = await config.foreign.web3.eth.getTransactionReceipt(transfer.transactionHash) const receipt = await config.foreign.web3.eth.getTransactionReceipt(transfer.transactionHash)
if (receipt.logs.some(isUserRequestForAffirmation)) { const existsAffirmationEvent = receipt.logs.some(
logger.info('Transfer event discarded because affirmation is detected in the same transaction') e => e.address === config.foreign.bridgeAddress && e.topics[0] === userRequestForAffirmationHash
)
if (existsAffirmationEvent) {
logger.info(
`Transfer event discarded because a transaction with alternative receiver detected in transaction ${
transfer.transactionHash
}`
)
return return
} }
if (from === ZERO_ADDRESS) { const existsTokensSwappedEvent = tokensSwappedAbi
logger.info('Mint-like transfers from zero address are not processed') ? receipt.logs.some(e => e.address === config.foreign.bridgeAddress && e.topics[0] === tokensSwappedHash)
return : false
}
// when bridge performs a withdrawal from Compound, the following three events occur if (from === ZERO_ADDRESS && existsTokensSwappedEvent) {
// * token.Transfer(from=cToken, to=bridge, amount=X) logger.info(
// * cToken.Redeem(redeemer=bridge, amount=X, tokens=Y) `Transfer event discarded because token swap is detected in transaction ${transfer.transactionHash}`
// * cToken.Transfer(from=bridge, to=cToken, amount=Y) )
if (receipt.logs.some(isRedeem(from)) && receipt.logs.some(isCTokenTransfer(from))) {
logger.info('Transfer event discarded because cToken redeem is detected in the same transaction')
return return
} }

View File

@@ -10,18 +10,18 @@ const { getNonce, getChainId } = require('./tx/web3')
const { const {
addExtraGas, addExtraGas,
checkHTTPS, checkHTTPS,
privateKeyToAddress,
syncForEach, syncForEach,
waitForFunds, waitForFunds,
waitForUnsuspend, waitForUnsuspend,
watchdog, watchdog,
isGasPriceError, nonceError
isSameTransactionError,
isInsufficientBalanceError,
isNonceError
} = require('./utils/utils') } = require('./utils/utils')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants') const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants')
const { ORACLE_TX_REDUNDANCY } = process.env const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_TX_REDUNDANCY } = process.env
const ORACLE_VALIDATOR_ADDRESS = privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY)
if (process.argv.length < 3) { if (process.argv.length < 3) {
logger.error('Please check the number of arguments, config file was not provided') logger.error('Please check the number of arguments, config file was not provided')
@@ -40,7 +40,7 @@ async function initialize() {
try { try {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger) const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
web3.currentProvider.urls.forEach(checkHttps(config.id)) web3.currentProvider.urls.forEach(checkHttps(config.chain))
GasPrice.start(config.id) GasPrice.start(config.id)
@@ -84,7 +84,7 @@ async function readNonce(forceUpdate) {
logger.debug('Reading nonce') logger.debug('Reading nonce')
if (forceUpdate) { if (forceUpdate) {
logger.debug('Forcing update of nonce') logger.debug('Forcing update of nonce')
return getNonce(web3, config.validatorAddress) return getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
} }
const nonce = await redis.get(nonceKey) const nonce = await redis.get(nonceKey)
@@ -93,7 +93,7 @@ async function readNonce(forceUpdate) {
return Number(nonce) return Number(nonce)
} else { } else {
logger.warn("Nonce wasn't found in the DB") logger.warn("Nonce wasn't found in the DB")
return getNonce(web3, config.validatorAddress) return getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
} }
} }
@@ -168,7 +168,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
gasPrice, gasPrice,
amount: '0', amount: '0',
gasLimit, gasLimit,
privateKey: config.validatorPrivateKey, privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
to: job.to, to: job.to,
chainId, chainId,
web3: web3Redundant web3: web3Redundant
@@ -192,11 +192,12 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
e.message e.message
) )
if (isGasPriceError(e)) { const message = e.message.toLowerCase()
if (message.includes('replacement transaction underpriced')) {
logger.info('Replacement transaction underpriced, forcing gas price update') logger.info('Replacement transaction underpriced, forcing gas price update')
GasPrice.start(config.id) GasPrice.start(config.id)
failedTx.push(job) failedTx.push(job)
} else if (isResend || isSameTransactionError(e)) { } else if (isResend || message.includes('transaction with the same hash was already imported')) {
resendJobs.push(job) resendJobs.push(job)
} else { } else {
// if initial transaction sending has failed not due to the same hash error // if initial transaction sending has failed not due to the same hash error
@@ -205,14 +206,14 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
failedTx.push(job) failedTx.push(job)
} }
if (isInsufficientBalanceError(e)) { if (message.includes('insufficient funds')) {
insufficientFunds = true insufficientFunds = true
const currentBalance = await web3.eth.getBalance(config.validatorAddress) const currentBalance = await web3.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
minimumBalance = gasLimit.multipliedBy(gasPrice) minimumBalance = gasLimit.multipliedBy(gasPrice)
logger.error( logger.error(
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.` `Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
) )
} else if (isNonceError(e)) { } else if (nonceError(e)) {
nonce = await readNonce(true) nonce = await readNonce(true)
} }
} }
@@ -237,7 +238,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
if (insufficientFunds) { if (insufficientFunds) {
logger.warn('Insufficient funds. Stop sending transactions until the account has the minimum balance') logger.warn('Insufficient funds. Stop sending transactions until the account has the minimum balance')
channel.close() channel.close()
waitForFunds(web3, config.validatorAddress, minimumBalance, resume, logger) waitForFunds(web3, ORACLE_VALIDATOR_ADDRESS, minimumBalance, resume, logger)
} }
} catch (e) { } catch (e) {
logger.error(e) logger.error(e)

View File

@@ -2,14 +2,8 @@ const fetch = require('node-fetch')
const promiseRetry = require('promise-retry') const promiseRetry = require('promise-retry')
const { FALLBACK_RPC_URL_SWITCH_TIMEOUT } = require('../utils/constants') const { FALLBACK_RPC_URL_SWITCH_TIMEOUT } = require('../utils/constants')
const { onInjected } = require('./injectedLogger')
const { ORACLE_JSONRPC_ERROR_CODES } = process.env
// From EIP-1474 and Infura documentation // From EIP-1474 and Infura documentation
const JSONRPC_ERROR_CODES = ORACLE_JSONRPC_ERROR_CODES const JSONRPC_ERROR_CODES = [-32603, -32002, -32005]
? ORACLE_JSONRPC_ERROR_CODES.split(',').map(s => parseInt(s, 10))
: [-32603, -32002, -32005]
const defaultOptions = { const defaultOptions = {
name: 'main', name: 'main',
@@ -39,24 +33,14 @@ function HttpListProvider(urls, options = {}) {
this.options = { ...defaultOptions, ...options } this.options = { ...defaultOptions, ...options }
this.currentIndex = 0 this.currentIndex = 0
this.lastTimeUsedPrimary = 0 this.lastTimeUsedPrimary = 0
this.logger = {
onInjected(logger => { debug: () => {},
this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` }) info: () => {}
}) }
} }
HttpListProvider.prototype.switchToFallbackRPC = function() { HttpListProvider.prototype.setLogger = function(logger) {
if (this.urls.length < 2) { this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` })
return
}
const prevIndex = this.currentIndex
const newIndex = (prevIndex + 1) % this.urls.length
this.logger.info(
{ index: newIndex, oldURL: this.urls[prevIndex], newURL: this.urls[newIndex] },
'Switching to fallback JSON-RPC URL'
)
this.currentIndex = newIndex
} }
HttpListProvider.prototype.send = async function send(payload, callback) { HttpListProvider.prototype.send = async function send(payload, callback) {

View File

@@ -1,7 +1,6 @@
const promiseRetry = require('promise-retry') const promiseRetry = require('promise-retry')
const { promiseAny } = require('../utils/utils') const { promiseAny } = require('../utils/utils')
const { defaultOptions, HttpListProviderError, send } = require('./HttpListProvider') const { defaultOptions, HttpListProviderError, send } = require('./HttpListProvider')
const { onInjected } = require('./injectedLogger')
function RedundantHttpListProvider(urls, options = {}) { function RedundantHttpListProvider(urls, options = {}) {
if (!(this instanceof RedundantHttpListProvider)) { if (!(this instanceof RedundantHttpListProvider)) {
@@ -14,9 +13,10 @@ function RedundantHttpListProvider(urls, options = {}) {
this.urls = urls this.urls = urls
this.options = { ...defaultOptions, ...options } this.options = { ...defaultOptions, ...options }
onInjected(logger => { }
this.logger = logger.child({ module: `RedundantHttpListProvider:${this.options.name}` })
}) RedundantHttpListProvider.prototype.setLogger = function(logger) {
this.logger = logger.child({ module: `RedundantHttpListProvider:${this.options.name}` })
} }
RedundantHttpListProvider.prototype.send = async function send(payload, callback) { RedundantHttpListProvider.prototype.send = async function send(payload, callback) {

View File

@@ -1,37 +0,0 @@
const { hexToNumber, isHexStrict } = require('web3').utils
function SafeEthLogsProvider(provider) {
const oldSend = provider.send.bind(provider)
const newSend = function(payload, callback) {
if (payload.method === 'eth_getLogs' && isHexStrict(payload.params[0].toBlock)) {
this.logger.debug('Modifying eth_getLogs request to include batch eth_blockNumber request')
const newPayload = [payload, { jsonrpc: '2.0', id: payload.id + 1, method: 'eth_blockNumber', params: [] }]
oldSend(newPayload, (err, res) => {
if (err) {
callback(err, null)
} else {
const rawLogs = res.find(({ id }) => id === payload.id)
const rawBlockNumber = res.find(({ id }) => id === payload.id + 1)
const blockNumber = hexToNumber(rawBlockNumber.result)
const toBlock = hexToNumber(payload.params[0].toBlock)
if (blockNumber < toBlock) {
this.logger.warn({ toBlock, blockNumber }, 'Returned block number is less than the specified toBlock')
callback(new Error('block number too low'), null)
} else {
callback(null, rawLogs)
}
}
})
} else {
oldSend(payload, callback)
}
}
provider.send = newSend.bind(provider)
return provider
}
module.exports = {
SafeEthLogsProvider
}

Some files were not shown because too many files have changed in this diff Show More