Merge the develop branch to the master branch, preparation to v3.0.0-rc1
This merge contains the following set of changes: * [Monitor, Improvement] Add statistics about used AMB information requests (#577) * [ALM, Improvement] Add safe-execute button in ALM (#580), closes #573, closes #551 * [Oracle, Improvement] Improve confirm-relay feature (#582), closes #569 * [Monitor, Fix] Prune print of long error messages about missing file (#579), closes #578 * [Oracle, Fix] Use safe approach for eth_getLogs requests (#581) * [Oracle, Fix] Fix logging in gas price service (#583), closes #552 * [Oracle, Fix] Fix oracle error patterns and oracle e2e tests (#585) * [Common, Other] Fix dependencies versions and update example.yml (#566) * [Common, Other] Upload services logs in e2e and ultimate tests (#568) * [Oracle, Other] added example of emergency shutdown controller contract (#572) * [Oracle, Other] Refactor oracle configuration (#584)
This commit is contained in:
commit
c92f80c484
17
.github/workflows/main.yml
vendored
17
.github/workflows/main.yml
vendored
@ -149,6 +149,12 @@ jobs:
|
|||||||
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
|
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
|
||||||
- name: yarn run ${{ matrix.task }}
|
- name: yarn run ${{ matrix.task }}
|
||||||
run: ${{ !matrix.use-cache || steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
|
run: ${{ !matrix.use-cache || steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
|
||||||
|
- name: Upload logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: logs-${{ matrix.task }}
|
||||||
|
path: e2e-commons/logs
|
||||||
deployment:
|
deployment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
@ -199,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 blocks
|
run: ${{ steps.cache-repo.outputs.cache-hit }} && e2e-commons/up.sh deploy generate-amb-tx 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
|
||||||
@ -210,3 +216,12 @@ jobs:
|
|||||||
run: sudo chown -R $USER:docker /var/run/docker.sock
|
run: sudo chown -R $USER:docker /var/run/docker.sock
|
||||||
- name: Run oracle e2e tests
|
- name: Run oracle e2e tests
|
||||||
run: docker-compose -f ./e2e-commons/docker-compose.yml run -e ULTIMATE=true e2e yarn workspace oracle-e2e run ${{ matrix.task }}
|
run: docker-compose -f ./e2e-commons/docker-compose.yml run -e ULTIMATE=true e2e yarn workspace oracle-e2e run ${{ matrix.task }}
|
||||||
|
- name: Save logs
|
||||||
|
if: always()
|
||||||
|
run: e2e-commons/down.sh
|
||||||
|
- name: Upload logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: logs-ultimate-${{ matrix.task }}
|
||||||
|
path: e2e-commons/logs
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
cd $(dirname $0)
|
cd $(dirname $0)
|
||||||
|
|
||||||
../e2e-commons/up.sh deploy blocks alm alm-e2e
|
../e2e-commons/up.sh deploy generate-amb-tx 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
|
||||||
|
@ -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/0xbc83d43bdc675a615a2b820e43e52d25857aa5fdd77acf2dd92cd247af2c693c'
|
const homeToForeignTxURL = 'http://localhost:3004/77/0x295efbe6ae98937ef35d939376c9bd752b4dc6f6899a9d5ddd6a57cea3d76c89'
|
||||||
const foreignToHomeTxURL = 'http://localhost:3004/42/0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
const foreignToHomeTxURL = 'http://localhost:3004/42/0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd'
|
||||||
|
|
||||||
describe('ALM', () => {
|
describe('ALM', () => {
|
||||||
let browser
|
let browser
|
||||||
|
@ -123,6 +123,24 @@ 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: [
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React, { useEffect, useState } 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,6 +9,7 @@ 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;
|
||||||
@ -33,6 +34,8 @@ 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 ||
|
||||||
@ -48,6 +51,22 @@ 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:
|
||||||
@ -105,6 +124,7 @@ 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[]}
|
||||||
|
@ -15,16 +15,19 @@ 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 StyledButton = styled.button`
|
const ActionButton = 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[]
|
||||||
@ -32,6 +35,7 @@ interface ManualExecutionButtonParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ManualExecutionButton = ({
|
export const ManualExecutionButton = ({
|
||||||
|
safeExecutionAvailable,
|
||||||
messageData,
|
messageData,
|
||||||
setExecutionData,
|
setExecutionData,
|
||||||
signatureCollected,
|
signatureCollected,
|
||||||
@ -40,6 +44,7 @@ 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)
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
@ -72,7 +77,11 @@ 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 data = bridge.methods.executeSignatures(messageData, signatures).encodeABI()
|
const executeMethod =
|
||||||
|
safeExecutionAvailable && !allowFailures
|
||||||
|
? bridge.methods.safeExecuteSignaturesWithAutoGasLimit
|
||||||
|
: bridge.methods.executeSignatures
|
||||||
|
const data = executeMethod(messageData, signatures).encodeABI()
|
||||||
setManualExecution(false)
|
setManualExecution(false)
|
||||||
|
|
||||||
library.eth
|
library.eth
|
||||||
@ -132,15 +141,35 @@ 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 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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
const { soliditySha3 } = require('web3-utils')
|
||||||
|
|
||||||
function strip0x(input) {
|
function strip0x(input) {
|
||||||
return input.replace(/^0x/, '')
|
return input.replace(/^0x/, '')
|
||||||
}
|
}
|
||||||
@ -39,8 +41,35 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
},
|
},
|
||||||
"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",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
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')
|
||||||
|
|
||||||
@ -178,17 +179,16 @@ const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
|
|||||||
return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei'))
|
return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchFn has to be supplied (instead of just url to oracle),
|
const gasPriceFromSupplier = async (url, options = {}) => {
|
||||||
// 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 (fetchFn) {
|
if (url === 'gas-price-oracle') {
|
||||||
const response = await fetchFn()
|
json = await gasPriceOracle.fetchGasPricesOffChain()
|
||||||
|
} else if (url) {
|
||||||
|
const response = await fetch(url, { timeout: 2000 })
|
||||||
json = await response.json()
|
json = await response.json()
|
||||||
} else {
|
} else {
|
||||||
json = await gasPriceOracle.fetchGasPricesOffChain()
|
return null
|
||||||
}
|
}
|
||||||
const oracleGasPrice = json[options.speedType]
|
const oracleGasPrice = json[options.speedType]
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ provisioner:
|
|||||||
inventory:
|
inventory:
|
||||||
host_vars:
|
host_vars:
|
||||||
multiple-host:
|
multiple-host:
|
||||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "6c48435bd464a53ed66ed62127c4dba8af75cf1a99a8ebe2680599948fbfbc6d"
|
||||||
MONITOR_PORT: 3003
|
MONITOR_PORT: 3003
|
||||||
syslog_server_port: "udp://127.0.0.1:514"
|
syslog_server_port: "udp://127.0.0.1:514"
|
||||||
verifier:
|
verifier:
|
||||||
|
@ -33,7 +33,7 @@ provisioner:
|
|||||||
inventory:
|
inventory:
|
||||||
host_vars:
|
host_vars:
|
||||||
oracle-host:
|
oracle-host:
|
||||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "6c48435bd464a53ed66ed62127c4dba8af75cf1a99a8ebe2680599948fbfbc6d"
|
||||||
syslog_server_port: "udp://127.0.0.1:514"
|
syslog_server_port: "udp://127.0.0.1:514"
|
||||||
verifier:
|
verifier:
|
||||||
name: testinfra
|
name: testinfra
|
||||||
|
@ -5,13 +5,13 @@ ORACLE_LOG_LEVEL: debug
|
|||||||
|
|
||||||
## Home contract
|
## Home contract
|
||||||
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
|
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
|
||||||
COMMON_HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976"
|
COMMON_HOME_BRIDGE_ADDRESS: "0x59ba90A588ce732AB33FD32Aab1b58c21400A0f6"
|
||||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||||
|
|
||||||
## Foreign contract
|
## Foreign contract
|
||||||
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
|
COMMON_FOREIGN_RPC_URL: "https://kovan.infura.io/v3/5d7bd94c50ed43fab1cb8e74f58678b0"
|
||||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981"
|
COMMON_FOREIGN_BRIDGE_ADDRESS: "0xdA4a49a00F4fF4A5988b9AceE95f99e3b2c208b6"
|
||||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
|
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000
|
||||||
|
|
||||||
## Home Gasprice
|
## Home Gasprice
|
||||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/"
|
||||||
@ -31,8 +31,8 @@ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
|||||||
MONITOR_BRIDGE_NAME: "bridge"
|
MONITOR_BRIDGE_NAME: "bridge"
|
||||||
MONITOR_PORT: 3003
|
MONITOR_PORT: 3003
|
||||||
MONITOR_CACHE_EVENTS: "false"
|
MONITOR_CACHE_EVENTS: "false"
|
||||||
MONITOR_HOME_START_BLOCK: 0
|
MONITOR_HOME_START_BLOCK: 20821049
|
||||||
MONITOR_FOREIGN_START_BLOCK: 0
|
MONITOR_FOREIGN_START_BLOCK: 24773297
|
||||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||||
|
@ -27,17 +27,6 @@
|
|||||||
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"
|
||||||
|
@ -6,14 +6,12 @@ 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
|
||||||
ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=
|
||||||
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=https://gasprice.poa.network/
|
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=
|
||||||
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
|
||||||
|
@ -6,8 +6,6 @@ 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
|
||||||
|
@ -49,6 +49,12 @@
|
|||||||
"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"
|
||||||
|
@ -1,7 +1,33 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
cd $(dirname $0)
|
cd $(dirname $0)
|
||||||
|
|
||||||
if [ $CI ]; then exit $rc; fi
|
if [ $CI ]; then
|
||||||
|
rm -rf logs || true
|
||||||
|
|
||||||
|
mkdir ./logs
|
||||||
|
|
||||||
|
for project in "" validator{1,2,3}; do
|
||||||
|
for container in $(docker-compose -p "$project" ps | tail -n +3 | awk '{print $1}') ; do
|
||||||
|
if [[ -z "$project" ]]; then
|
||||||
|
path="./logs/$container.log"
|
||||||
|
else
|
||||||
|
mkdir -p "./logs/$project"
|
||||||
|
path="./logs/$project/$container.log"
|
||||||
|
fi
|
||||||
|
docker logs "$container" > "$path" 2>&1
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
touch ../oracle/.env
|
||||||
|
for file in ../oracle/docker-compose-{amb,transfer}.yml; do
|
||||||
|
for container in $(docker-compose -f "$file" ps | tail -n +3 | awk '{print $1}') ; do
|
||||||
|
mkdir -p "./logs/oracle"
|
||||||
|
docker logs "$container" > "./logs/oracle/$container.log" 2>&1
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $rc;
|
||||||
|
fi
|
||||||
|
|
||||||
ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash
|
ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash
|
||||||
docker-compose down
|
docker-compose down
|
||||||
|
@ -35,3 +35,9 @@ 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
|
||||||
|
@ -15,42 +15,46 @@ 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://$4 -e ORACLE_REDIS_URL=redis://$3"
|
db_env="-e ORACLE_QUEUE_URL=amqp://$3 -e ORACLE_REDIS_URL=redis://$2"
|
||||||
|
|
||||||
docker-compose $1 run -d --name $3 redis
|
docker-compose $1 run -d --name $2 redis
|
||||||
docker-compose $1 run -d --name $4 rabbit
|
docker-compose $1 run -d --name $3 rabbit
|
||||||
|
|
||||||
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
|
if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then
|
||||||
docker-compose $1 run $2 $db_env -d oracle-erc20-native yarn watcher:signature-request
|
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:collected-signatures
|
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:affirmation-request
|
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:transfer
|
docker-compose $1 run $oracleAddr $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 $2 $db_env -d oracle-amb yarn watcher:signature-request
|
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:collected-signatures
|
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:affirmation-request
|
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:information-request
|
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:information-request
|
||||||
fi
|
fi
|
||||||
|
|
||||||
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:home
|
||||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn sender:foreign
|
docker-compose $1 run $oraclePK $db_env -d oracle-amb yarn sender:foreign
|
||||||
docker-compose $1 run $2 $db_env -d oracle-amb yarn manager:shutdown
|
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn manager:shutdown
|
||||||
}
|
}
|
||||||
|
|
||||||
while [ "$1" != "" ]; do
|
while [ "$1" != "" ]; do
|
||||||
if [ "$1" == "oracle" ]; then
|
if [ "$1" == "oracle" ]; then
|
||||||
startValidator "-p validator1" "" redis rabbit
|
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||||
|
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
|
||||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4"
|
||||||
startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
|
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||||
|
startValidator "-p validator2" redis2 rabbit2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "oracle-validator-3" ]; then
|
if [ "$1" == "oracle-validator-3" ]; then
|
||||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d"
|
||||||
startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
|
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||||
|
startValidator "-p validator3" redis3 rabbit3
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "alm" ]; then
|
if [ "$1" == "alm" ]; then
|
||||||
@ -84,13 +88,38 @@ while [ "$1" != "" ]; do
|
|||||||
if [ "$1" == "alm-e2e" ]; then
|
if [ "$1" == "alm-e2e" ]; then
|
||||||
MODE=amb
|
MODE=amb
|
||||||
|
|
||||||
startValidator "-p validator1" "" redis rabbit
|
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||||
|
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||||
|
startValidator "-p validator1" redis rabbit
|
||||||
|
|
||||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4"
|
||||||
startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
|
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||||
|
startValidator "-p validator2" redis2 rabbit2
|
||||||
|
|
||||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d"
|
||||||
startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
|
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||||
|
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
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "mocha --timeout 120000",
|
"start": "mocha --timeout 120000 --exit",
|
||||||
"lint": "eslint . --ignore-path ../.eslintignore"
|
"lint": "eslint . --ignore-path ../.eslintignore"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
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 } = require('./utils/web3Cache')
|
const { getHomeTxSender, getForeignTxSender, isHomeContract, isForeignContract } = require('./utils/web3Cache')
|
||||||
const { addExecutionStatus } = require('./utils/message')
|
const { addExecutionStatus, addRetrievalStatus } = require('./utils/message')
|
||||||
const { normalizeAMBMessageEvent } = require('../commons')
|
const { normalizeAMBMessageEvent, normalizeAMBInfoRequest } = require('../commons')
|
||||||
|
|
||||||
function countInteractions(requests) {
|
function countInteractions(requests) {
|
||||||
const stats = {}
|
const stats = {}
|
||||||
@ -30,6 +29,41 @@ 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,
|
||||||
@ -88,10 +122,13 @@ 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.
|
||||||
@ -146,6 +183,7 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,9 @@ 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) {
|
||||||
@ -34,7 +36,9 @@ 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,
|
||||||
|
@ -129,6 +129,27 @@ 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, {
|
||||||
@ -225,6 +246,8 @@ async function main(mode) {
|
|||||||
homeToForeignConfirmations,
|
homeToForeignConfirmations,
|
||||||
foreignToHomeConfirmations,
|
foreignToHomeConfirmations,
|
||||||
foreignToHomeRequests,
|
foreignToHomeRequests,
|
||||||
|
informationRequests,
|
||||||
|
informationResponses,
|
||||||
isExternalErc20,
|
isExternalErc20,
|
||||||
bridgeMode,
|
bridgeMode,
|
||||||
homeBlockNumber,
|
homeBlockNumber,
|
||||||
|
@ -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 (e) {
|
} catch (_) {
|
||||||
console.error('readFlle', e)
|
console.error(`File ${filePath} does not exist`)
|
||||||
return {
|
return {
|
||||||
error: 'the bridge statistics are not available'
|
error: 'the bridge statistics are not available'
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,21 @@ 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
|
||||||
@ -89,6 +104,7 @@ module.exports = {
|
|||||||
deliveredMsgNotProcessed,
|
deliveredMsgNotProcessed,
|
||||||
processedMsgNotDelivered,
|
processedMsgNotDelivered,
|
||||||
addExecutionStatus,
|
addExecutionStatus,
|
||||||
|
addRetrievalStatus,
|
||||||
normalizeEventInformation,
|
normalizeEventInformation,
|
||||||
eventWithoutReference,
|
eventWithoutReference,
|
||||||
unclaimedHomeToForeignRequests,
|
unclaimedHomeToForeignRequests,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
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')
|
||||||
@ -81,7 +80,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(() => fetch(COMMON_HOME_GAS_PRICE_SUPPLIER_URL), homeGasPriceSupplierOpts)) ||
|
(await gasPriceFromSupplier(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))
|
||||||
@ -93,13 +92,9 @@ 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(fetchFn, foreignGasPriceSupplierOpts)) ||
|
(await gasPriceFromSupplier(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL, 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))
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "mocha --exit",
|
"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",
|
||||||
|
@ -10,7 +10,7 @@ case "$mode" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
MODE="$mode" ../e2e-commons/up.sh deploy blocks oracle oracle-validator-2 oracle-validator-3
|
MODE="$mode" ../e2e-commons/up.sh deploy generate-amb-tx manual-amb-relay 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=$?
|
||||||
|
29
oracle-e2e/scripts/generate-amb-tx.js
Normal file
29
oracle-e2e/scripts/generate-amb-tx.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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()
|
@ -2,7 +2,7 @@ const Web3 = require('web3')
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
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 } = require('../../commons')
|
const { BOX_ABI, HOME_AMB_ABI, FOREIGN_AMB_ABI, ambInformationSignatures } = require('../../commons')
|
||||||
const { delay, setRequiredSignatures } = require('./utils')
|
const { delay, setRequiredSignatures } = require('./utils')
|
||||||
|
|
||||||
const { toBN } = Web3.utils
|
const { toBN } = Web3.utils
|
||||||
@ -29,24 +29,7 @@ const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, amb.foreign,
|
|||||||
describe('arbitrary message bridging', () => {
|
describe('arbitrary message bridging', () => {
|
||||||
let requiredSignatures = 1
|
let requiredSignatures = 1
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const allowedMethods = [
|
for (const method of 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)'
|
|
||||||
]
|
|
||||||
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 })
|
||||||
}
|
}
|
||||||
@ -80,6 +63,19 @@ 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 () => {
|
||||||
@ -88,12 +84,13 @@ 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')
|
||||||
|
|
||||||
await homeBox.methods
|
const res = 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 => {
|
||||||
@ -186,12 +183,13 @@ 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')
|
||||||
|
|
||||||
await foreignBox.methods
|
const res = 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 => {
|
||||||
@ -285,7 +283,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(), 60]
|
[amb.foreignBox, foreignBox.methods.value().encodeABI(), 25]
|
||||||
)
|
)
|
||||||
const data2 = homeWeb3.eth.abi.encodeParameters(
|
const data2 = homeWeb3.eth.abi.encodeParameters(
|
||||||
['address', 'bytes', 'uint256'],
|
['address', 'bytes', 'uint256'],
|
||||||
@ -463,7 +461,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should make async eth_getTransactionByHash', async () => {
|
it('should make async eth_getTransactionByHash', async () => {
|
||||||
const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
const txHash = '0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd'
|
||||||
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)')
|
||||||
|
|
||||||
@ -496,7 +494,7 @@ describe('arbitrary message bridging', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should make async eth_getTransactionReceipt', async () => {
|
it('should make async eth_getTransactionReceipt', async () => {
|
||||||
const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
const txHash = '0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd'
|
||||||
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)')
|
||||||
|
|
||||||
|
@ -100,6 +100,8 @@ 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)
|
||||||
|
@ -1 +1 @@
|
|||||||
--timeout 120000
|
--timeout 120000 --exit
|
||||||
|
@ -6,6 +6,7 @@ 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
|
||||||
|
@ -8,7 +8,8 @@ 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 { privateKeyToAddress } = require('../src/utils/utils')
|
const { add0xPrefix, privateKeyToAddress } = require('../src/utils/utils')
|
||||||
|
const { EXIT_CODES } = require('../src/utils/constants')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ORACLE_BRIDGE_MODE,
|
ORACLE_BRIDGE_MODE,
|
||||||
@ -79,9 +80,22 @@ 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
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
eventFilter: {},
|
eventFilter: {},
|
||||||
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
|
validatorPrivateKey,
|
||||||
|
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(validatorPrivateKey),
|
||||||
maxProcessingTime,
|
maxProcessingTime,
|
||||||
shutdownKey: 'oracle-shutdown',
|
shutdownKey: 'oracle-shutdown',
|
||||||
home: homeConfig,
|
home: homeConfig,
|
||||||
|
@ -6,6 +6,7 @@ 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
|
||||||
|
@ -8,6 +8,7 @@ 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
|
||||||
|
@ -6,6 +6,7 @@ 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
|
||||||
|
@ -1,20 +1,7 @@
|
|||||||
const baseConfig = require('./base.config')
|
const baseConfig = require('./base.config')
|
||||||
const { ERC20_ABI } = require('../../commons')
|
const { ERC20_ABI, ZERO_ADDRESS } = 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') {
|
||||||
@ -22,14 +9,15 @@ 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: {
|
main: baseConfig.foreign,
|
||||||
...baseConfig.foreign,
|
|
||||||
eventContract: new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, initialChecks.bridgeableTokenAddress)
|
|
||||||
},
|
|
||||||
event: 'Transfer',
|
event: 'Transfer',
|
||||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
eventFilter: { to: baseConfig.foreign.bridgeAddress },
|
||||||
|
sender: 'home',
|
||||||
queue: 'home-prioritized',
|
queue: 'home-prioritized',
|
||||||
name: `watcher-${id}`,
|
name: `watcher-${id}`,
|
||||||
id
|
id
|
||||||
|
52
oracle/esController.sol
Normal file
52
oracle/esController.sol
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
/*
|
||||||
|
This contract can be used together with the emergency shutdown
|
||||||
|
functionality of the TokenBridge oracles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity 0.7.6;
|
||||||
|
|
||||||
|
contract PauseController {
|
||||||
|
address public manager;
|
||||||
|
bool internal paused;
|
||||||
|
bytes32 private immutable ID;
|
||||||
|
|
||||||
|
constructor (string memory _id, address _manager) {
|
||||||
|
require(bytes(_id).length <= 32);
|
||||||
|
bytes32 id;
|
||||||
|
assembly {
|
||||||
|
id := mload(add(_id, 32))
|
||||||
|
}
|
||||||
|
ID = id;
|
||||||
|
|
||||||
|
manager = _manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyManager() {
|
||||||
|
require(msg.sender == manager);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeManager(address _newmanager) external onlyManager {
|
||||||
|
require(_newmanager != address(0));
|
||||||
|
manager = _newmanager;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause() external onlyManager {
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function play() external onlyManager {
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPaused() external view returns(bool) {
|
||||||
|
return paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
function id() external view returns(string memory) {
|
||||||
|
return string(abi.encodePacked(ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,10 @@
|
|||||||
"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",
|
||||||
"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",
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
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
|
|
@ -8,12 +8,10 @@ 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}" "$CHECKS" "$TX_HASH"
|
exec node "${WORKER}" "${CONFIG}" $TX_HASH
|
||||||
else
|
else
|
||||||
node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH" | tee -a "${LOG}" | pino-pretty
|
node "${WORKER}" "${CONFIG}" $TX_HASH | tee -a "${LOG}" | pino-pretty
|
||||||
fi
|
fi
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
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')
|
||||||
@ -8,15 +9,30 @@ const { sendTx } = require('./tx/sendTx')
|
|||||||
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_VALIDATOR_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
|
const { ORACLE_ALLOW_HTTP_FOR_RPC } = process.env
|
||||||
|
|
||||||
if (process.argv.length < 5) {
|
if (process.argv.length < 4) {
|
||||||
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 txHash = process.argv[4]
|
const { web3, eventContract, chain } = config.main
|
||||||
|
|
||||||
|
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)
|
||||||
@ -27,15 +43,13 @@ 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(config.chain))
|
web3.currentProvider.subProvider.urls.forEach(checkHttps(chain))
|
||||||
|
|
||||||
attached = await isAttached()
|
attached = await isAttached()
|
||||||
if (attached) {
|
if (attached) {
|
||||||
@ -59,12 +73,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, txHash }), config.maxProcessingTime, () => {
|
await watchdog(() => main({ sendJob, txHashes }), 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, txHash })
|
await main({ sendJob, txHashes })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -99,8 +113,11 @@ function processEvents(events) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main({ sendJob, txHash }) {
|
async function main({ sendJob, txHashes }) {
|
||||||
|
logger.info(`Processing ${txHashes.length} input transactions`)
|
||||||
|
for (const txHash of txHashes) {
|
||||||
try {
|
try {
|
||||||
|
logger.info({ txHash }, `Processing transaction`)
|
||||||
const events = await getEventsFromTx({
|
const events = await getEventsFromTx({
|
||||||
web3,
|
web3,
|
||||||
contract: eventContract,
|
contract: eventContract,
|
||||||
@ -108,11 +125,11 @@ async function main({ sendJob, txHash }) {
|
|||||||
txHash,
|
txHash,
|
||||||
filter: config.eventFilter
|
filter: config.eventFilter
|
||||||
})
|
})
|
||||||
logger.info(`Found ${events.length} ${config.event} events`)
|
logger.info({ txHash }, `Found ${events.length} ${config.event} events`)
|
||||||
|
|
||||||
if (events.length) {
|
if (events.length) {
|
||||||
const job = await processEvents(events)
|
const job = await processEvents(events)
|
||||||
logger.info('Transactions to send:', job.length)
|
logger.info({ txHash }, 'Transactions to send:', job.length)
|
||||||
|
|
||||||
if (job.length) {
|
if (job.length) {
|
||||||
await sendJob(job)
|
await sendJob(job)
|
||||||
@ -121,6 +138,7 @@ async function main({ sendJob, txHash }) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await connection.close()
|
await connection.close()
|
||||||
|
|
||||||
@ -128,9 +146,13 @@ async function main({ sendJob, txHash }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function sendJobTx(jobs) {
|
async function sendJobTx(jobs) {
|
||||||
const gasPrice = await GasPrice.start(config.chain, true)
|
await GasPrice.start(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, ORACLE_VALIDATOR_ADDRESS)
|
let nonce = await getNonce(web3, config.validatorAddress)
|
||||||
|
|
||||||
await syncForEach(jobs, async job => {
|
await syncForEach(jobs, async job => {
|
||||||
let gasLimit
|
let gasLimit
|
||||||
@ -145,10 +167,10 @@ async function sendJobTx(jobs) {
|
|||||||
const txHash = await sendTx({
|
const txHash = await sendTx({
|
||||||
data: job.data,
|
data: job.data,
|
||||||
nonce,
|
nonce,
|
||||||
gasPrice: gasPrice.toString(10),
|
gasPrice,
|
||||||
amount: '0',
|
amount: '0',
|
||||||
gasLimit,
|
gasLimit,
|
||||||
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
privateKey: config.validatorPrivateKey,
|
||||||
to: job.to,
|
to: job.to,
|
||||||
chainId,
|
chainId,
|
||||||
web3
|
web3
|
||||||
@ -167,7 +189,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(ORACLE_VALIDATOR_ADDRESS)
|
const currentBalance = await web3.eth.getBalance(config.validatorAddress)
|
||||||
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}.`
|
||||||
|
@ -8,8 +8,6 @@ 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) {
|
||||||
@ -37,7 +35,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, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
|
const signature = web3.eth.accounts.sign(message, config.validatorPrivateKey)
|
||||||
|
|
||||||
let gasEstimate
|
let gasEstimate
|
||||||
try {
|
try {
|
||||||
|
@ -8,8 +8,6 @@ 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) {
|
||||||
@ -48,7 +46,7 @@ function processSignatureRequestsBuilder(config) {
|
|||||||
expectedMessageLength
|
expectedMessageLength
|
||||||
})
|
})
|
||||||
|
|
||||||
const signature = web3.eth.accounts.sign(message, `0x${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}`)
|
const signature = web3.eth.accounts.sign(message, config.validatorPrivateKey)
|
||||||
|
|
||||||
let gasEstimate
|
let gasEstimate
|
||||||
try {
|
try {
|
||||||
|
@ -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,
|
||||||
nonceError
|
isGasPriceError,
|
||||||
|
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_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_TX_REDUNDANCY } = process.env
|
const { 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.chain))
|
web3.currentProvider.subProvider.urls.forEach(checkHttps(config.id))
|
||||||
|
|
||||||
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, ORACLE_VALIDATOR_ADDRESS)
|
return getNonce(web3, config.validatorAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ORACLE_VALIDATOR_ADDRESS)
|
return getNonce(web3, config.validatorAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
|||||||
gasPrice,
|
gasPrice,
|
||||||
amount: '0',
|
amount: '0',
|
||||||
gasLimit,
|
gasLimit,
|
||||||
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
privateKey: config.validatorPrivateKey,
|
||||||
to: job.to,
|
to: job.to,
|
||||||
chainId,
|
chainId,
|
||||||
web3: web3Redundant
|
web3: web3Redundant
|
||||||
@ -192,12 +192,11 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
|||||||
e.message
|
e.message
|
||||||
)
|
)
|
||||||
|
|
||||||
const message = e.message.toLowerCase()
|
if (isGasPriceError(e)) {
|
||||||
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 || message.includes('transaction with the same hash was already imported')) {
|
} else if (isResend || isSameTransactionError(e)) {
|
||||||
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
|
||||||
@ -206,14 +205,14 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
|
|||||||
failedTx.push(job)
|
failedTx.push(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.includes('insufficient funds')) {
|
if (isInsufficientBalanceError(e)) {
|
||||||
insufficientFunds = true
|
insufficientFunds = true
|
||||||
const currentBalance = await web3.eth.getBalance(ORACLE_VALIDATOR_ADDRESS)
|
const currentBalance = await web3.eth.getBalance(config.validatorAddress)
|
||||||
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 (nonceError(e)) {
|
} else if (isNonceError(e)) {
|
||||||
nonce = await readNonce(true)
|
nonce = await readNonce(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,7 +237,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, ORACLE_VALIDATOR_ADDRESS, minimumBalance, resume, logger)
|
waitForFunds(web3, config.validatorAddress, minimumBalance, resume, logger)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
@ -2,6 +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')
|
||||||
|
|
||||||
// From EIP-1474 and Infura documentation
|
// From EIP-1474 and Infura documentation
|
||||||
const JSONRPC_ERROR_CODES = [-32603, -32002, -32005]
|
const JSONRPC_ERROR_CODES = [-32603, -32002, -32005]
|
||||||
|
|
||||||
@ -33,14 +35,10 @@ 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 = {
|
|
||||||
debug: () => {},
|
|
||||||
info: () => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpListProvider.prototype.setLogger = function(logger) {
|
onInjected(logger => {
|
||||||
this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` })
|
this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpListProvider.prototype.send = async function send(payload, callback) {
|
HttpListProvider.prototype.send = async function send(payload, callback) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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)) {
|
||||||
@ -13,10 +14,9 @@ function RedundantHttpListProvider(urls, options = {}) {
|
|||||||
|
|
||||||
this.urls = urls
|
this.urls = urls
|
||||||
this.options = { ...defaultOptions, ...options }
|
this.options = { ...defaultOptions, ...options }
|
||||||
}
|
onInjected(logger => {
|
||||||
|
|
||||||
RedundantHttpListProvider.prototype.setLogger = function(logger) {
|
|
||||||
this.logger = logger.child({ module: `RedundantHttpListProvider:${this.options.name}` })
|
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) {
|
||||||
|
41
oracle/src/services/SafeEthLogsProvider.js
Normal file
41
oracle/src/services/SafeEthLogsProvider.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const { hexToNumber, isHexStrict } = require('web3').utils
|
||||||
|
|
||||||
|
const { onInjected } = require('./injectedLogger')
|
||||||
|
|
||||||
|
function SafeEthLogsProvider(provider) {
|
||||||
|
this.subProvider = provider
|
||||||
|
onInjected(logger => {
|
||||||
|
this.logger = logger.child({ module: 'SafeEthLogsProvider' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeEthLogsProvider.prototype.send = function send(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: [] }]
|
||||||
|
this.subProvider.send(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 {
|
||||||
|
this.subProvider.send(payload, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SafeEthLogsProvider
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
require('../../env')
|
require('../../env')
|
||||||
const fetch = require('node-fetch')
|
|
||||||
const { home, foreign } = require('../../config/base.config')
|
const { home, foreign } = require('../../config/base.config')
|
||||||
const logger = require('../services/logger').child({
|
const logger = require('../services/logger').child({
|
||||||
module: 'gasPrice'
|
module: 'gasPrice'
|
||||||
@ -25,11 +24,11 @@ let cachedGasPrice = null
|
|||||||
|
|
||||||
let fetchGasPriceInterval = null
|
let fetchGasPriceInterval = null
|
||||||
|
|
||||||
const fetchGasPrice = async (speedType, factor, bridgeContract, gasPriceSupplierFetchFn) => {
|
const fetchGasPrice = async (speedType, factor, bridgeContract, gasPriceSupplierUrl) => {
|
||||||
const contractOptions = { logger }
|
const contractOptions = { logger }
|
||||||
const supplierOptions = { speedType, factor, limits: GAS_PRICE_BOUNDARIES, logger }
|
const supplierOptions = { speedType, factor, limits: GAS_PRICE_BOUNDARIES, logger }
|
||||||
cachedGasPrice =
|
cachedGasPrice =
|
||||||
(await gasPriceFromSupplier(gasPriceSupplierFetchFn, supplierOptions)) ||
|
(await gasPriceFromSupplier(gasPriceSupplierUrl, supplierOptions)) ||
|
||||||
(await gasPriceFromContract(bridgeContract, contractOptions)) ||
|
(await gasPriceFromContract(bridgeContract, contractOptions)) ||
|
||||||
cachedGasPrice
|
cachedGasPrice
|
||||||
return cachedGasPrice
|
return cachedGasPrice
|
||||||
@ -63,16 +62,15 @@ async function start(chainId, fetchOnce) {
|
|||||||
throw new Error(`Unrecognized chainId '${chainId}'`)
|
throw new Error(`Unrecognized chainId '${chainId}'`)
|
||||||
}
|
}
|
||||||
|
|
||||||
let fetchFn = null
|
if (!gasPriceSupplierUrl) {
|
||||||
if (gasPriceSupplierUrl !== 'gas-price-oracle') {
|
logger.warn({ chainId }, 'Gas price API is not configured, will fallback to the contract-supplied gas price')
|
||||||
fetchFn = () => fetch(gasPriceSupplierUrl, { timeout: 2000 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fetchOnce) {
|
if (fetchOnce) {
|
||||||
await fetchGasPrice(speedType, factor, contract, fetchFn)
|
await fetchGasPrice(speedType, factor, contract, gasPriceSupplierUrl)
|
||||||
} else {
|
} else {
|
||||||
fetchGasPriceInterval = await setIntervalAndRun(
|
fetchGasPriceInterval = await setIntervalAndRun(
|
||||||
() => fetchGasPrice(speedType, factor, contract, fetchFn),
|
() => fetchGasPrice(speedType, factor, contract, gasPriceSupplierUrl),
|
||||||
updateInterval
|
updateInterval
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
27
oracle/src/services/injectedLogger.js
Normal file
27
oracle/src/services/injectedLogger.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// workaround to avoid circular dependencies in module imports
|
||||||
|
// e.g. logger -> config -> web3 -> provider -> logger
|
||||||
|
// transforms to the following import chain
|
||||||
|
// logger -> config -> web3 -> provider -> injectedLogger
|
||||||
|
// logger -> injectedLogger
|
||||||
|
|
||||||
|
let logger
|
||||||
|
|
||||||
|
const callbacks = []
|
||||||
|
|
||||||
|
function onInjected(cb) {
|
||||||
|
if (logger) {
|
||||||
|
cb(logger)
|
||||||
|
} else {
|
||||||
|
callbacks.push(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLogger(newLogger) {
|
||||||
|
logger = newLogger
|
||||||
|
callbacks.forEach(cb => cb(logger))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
onInjected,
|
||||||
|
setLogger
|
||||||
|
}
|
@ -1,15 +1,7 @@
|
|||||||
const pino = require('pino')
|
const pino = require('pino')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const {
|
|
||||||
web3Home,
|
const { setLogger } = require('./injectedLogger')
|
||||||
web3Foreign,
|
|
||||||
web3ForeignArchive,
|
|
||||||
web3Side,
|
|
||||||
web3HomeFallback,
|
|
||||||
web3ForeignFallback,
|
|
||||||
web3HomeRedundant,
|
|
||||||
web3ForeignRedundant
|
|
||||||
} = require('./web3')
|
|
||||||
|
|
||||||
const config = process.env.NODE_ENV !== 'test' ? require(path.join('../../config/', process.argv[2])) : {}
|
const config = process.env.NODE_ENV !== 'test' ? require(path.join('../../config/', process.argv[2])) : {}
|
||||||
|
|
||||||
@ -17,27 +9,11 @@ const logger = pino({
|
|||||||
enabled: process.env.NODE_ENV !== 'test',
|
enabled: process.env.NODE_ENV !== 'test',
|
||||||
name: config.name,
|
name: config.name,
|
||||||
level: process.env.ORACLE_LOG_LEVEL || 'debug',
|
level: process.env.ORACLE_LOG_LEVEL || 'debug',
|
||||||
base:
|
base: {
|
||||||
process.env.NODE_ENV === 'production'
|
validator: config.validatorAddress
|
||||||
? {
|
|
||||||
validator: process.env.ORACLE_VALIDATOR_ADDRESS
|
|
||||||
}
|
}
|
||||||
: {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
web3Home.currentProvider.setLogger(logger)
|
setLogger(logger)
|
||||||
web3Foreign.currentProvider.setLogger(logger)
|
|
||||||
web3HomeFallback.currentProvider.setLogger(logger)
|
|
||||||
web3ForeignFallback.currentProvider.setLogger(logger)
|
|
||||||
web3HomeRedundant.currentProvider.setLogger(logger)
|
|
||||||
web3ForeignRedundant.currentProvider.setLogger(logger)
|
|
||||||
|
|
||||||
if (web3ForeignArchive) {
|
|
||||||
web3ForeignArchive.currentProvider.setLogger(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (web3Side) {
|
|
||||||
web3Side.currentProvider.setLogger(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = logger
|
module.exports = logger
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const { HttpListProvider } = require('./HttpListProvider')
|
const { HttpListProvider } = require('./HttpListProvider')
|
||||||
|
const { SafeEthLogsProvider } = require('./SafeEthLogsProvider')
|
||||||
const { RedundantHttpListProvider } = require('./RedundantHttpListProvider')
|
const { RedundantHttpListProvider } = require('./RedundantHttpListProvider')
|
||||||
const { RETRY_CONFIG } = require('../utils/constants')
|
const { RETRY_CONFIG } = require('../utils/constants')
|
||||||
|
|
||||||
@ -37,10 +38,10 @@ const foreignOptions = {
|
|||||||
retry: RETRY_CONFIG
|
retry: RETRY_CONFIG
|
||||||
}
|
}
|
||||||
|
|
||||||
const homeProvider = new HttpListProvider(homeUrls, homeOptions)
|
const homeProvider = new SafeEthLogsProvider(new HttpListProvider(homeUrls, homeOptions))
|
||||||
const web3Home = new Web3(homeProvider)
|
const web3Home = new Web3(homeProvider)
|
||||||
|
|
||||||
const foreignProvider = new HttpListProvider(foreignUrls, foreignOptions)
|
const foreignProvider = new SafeEthLogsProvider(new HttpListProvider(foreignUrls, foreignOptions))
|
||||||
const web3Foreign = new Web3(foreignProvider)
|
const web3Foreign = new Web3(foreignProvider)
|
||||||
|
|
||||||
let web3ForeignArchive = null
|
let web3ForeignArchive = null
|
||||||
|
@ -11,7 +11,7 @@ async function sendTx({ privateKey, data, nonce, gasPrice, amount, gasLimit, to,
|
|||||||
gasPrice,
|
gasPrice,
|
||||||
gas: gasLimit
|
gas: gasLimit
|
||||||
},
|
},
|
||||||
`0x${privateKey}`
|
privateKey
|
||||||
)
|
)
|
||||||
|
|
||||||
return new Promise((res, rej) =>
|
return new Promise((res, rej) =>
|
||||||
|
@ -102,6 +102,7 @@ async function getEventsFromTx({ web3, contract, event, txHash, filter }) {
|
|||||||
const eventAbi = contract.options.jsonInterface.find(abi => abi.name === event)
|
const eventAbi = contract.options.jsonInterface.find(abi => abi.name === event)
|
||||||
const decodeAbi = contract._decodeEventABI.bind(eventAbi)
|
const decodeAbi = contract._decodeEventABI.bind(eventAbi)
|
||||||
const pastEvents = logs
|
const pastEvents = logs
|
||||||
|
.filter(event => event.address.toLowerCase() === contractAddress.toLowerCase())
|
||||||
.filter(event => event.topics[0] === eventAbi.signature)
|
.filter(event => event.topics[0] === eventAbi.signature)
|
||||||
.map(decodeAbi)
|
.map(decodeAbi)
|
||||||
.filter(event =>
|
.filter(event =>
|
||||||
|
@ -99,7 +99,22 @@ function privateKeyToAddress(privateKey) {
|
|||||||
return privateKey ? new Web3().eth.accounts.privateKeyToAccount(add0xPrefix(privateKey)).address : null
|
return privateKey ? new Web3().eth.accounts.privateKeyToAccount(add0xPrefix(privateKey)).address : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function nonceError(e) {
|
function isGasPriceError(e) {
|
||||||
|
const message = e.message.toLowerCase()
|
||||||
|
return message.includes('replacement transaction underpriced')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSameTransactionError(e) {
|
||||||
|
const message = e.message.toLowerCase()
|
||||||
|
return message.includes('transaction with the same hash was already imported') || message.includes('already known')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInsufficientBalanceError(e) {
|
||||||
|
const message = e.message.toLowerCase()
|
||||||
|
return message.includes('insufficient funds')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNonceError(e) {
|
||||||
const message = e.message.toLowerCase()
|
const message = e.message.toLowerCase()
|
||||||
return (
|
return (
|
||||||
message.includes('transaction nonce is too low') ||
|
message.includes('transaction nonce is too low') ||
|
||||||
@ -153,8 +168,12 @@ module.exports = {
|
|||||||
addExtraGas,
|
addExtraGas,
|
||||||
setIntervalAndRun,
|
setIntervalAndRun,
|
||||||
watchdog,
|
watchdog,
|
||||||
|
add0xPrefix,
|
||||||
privateKeyToAddress,
|
privateKeyToAddress,
|
||||||
nonceError,
|
isGasPriceError,
|
||||||
|
isSameTransactionError,
|
||||||
|
isInsufficientBalanceError,
|
||||||
|
isNonceError,
|
||||||
getRetrySequence,
|
getRetrySequence,
|
||||||
promiseAny,
|
promiseAny,
|
||||||
readAccessListFile,
|
readAccessListFile,
|
||||||
|
@ -34,7 +34,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(chain))
|
web3.currentProvider.subProvider.urls.forEach(checkHttps(chain))
|
||||||
|
|
||||||
await getLastProcessedBlock()
|
await getLastProcessedBlock()
|
||||||
connectWatcherToQueue({
|
connectWatcherToQueue({
|
||||||
|
@ -3,77 +3,75 @@ const { expect } = require('chai')
|
|||||||
const proxyquire = require('proxyquire').noPreserveCache()
|
const proxyquire = require('proxyquire').noPreserveCache()
|
||||||
const { DEFAULT_UPDATE_INTERVAL } = require('../src/utils/constants')
|
const { DEFAULT_UPDATE_INTERVAL } = require('../src/utils/constants')
|
||||||
|
|
||||||
describe('gasPrice', () => {
|
|
||||||
describe('start', () => {
|
|
||||||
const utils = { setIntervalAndRun: sinon.spy() }
|
const utils = { setIntervalAndRun: sinon.spy() }
|
||||||
|
const fetchStub = () => ({
|
||||||
|
json: () => ({
|
||||||
|
standard: '103'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const fakeLogger = { error: sinon.spy(), warn: sinon.spy(), child: () => fakeLogger }
|
||||||
|
fetchStub['@global'] = true
|
||||||
|
const gasPriceDefault = proxyquire('../src/services/gasPrice', {
|
||||||
|
'../utils/utils': utils,
|
||||||
|
'node-fetch': fetchStub,
|
||||||
|
'../services/logger': { child: () => fakeLogger }
|
||||||
|
})
|
||||||
|
process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL = 15000
|
||||||
|
process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL = 30000
|
||||||
|
process.env.COMMON_HOME_GAS_PRICE_FALLBACK = '101000000000'
|
||||||
|
const gasPrice = proxyquire('../src/services/gasPrice', {
|
||||||
|
'../utils/utils': utils,
|
||||||
|
'node-fetch': fetchStub,
|
||||||
|
'../services/logger': { child: () => fakeLogger }
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('gasPrice', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
utils.setIntervalAndRun.resetHistory()
|
utils.setIntervalAndRun.resetHistory()
|
||||||
|
fakeLogger.error.resetHistory()
|
||||||
|
fakeLogger.warn.resetHistory()
|
||||||
})
|
})
|
||||||
it('should call setIntervalAndRun with ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL interval value on Home', async () => {
|
|
||||||
// given
|
|
||||||
process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL = 15000
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
|
|
||||||
|
describe('start', () => {
|
||||||
|
it('should call setIntervalAndRun with ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL interval value on Home', async () => {
|
||||||
// when
|
// when
|
||||||
await gasPrice.start('home')
|
await gasPrice.start('home')
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL).to.equal('15000')
|
|
||||||
expect(process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL).to.not.equal(DEFAULT_UPDATE_INTERVAL.toString())
|
|
||||||
expect(utils.setIntervalAndRun.args[0][1]).to.equal(process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL.toString())
|
expect(utils.setIntervalAndRun.args[0][1]).to.equal(process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL.toString())
|
||||||
})
|
})
|
||||||
it('should call setIntervalAndRun with ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL interval value on Foreign', async () => {
|
it('should call setIntervalAndRun with ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL interval value on Foreign', async () => {
|
||||||
// given
|
|
||||||
process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL = 15000
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.start('foreign')
|
await gasPrice.start('foreign')
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL).to.equal('15000')
|
|
||||||
expect(process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL).to.not.equal(DEFAULT_UPDATE_INTERVAL.toString())
|
|
||||||
expect(utils.setIntervalAndRun.args[0][1]).to.equal(
|
expect(utils.setIntervalAndRun.args[0][1]).to.equal(
|
||||||
process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL.toString()
|
process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL.toString()
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('should call setIntervalAndRun with default interval value on Home', async () => {
|
it('should call setIntervalAndRun with default interval value on Home', async () => {
|
||||||
// given
|
|
||||||
delete process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.start('home')
|
await gasPriceDefault.start('home')
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(process.env.ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL).to.equal(undefined)
|
|
||||||
expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL)
|
expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL)
|
||||||
})
|
})
|
||||||
it('should call setIntervalAndRun with default interval value on Foreign', async () => {
|
it('should call setIntervalAndRun with default interval value on Foreign', async () => {
|
||||||
// given
|
|
||||||
delete process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.start('foreign')
|
await gasPriceDefault.start('foreign')
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(process.env.ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL).to.equal(undefined)
|
|
||||||
expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL)
|
expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('fetching gas price', () => {
|
describe('fetching gas price', () => {
|
||||||
const utils = { setIntervalAndRun: () => {} }
|
|
||||||
|
|
||||||
it('should fall back to default if contract and supplier are not working', async () => {
|
it('should fall back to default if contract and supplier are not working', async () => {
|
||||||
// given
|
// given
|
||||||
process.env.COMMON_HOME_GAS_PRICE_FALLBACK = '101000000000'
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
await gasPrice.start('home')
|
await gasPrice.start('home')
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.fetchGasPrice('standard', 1, null, () => null)
|
await gasPrice.fetchGasPrice('standard', 1, null, null)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(gasPrice.getPrice()).to.equal('101000000000')
|
expect(gasPrice.getPrice()).to.equal('101000000000')
|
||||||
@ -81,18 +79,10 @@ describe('gasPrice', () => {
|
|||||||
|
|
||||||
it('should fetch gas from supplier', async () => {
|
it('should fetch gas from supplier', async () => {
|
||||||
// given
|
// given
|
||||||
process.env.COMMON_HOME_GAS_PRICE_FALLBACK = '101000000000'
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
await gasPrice.start('home')
|
await gasPrice.start('home')
|
||||||
|
|
||||||
const gasPriceSupplierFetchFn = () => ({
|
|
||||||
json: () => ({
|
|
||||||
standard: '103'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.fetchGasPrice('standard', 1, null, gasPriceSupplierFetchFn)
|
await gasPrice.fetchGasPrice('standard', 1, null, 'url')
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(gasPrice.getPrice().toString()).to.equal('103000000000')
|
expect(gasPrice.getPrice().toString()).to.equal('103000000000')
|
||||||
@ -100,8 +90,6 @@ describe('gasPrice', () => {
|
|||||||
|
|
||||||
it('should fetch gas from contract', async () => {
|
it('should fetch gas from contract', async () => {
|
||||||
// given
|
// given
|
||||||
process.env.COMMON_HOME_GAS_PRICE_FALLBACK = '101000000000'
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
await gasPrice.start('home')
|
await gasPrice.start('home')
|
||||||
|
|
||||||
const bridgeContractMock = {
|
const bridgeContractMock = {
|
||||||
@ -113,7 +101,7 @@ describe('gasPrice', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, () => {})
|
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, null)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(gasPrice.getPrice().toString()).to.equal('102000000000')
|
expect(gasPrice.getPrice().toString()).to.equal('102000000000')
|
||||||
@ -121,8 +109,6 @@ describe('gasPrice', () => {
|
|||||||
|
|
||||||
it('should fetch the gas price from the oracle first', async () => {
|
it('should fetch the gas price from the oracle first', async () => {
|
||||||
// given
|
// given
|
||||||
process.env.COMMON_HOME_GAS_PRICE_FALLBACK = '101000000000'
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
|
||||||
await gasPrice.start('home')
|
await gasPrice.start('home')
|
||||||
|
|
||||||
const bridgeContractMock = {
|
const bridgeContractMock = {
|
||||||
@ -133,33 +119,23 @@ describe('gasPrice', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const gasPriceSupplierFetchFn = () => ({
|
|
||||||
json: () => ({
|
|
||||||
standard: '103'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, gasPriceSupplierFetchFn)
|
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, 'url')
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(gasPrice.getPrice().toString()).to.equal('103000000000')
|
expect(gasPrice.getPrice().toString()).to.equal('103000000000')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('log errors using the logger', async () => {
|
it('log error using the logger', async () => {
|
||||||
// given
|
// given
|
||||||
const fakeLogger = { error: sinon.spy() }
|
|
||||||
const gasPrice = proxyquire('../src/services/gasPrice', {
|
|
||||||
'../utils/utils': utils,
|
|
||||||
'../services/logger': { child: () => fakeLogger }
|
|
||||||
})
|
|
||||||
await gasPrice.start('home')
|
await gasPrice.start('home')
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await gasPrice.fetchGasPrice('standard', 1, null, () => {})
|
await gasPrice.fetchGasPrice('standard', 1, null, null)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(fakeLogger.error.calledTwice).to.equal(true) // two errors
|
expect(fakeLogger.warn.calledOnce).to.equal(true) // one warning
|
||||||
|
expect(fakeLogger.error.calledOnce).to.equal(true) // one error
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user