Compare commits
37 Commits
3.0.0-rc0
...
test-molec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edea25ee38 | ||
|
|
db1eddccb8 | ||
|
|
03dfaa9bb5 | ||
|
|
e899b15808 | ||
|
|
4eba91ef7e | ||
|
|
1e3aa53ab3 | ||
|
|
a05ff51555 | ||
|
|
52e1c88b58 | ||
|
|
d36dcadd34 | ||
|
|
d543dbb339 | ||
|
|
bd21cc163e | ||
|
|
4a0fc936a1 | ||
|
|
78564afabd | ||
|
|
70a2c30b4c | ||
|
|
06a9586148 | ||
|
|
7379fe4190 | ||
|
|
e59766c5df | ||
|
|
fdb18a1a17 | ||
|
|
4412046f66 | ||
|
|
0ff224ccd3 | ||
|
|
5cedacafe5 | ||
|
|
2e6179f974 | ||
|
|
4f5e3c47be | ||
|
|
4c06329153 | ||
|
|
d53452675e | ||
|
|
c92f80c484 | ||
|
|
5e95b5d8c5 | ||
|
|
6e1f57493a | ||
|
|
8ed6550635 | ||
|
|
c8eb0f1ed8 | ||
|
|
3e5e50c06e | ||
|
|
92e1b597c4 | ||
|
|
8b1a97e673 | ||
|
|
3cf184c391 | ||
|
|
8f72516374 | ||
|
|
98155e3075 | ||
|
|
0d724147bd |
21
.github/workflows/main.yml
vendored
21
.github/workflows/main.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Rebuild and push updated images
|
||||
run: |
|
||||
function check_if_image_exists() {
|
||||
curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
|
||||
curl -fsSlL "https://${{ github.actor }}:${{ github.token }}@${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
|
||||
}
|
||||
updated=()
|
||||
if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
- name: Rebuild and push molecule runner e2e image
|
||||
run: |
|
||||
function check_if_image_exists() {
|
||||
curl -fsSlL -H 'Authorization: bearer ${{ github.token }}' "https://${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
|
||||
curl -fsSlL "https://${{ github.actor }}:${{ github.token }}@${DOCKER_REGISTRY}/v2/${DOCKER_REPO}/tokenbridge-e2e-$1/manifests/$2" > /dev/null
|
||||
}
|
||||
if check_if_image_exists molecule_runner ${MOLECULE_RUNNER_TAG}; then
|
||||
echo "Image already exists"
|
||||
@@ -149,6 +149,12 @@ jobs:
|
||||
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
|
||||
- name: 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:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
@@ -199,7 +205,7 @@ jobs:
|
||||
- name: Login to docker registry
|
||||
run: docker login ${DOCKER_REGISTRY} -u ${{ github.actor }} -p ${{ github.token }}
|
||||
- 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
|
||||
run: |
|
||||
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
|
||||
- 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 }}
|
||||
- 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
|
||||
|
||||
@@ -52,6 +52,9 @@ ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain a
|
||||
ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature`
|
||||
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer`
|
||||
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Home chain. Infinite, if not provided. | `integer`
|
||||
ORACLE_JSONRPC_ERROR_CODES | Override default JSON rpc error codes that can trigger RPC fallback to the next URL from the list (or a retry in case of a single RPC URL). Default is `-32603,-32002,-32005`. Should be a comma-separated list of negative integers. | `string`
|
||||
ORACLE_VALIDATOR_KEYSTORE_PATH | Path to the keystore v3 json file with the encrypted validator key. | `string`
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD | Password from the provided keystore file, oracle won't startup properly, if the provided password is invalid | `string`
|
||||
|
||||
|
||||
## Monitor configuration
|
||||
|
||||
@@ -19,6 +19,7 @@ COPY --from=contracts /mono/contracts/build ./contracts/build
|
||||
COPY commons/package.json ./commons/
|
||||
COPY oracle-e2e/package.json ./oracle-e2e/
|
||||
COPY monitor-e2e/package.json ./monitor-e2e/
|
||||
COPY oracle/src/utils/constants.js ./oracle/src/utils/constants.js
|
||||
|
||||
COPY yarn.lock .
|
||||
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
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
|
||||
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 statusSelector = 'label[data-id="status"]'
|
||||
|
||||
const homeToForeignTxURL = 'http://localhost:3004/77/0xbc83d43bdc675a615a2b820e43e52d25857aa5fdd77acf2dd92cd247af2c693c'
|
||||
const foreignToHomeTxURL = 'http://localhost:3004/42/0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
||||
const homeToForeignTxURL = 'http://localhost:3004/77/0x295efbe6ae98937ef35d939376c9bd752b4dc6f6899a9d5ddd6a57cea3d76c89'
|
||||
const foreignToHomeTxURL = 'http://localhost:3004/42/0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd'
|
||||
|
||||
describe('ALM', () => {
|
||||
let browser
|
||||
|
||||
@@ -123,6 +123,24 @@ const abi: AbiItem[] = [
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes'
|
||||
},
|
||||
{
|
||||
name: '_signatures',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'safeExecuteSignaturesWithAutoGasLimit',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks'
|
||||
import { useWindowWidth } from '@react-hook/window-size'
|
||||
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 { Thead, AgeTd, StatusTd } from './commons/Table'
|
||||
import { ManualExecutionButton } from './ManualExecutionButton'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
|
||||
const StyledExecutionConfirmation = styled.div`
|
||||
margin-top: 30px;
|
||||
@@ -33,6 +34,8 @@ export const ExecutionConfirmation = ({
|
||||
executionEventsFetched,
|
||||
setPendingExecution
|
||||
}: ExecutionConfirmationParams) => {
|
||||
const { foreign } = useStateProvider()
|
||||
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
|
||||
const availableManualExecution =
|
||||
!isHome &&
|
||||
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
|
||||
@@ -48,6 +51,22 @@ export const ExecutionConfirmation = ({
|
||||
const formattedValidator =
|
||||
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 = '') => {
|
||||
switch (validatorStatus) {
|
||||
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
||||
@@ -105,6 +124,7 @@ export const ExecutionConfirmation = ({
|
||||
{availableManualExecution && (
|
||||
<td>
|
||||
<ManualExecutionButton
|
||||
safeExecutionAvailable={safeExecutionAvailable}
|
||||
messageData={messageData}
|
||||
setExecutionData={setExecutionData}
|
||||
signatureCollected={signatureCollected as string[]}
|
||||
|
||||
@@ -15,16 +15,19 @@ import { signatureToVRS, packSignatures } from '../utils/signatures'
|
||||
import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
|
||||
const StyledButton = styled.button`
|
||||
const ActionButton = styled.button`
|
||||
color: var(--button-color);
|
||||
border-color: var(--font-color);
|
||||
margin-top: 10px;
|
||||
min-width: 120px;
|
||||
padding: 1rem;
|
||||
&:focus {
|
||||
outline: var(--button-color);
|
||||
}
|
||||
`
|
||||
|
||||
interface ManualExecutionButtonParams {
|
||||
safeExecutionAvailable: boolean
|
||||
messageData: string
|
||||
setExecutionData: Function
|
||||
signatureCollected: string[]
|
||||
@@ -32,6 +35,7 @@ interface ManualExecutionButtonParams {
|
||||
}
|
||||
|
||||
export const ManualExecutionButton = ({
|
||||
safeExecutionAvailable,
|
||||
messageData,
|
||||
setExecutionData,
|
||||
signatureCollected,
|
||||
@@ -40,6 +44,8 @@ export const ManualExecutionButton = ({
|
||||
const { foreign, setError } = useStateProvider()
|
||||
const { library, activate, account, active } = useWeb3React()
|
||||
const [manualExecution, setManualExecution] = useState(false)
|
||||
const [allowFailures, setAllowFailures] = useState(false)
|
||||
const notReady = !foreign.bridgeContract || !signatureCollected || !signatureCollected.length
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
@@ -72,7 +78,11 @@ export const ManualExecutionButton = ({
|
||||
const signatures = packSignatures(signatureCollected.map(signatureToVRS))
|
||||
const messageId = messageData.slice(0, 66)
|
||||
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)
|
||||
|
||||
library.eth
|
||||
@@ -132,15 +142,35 @@ export const ManualExecutionButton = ({
|
||||
messageData,
|
||||
signatureCollected,
|
||||
setExecutionData,
|
||||
setPendingExecution
|
||||
setPendingExecution,
|
||||
safeExecutionAvailable,
|
||||
allowFailures
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="is-center">
|
||||
<StyledButton className="button outline" onClick={() => setManualExecution(true)}>
|
||||
Execute
|
||||
</StyledButton>
|
||||
<div>
|
||||
<div className="is-center">
|
||||
<ActionButton disabled={notReady} className="button outline" onClick={() => setManualExecution(true)}>
|
||||
Execute
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -86,6 +86,14 @@ export const getConfirmationsForTx = async (
|
||||
setPendingConfirmations(validatorPendingConfirmations.length > 0)
|
||||
} else {
|
||||
setPendingConfirmations(false)
|
||||
if (fromHome) {
|
||||
// fetch collected signatures for possible manual processing
|
||||
setSignatureCollected(
|
||||
await Promise.all(
|
||||
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const undefinedConfirmations = validatorConfirmations.filter(
|
||||
@@ -115,15 +123,6 @@ export const getConfirmationsForTx = async (
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
|
||||
}))
|
||||
updateConfirmations(notRequiredConfirmations)
|
||||
|
||||
if (fromHome) {
|
||||
// fetch collected signatures for possible manual processing
|
||||
setSignatureCollected(
|
||||
await Promise.all(
|
||||
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// get transactions from success signatures
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
const HOME_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/HomeBridgeErcToNative').abi
|
||||
const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/ForeignBridgeErcToNative').abi
|
||||
const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/XDaiForeignBridge.json').abi
|
||||
const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi
|
||||
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/BlockRewardMock').abi
|
||||
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const { soliditySha3 } = require('web3-utils')
|
||||
|
||||
function strip0x(input) {
|
||||
return input.replace(/^0x/, '')
|
||||
}
|
||||
@@ -39,8 +41,35 @@ const normalizeAMBMessageEvent = e => {
|
||||
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 = {
|
||||
strip0x,
|
||||
parseAMBMessage,
|
||||
normalizeAMBMessageEvent
|
||||
normalizeAMBMessageEvent,
|
||||
ambInformationSignatures,
|
||||
normalizeAMBInfoRequest
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"gas-price-oracle": "^0.1.5",
|
||||
"web3-utils": "^1.3.0"
|
||||
"web3-utils": "^1.3.0",
|
||||
"node-fetch": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bn-chai": "^1.0.1",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { toWei, toBN, BN } = require('web3-utils')
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const fetch = require('node-fetch')
|
||||
const { BRIDGE_MODES } = require('./constants')
|
||||
const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
|
||||
|
||||
@@ -178,17 +179,16 @@ const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
|
||||
return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei'))
|
||||
}
|
||||
|
||||
// fetchFn has to be supplied (instead of just url to oracle),
|
||||
// because this utility function is shared between Browser and Node,
|
||||
// we use built-in 'fetch' on browser side, and `node-fetch` package in Node.
|
||||
const gasPriceFromSupplier = async (fetchFn, options = {}) => {
|
||||
const gasPriceFromSupplier = async (url, options = {}) => {
|
||||
try {
|
||||
let json
|
||||
if (fetchFn) {
|
||||
const response = await fetchFn()
|
||||
if (url === 'gas-price-oracle') {
|
||||
json = await gasPriceOracle.fetchGasPricesOffChain()
|
||||
} else if (url) {
|
||||
const response = await fetch(url, { timeout: 2000 })
|
||||
json = await response.json()
|
||||
} else {
|
||||
json = await gasPriceOracle.fetchGasPricesOffChain()
|
||||
return null
|
||||
}
|
||||
const oracleGasPrice = json[options.speedType]
|
||||
|
||||
|
||||
Submodule contracts updated: 004d466a3d...908a481079
@@ -1,4 +1,4 @@
|
||||
FROM python:3.7-stretch
|
||||
FROM python:3.7
|
||||
RUN curl -fsSL https://get.docker.com | sh
|
||||
RUN pip3 install docker molecule==2.22rc1 molecule[docker] flake8
|
||||
RUN pip3 install docker molecule[docker,ansible] pytest pytest-testinfra flake8
|
||||
WORKDIR mono/deployment-e2e
|
||||
|
||||
@@ -3,12 +3,6 @@ dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: monitor-host
|
||||
groups:
|
||||
@@ -22,11 +16,6 @@ platforms:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ./converge.yml
|
||||
@@ -37,14 +26,11 @@ provisioner:
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: monitor
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
|
||||
@@ -3,12 +3,6 @@ dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: multiple-host
|
||||
groups:
|
||||
@@ -23,30 +17,22 @@ platforms:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../monitor/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
multiple-host:
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "6c48435bd464a53ed66ed62127c4dba8af75cf1a99a8ebe2680599948fbfbc6d"
|
||||
MONITOR_PORT: 3003
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: multiple
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
|
||||
@@ -3,18 +3,12 @@ dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
lint:
|
||||
name: yamllint
|
||||
enabled: True
|
||||
options:
|
||||
config-data:
|
||||
ignore: ../../hosts.yml
|
||||
platforms:
|
||||
- name: oracle-host
|
||||
groups:
|
||||
- example
|
||||
children:
|
||||
- oracle
|
||||
- oracle_swarm
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
@@ -22,29 +16,22 @@ platforms:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
lint:
|
||||
name: ansible-lint
|
||||
enabled: True
|
||||
options:
|
||||
r: ["bug"]
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../../../deployment/site.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-host:
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ORACLE_VALIDATOR_KEYSTORE_PATH: "../../../e2e-commons/keystore.json"
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD: "12345678"
|
||||
syslog_server_port: "udp://127.0.0.1:514"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
additional_files_or_dirs:
|
||||
- ../../tests/*
|
||||
scenario:
|
||||
name: oracle
|
||||
test_sequence:
|
||||
- lint
|
||||
- cleanup
|
||||
- destroy
|
||||
- dependency
|
||||
|
||||
@@ -3,22 +3,21 @@ import pytest
|
||||
import testinfra.utils.ansible_runner
|
||||
|
||||
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('oracle')
|
||||
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('oracle_swarm')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", [
|
||||
("oracle_rabbit_1"),
|
||||
("oracle_redis_1"),
|
||||
("oracle_bridge_request_1"),
|
||||
("oracle_bridge_collected_1"),
|
||||
("oracle_bridge_affirmation_1"),
|
||||
("oracle_bridge_senderhome_1"),
|
||||
("oracle_bridge_senderforeign_1"),
|
||||
("oracle_bridge_shutdown_1"),
|
||||
("oracle_rabbit"),
|
||||
("oracle_redis"),
|
||||
("oracle_bridge_request"),
|
||||
("oracle_bridge_collected"),
|
||||
("oracle_bridge_affirmation"),
|
||||
("oracle_bridge_senderhome"),
|
||||
("oracle_bridge_senderforeign"),
|
||||
("oracle_bridge_shutdown"),
|
||||
])
|
||||
def test_docker_containers(host, name):
|
||||
container = host.docker(name)
|
||||
assert container.is_running
|
||||
assert host.docker(name) is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("service", [
|
||||
|
||||
@@ -7,7 +7,7 @@ platforms:
|
||||
- ultimate
|
||||
- amb
|
||||
children:
|
||||
- oracle
|
||||
- oracle_swarm
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
@@ -21,12 +21,10 @@ provisioner:
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-amb-host:
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ORACLE_VALIDATOR_KEYSTORE_PATH: "../../../e2e-commons/keystore_ultimate.json"
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD: "12345678"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-amb
|
||||
test_sequence:
|
||||
|
||||
@@ -27,8 +27,6 @@ provisioner:
|
||||
ORACLE_FOREIGN_START_BLOCK: 1
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-erc-to-native
|
||||
test_sequence:
|
||||
|
||||
@@ -5,13 +5,13 @@ ORACLE_LOG_LEVEL: debug
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL: "https://sokol.poa.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x59ba90A588ce732AB33FD32Aab1b58c21400A0f6"
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
|
||||
COMMON_FOREIGN_RPC_URL: "https://kovan.infura.io/v3/5d7bd94c50ed43fab1cb8e74f58678b0"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0xdA4a49a00F4fF4A5988b9AceE95f99e3b2c208b6"
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000
|
||||
|
||||
## Home Gasprice
|
||||
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_PORT: 3003
|
||||
MONITOR_CACHE_EVENTS: "false"
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_HOME_START_BLOCK: 20821049
|
||||
MONITOR_FOREIGN_START_BLOCK: 24773297
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000
|
||||
MONITOR_TX_NUMBER_THRESHOLD: 100
|
||||
|
||||
@@ -7,6 +7,13 @@ sokol-kovan:
|
||||
ansible_user: ubuntu
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
oracle_swarm:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
ansible_user: ubuntu
|
||||
ORACLE_VALIDATOR_KEYSTORE_PATH: "/path/to/keystore.json"
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD: "12345678"
|
||||
#syslog_server_port: "udp://127.0.0.1:514"
|
||||
monitor:
|
||||
hosts:
|
||||
127.0.0.1:
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# pre-release because it contains "CI Fixes for ansible 2.8"
|
||||
molecule==2.22rc1
|
||||
docker
|
||||
flake8
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"live-restore": true,
|
||||
"live-restore": false,
|
||||
"no-new-privileges": true
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "0755"
|
||||
when: skip_compose is undefined
|
||||
|
||||
- name: Upgrade pip version
|
||||
shell: pip3 install --upgrade pip==19.3.1
|
||||
@@ -45,6 +46,9 @@
|
||||
group: docker
|
||||
createhome: yes
|
||||
|
||||
- name: reset ssh connection to allow user changes to affect ansible user
|
||||
meta: reset_connection
|
||||
|
||||
- name: Install auditd
|
||||
apt:
|
||||
name: auditd
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/var/log/docker/*/docker.log {
|
||||
rotate 5
|
||||
size 1G
|
||||
size 100M
|
||||
compress
|
||||
missingok
|
||||
delaycompress
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
/var/log/docker/*.log {
|
||||
rotate 5
|
||||
size 1G
|
||||
size 100M
|
||||
compress
|
||||
missingok
|
||||
delaycompress
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
loop_control:
|
||||
loop_var: file
|
||||
|
||||
- name: Set the local container logs configuration file
|
||||
- name: Set the oracle's containers local logs configuration file
|
||||
template:
|
||||
src: 31-oracle-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/31-oracle-docker.conf
|
||||
@@ -15,6 +15,22 @@
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the redis container local logs configuration file
|
||||
template:
|
||||
src: 32-redis-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/32-redis-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the rabbit MQ container local logs configuration file
|
||||
template:
|
||||
src: 33-rabbit-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/33-rabbit-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the log configuration file to send container logs to remote server
|
||||
template:
|
||||
src: 36-oracle-remote-logging.conf.j2
|
||||
|
||||
@@ -27,17 +27,6 @@
|
||||
set_fact:
|
||||
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
|
||||
set_fact: composefileoverride="-f docker-compose-transfer.yml"
|
||||
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"
|
||||
|
||||
@@ -11,9 +11,16 @@ ORACLE_HOME_RPC_POLLING_INTERVAL={{ ORACLE_HOME_RPC_POLLING_INTERVAL }}
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
{% if ORACLE_FOREIGN_ARCHIVE_RPC_URL | default('') != '' %}
|
||||
ORACLE_FOREIGN_ARCHIVE_RPC_URL={{ ORACLE_FOREIGN_ARCHIVE_RPC_URL }}
|
||||
{% endif %}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL={{ ORACLE_FOREIGN_RPC_POLLING_INTERVAL }}
|
||||
|
||||
{% if ORACLE_TX_REDUNDANCY | default('') != '' %}
|
||||
ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
|
||||
{% endif %}
|
||||
|
||||
## Gasprice
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
@@ -47,8 +54,28 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC={{ "yes" if ORACLE_ALLOW_HTTP_FOR_RPC else "no" }}
|
||||
ORACLE_QUEUE_URL={{ ORACLE_QUEUE_URL }}
|
||||
ORACLE_REDIS_URL={{ ORACLE_REDIS_URL }}
|
||||
{% if ORACLE_TX_REDUNDANCY | default('') != '' %}
|
||||
ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
|
||||
{% if ORACLE_FOREIGN_TX_RESEND_INTERVAL | default('') != '' %}
|
||||
ORACLE_FOREIGN_TX_RESEND_INTERVAL={{ ORACLE_FOREIGN_TX_RESEND_INTERVAL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_HOME_TX_RESEND_INTERVAL | default('') != '' %}
|
||||
ORACLE_HOME_TX_RESEND_INTERVAL={{ ORACLE_HOME_TX_RESEND_INTERVAL }}
|
||||
{% endif %}
|
||||
|
||||
## Emergency shutdown configuration
|
||||
{% if ORACLE_SHUTDOWN_SERVICE_URL | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_SERVICE_URL={{ ORACLE_SHUTDOWN_SERVICE_URL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL={{ ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SIDE_RPC_URL | default('') != '' %}
|
||||
ORACLE_SIDE_RPC_URL={{ ORACLE_SIDE_RPC_URL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SHUTDOWN_CONTRACT_ADDRESS | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_CONTRACT_ADDRESS={{ ORACLE_SHUTDOWN_CONTRACT_ADDRESS }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SHUTDOWN_CONTRACT_METHOD | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_CONTRACT_METHOD={{ ORACLE_SHUTDOWN_CONTRACT_METHOD }}
|
||||
{% endif %}
|
||||
|
||||
{% if ORACLE_HOME_START_BLOCK | default('') != '' %}
|
||||
|
||||
11
deployment/roles/oracle/templates/32-redis-docker.conf.j2
Normal file
11
deployment/roles/oracle/templates/32-redis-docker.conf.j2
Normal file
@@ -0,0 +1,11 @@
|
||||
$FileCreateMode 0644
|
||||
template(name="DockerLogFileName_Redis" type="list") {
|
||||
constant(value="/var/log/docker/")
|
||||
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*redis.*)\\/[a-zA-Z0-9]+\\[")
|
||||
constant(value="/docker.log")
|
||||
}
|
||||
|
||||
if $programname contains 'oracle' and $programname contains 'redis' then \
|
||||
?DockerLogFileName_Redis
|
||||
|
||||
$FileCreateMode 0600
|
||||
11
deployment/roles/oracle/templates/33-rabbit-docker.conf.j2
Normal file
11
deployment/roles/oracle/templates/33-rabbit-docker.conf.j2
Normal file
@@ -0,0 +1,11 @@
|
||||
$FileCreateMode 0644
|
||||
template(name="DockerLogFileName_Rabbit" type="list") {
|
||||
constant(value="/var/log/docker/")
|
||||
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*rabbit.*)\\/[a-zA-Z0-9]+\\[")
|
||||
constant(value="/docker.log")
|
||||
}
|
||||
|
||||
if $programname contains 'oracle' and $programname contains 'rabbit' then \
|
||||
?DockerLogFileName_Rabbit
|
||||
|
||||
$FileCreateMode 0600
|
||||
9
deployment/roles/oracle_swarm/defaults/main.yml
Normal file
9
deployment/roles/oracle_swarm/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
bridge_path: "/home/{{ compose_service_user }}/bridge"
|
||||
bridge_data_path: "/home/{{ compose_service_user }}/bridge_data"
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC: no
|
||||
ORACLE_QUEUE_URL: amqp://rabbit
|
||||
ORACLE_REDIS_URL: redis://redis
|
||||
keyfile_path: "/root/.key"
|
||||
keystore_path: "/root/.keystore.json"
|
||||
oracle_image: poanetwork/tokenbridge-oracle:latest
|
||||
3
deployment/roles/oracle_swarm/meta/main.yml
Normal file
3
deployment/roles/oracle_swarm/meta/main.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- { role: common, skip_repo: true, skip_compose: true }
|
||||
8
deployment/roles/oracle_swarm/tasks/jumpbox.yml
Normal file
8
deployment/roles/oracle_swarm/tasks/jumpbox.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
- name: Pull the containers images
|
||||
community.docker.docker_image:
|
||||
name: "{{ oracle_image }}"
|
||||
source: pull
|
||||
when: skip_pull is undefined
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
41
deployment/roles/oracle_swarm/tasks/logging.yml
Normal file
41
deployment/roles/oracle_swarm/tasks/logging.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
- name: Set the oracle's containers local logs configuration file
|
||||
template:
|
||||
src: 31-oracle-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/31-oracle-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the redis container local logs configuration file
|
||||
template:
|
||||
src: 32-redis-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/32-redis-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the rabbit MQ container local logs configuration file
|
||||
template:
|
||||
src: 33-rabbit-docker.conf.j2
|
||||
dest: /etc/rsyslog.d/33-rabbit-docker.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
- name: Set the log configuration file to send container logs to remote server
|
||||
template:
|
||||
src: 36-oracle-remote-logging.conf.j2
|
||||
dest: /etc/rsyslog.d/36-oracle-remote-logging.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
when: syslog_server_port is defined
|
||||
|
||||
- name: Discarding unwanted messages in rsyslog
|
||||
blockinfile:
|
||||
path: /etc/rsyslog.conf
|
||||
insertbefore: "# Where to place spool and state files"
|
||||
marker: "#{mark} add string to discarding unwanted messages"
|
||||
content: ':msg, contains, "ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY" ~'
|
||||
notify: restart rsyslog
|
||||
6
deployment/roles/oracle_swarm/tasks/main.yml
Normal file
6
deployment/roles/oracle_swarm/tasks/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- include_tasks: pre_config.yml
|
||||
- include_tasks: logging.yml
|
||||
- include_tasks: jumpbox.yml
|
||||
- include_tasks: post_config.yml
|
||||
- include_tasks: servinstall.yml
|
||||
46
deployment/roles/oracle_swarm/tasks/post_config.yml
Normal file
46
deployment/roles/oracle_swarm/tasks/post_config.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
- name: Get blocks
|
||||
become_user: "{{ compose_service_user }}"
|
||||
shell: docker run --env-file .env --rm {{ oracle_image }} scripts/getValidatorStartBlocks.js
|
||||
args:
|
||||
chdir: "{{ bridge_path }}/oracle"
|
||||
register: BLOCKS
|
||||
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
|
||||
|
||||
- name: Write blocks
|
||||
blockinfile:
|
||||
path: "{{ bridge_path }}/oracle/.env"
|
||||
marker: "## {mark} Calculated by scripts/getValidatorStartBlocks.js"
|
||||
block: |
|
||||
ORACLE_HOME_START_BLOCK={{ (BLOCKS.stdout | from_json).homeStartBlock }}
|
||||
ORACLE_FOREIGN_START_BLOCK={{ (BLOCKS.stdout | from_json).foreignStartBlock }}
|
||||
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
|
||||
|
||||
- name: Copy keystore file
|
||||
copy:
|
||||
src: "{{ ORACLE_VALIDATOR_KEYSTORE_PATH }}"
|
||||
dest: "{{ keystore_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
|
||||
- name: Create swarm secret
|
||||
community.docker.docker_secret:
|
||||
name: oracle_keystore
|
||||
state: present
|
||||
data_src: "{{ keystore_path }}"
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
- name: Remove unencrypted keystore file
|
||||
file:
|
||||
path: "{{ keystore_path }}"
|
||||
state: absent
|
||||
|
||||
- name: Install .key config
|
||||
template:
|
||||
src: key.j2
|
||||
dest: "{{ keyfile_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
47
deployment/roles/oracle_swarm/tasks/pre_config.yml
Normal file
47
deployment/roles/oracle_swarm/tasks/pre_config.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
- name: Init docker swarm
|
||||
community.docker.docker_swarm:
|
||||
state: present
|
||||
autolock_managers: yes
|
||||
listen_addr: 127.0.0.1:2377
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
- name: Get unlock token
|
||||
community.docker.docker_swarm_info:
|
||||
unlock_key: yes
|
||||
register: result
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
- name: Print unlock token
|
||||
debug:
|
||||
var: result.swarm_unlock_key
|
||||
|
||||
- name: Create oracle directory
|
||||
file:
|
||||
path: "{{ bridge_path }}/oracle"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create rabbitmq directory
|
||||
file:
|
||||
path: "{{ bridge_data_path }}/{{ item }}"
|
||||
state: directory
|
||||
mode: '0775'
|
||||
loop:
|
||||
- rabbitmq
|
||||
- redis
|
||||
|
||||
- name: Install .env config
|
||||
template:
|
||||
src: .env.j2
|
||||
dest: "{{ bridge_path }}/oracle/.env"
|
||||
owner: "{{ compose_service_user }}"
|
||||
mode: '0640'
|
||||
|
||||
- name: Install docker-compose file
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ bridge_path }}/oracle/docker-compose.yml"
|
||||
mode: '0755'
|
||||
19
deployment/roles/oracle_swarm/tasks/servinstall.yml
Normal file
19
deployment/roles/oracle_swarm/tasks/servinstall.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# This role creates a poabridge service which is designed to manage docker-compose bridge deployment.
|
||||
# /etc/init.d/poabridge start, status, stop, restart - does what the services usually do in such cases.
|
||||
---
|
||||
- name: "Set poabridge service"
|
||||
template:
|
||||
src: poabridge.j2
|
||||
dest: "/etc/init.d/poabridge"
|
||||
owner: root
|
||||
mode: 755
|
||||
|
||||
- name: "Enable the service"
|
||||
service:
|
||||
name: "poabridge"
|
||||
state: started
|
||||
enabled: yes
|
||||
use: service
|
||||
|
||||
- name: Start the service
|
||||
shell: service poabridge start
|
||||
86
deployment/roles/oracle_swarm/templates/.env.j2
Normal file
86
deployment/roles/oracle_swarm/templates/.env.j2
Normal file
@@ -0,0 +1,86 @@
|
||||
## General settings
|
||||
ORACLE_BRIDGE_MODE={{ ORACLE_BRIDGE_MODE }}
|
||||
{% if ORACLE_LOG_LEVEL | default('') != '' %}
|
||||
ORACLE_LOG_LEVEL={{ ORACLE_LOG_LEVEL }}
|
||||
{% endif %}
|
||||
|
||||
## Home contract
|
||||
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
|
||||
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
|
||||
ORACLE_HOME_RPC_POLLING_INTERVAL={{ ORACLE_HOME_RPC_POLLING_INTERVAL }}
|
||||
|
||||
## Foreign contract
|
||||
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
|
||||
{% if ORACLE_FOREIGN_ARCHIVE_RPC_URL | default('') != '' %}
|
||||
ORACLE_FOREIGN_ARCHIVE_RPC_URL={{ ORACLE_FOREIGN_ARCHIVE_RPC_URL }}
|
||||
{% endif %}
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
|
||||
ORACLE_FOREIGN_RPC_POLLING_INTERVAL={{ ORACLE_FOREIGN_RPC_POLLING_INTERVAL }}
|
||||
|
||||
{% if ORACLE_TX_REDUNDANCY | default('') != '' %}
|
||||
ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
|
||||
{% endif %}
|
||||
|
||||
## Gasprice
|
||||
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_HOME_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
|
||||
{% if ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL | default('') != '' %}
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL={{ ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL }}
|
||||
{% endif %}
|
||||
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
|
||||
{% endif %}
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
|
||||
{% endif %}
|
||||
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
|
||||
{% if ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | default('') != '' %}
|
||||
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL={{ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL }}
|
||||
{% endif %}
|
||||
{% if COMMON_FOREIGN_GAS_PRICE_FACTOR | default('') != '' %}
|
||||
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
|
||||
{% endif %}
|
||||
|
||||
## Transport configuration
|
||||
ORACLE_ALLOW_HTTP_FOR_RPC={{ "yes" if ORACLE_ALLOW_HTTP_FOR_RPC else "no" }}
|
||||
ORACLE_QUEUE_URL={{ ORACLE_QUEUE_URL }}
|
||||
ORACLE_REDIS_URL={{ ORACLE_REDIS_URL }}
|
||||
{% if ORACLE_FOREIGN_TX_RESEND_INTERVAL | default('') != '' %}
|
||||
ORACLE_FOREIGN_TX_RESEND_INTERVAL={{ ORACLE_FOREIGN_TX_RESEND_INTERVAL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_HOME_TX_RESEND_INTERVAL | default('') != '' %}
|
||||
ORACLE_HOME_TX_RESEND_INTERVAL={{ ORACLE_HOME_TX_RESEND_INTERVAL }}
|
||||
{% endif %}
|
||||
|
||||
## Emergency shutdown configuration
|
||||
{% if ORACLE_SHUTDOWN_SERVICE_URL | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_SERVICE_URL={{ ORACLE_SHUTDOWN_SERVICE_URL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL={{ ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SIDE_RPC_URL | default('') != '' %}
|
||||
ORACLE_SIDE_RPC_URL={{ ORACLE_SIDE_RPC_URL }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SHUTDOWN_CONTRACT_ADDRESS | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_CONTRACT_ADDRESS={{ ORACLE_SHUTDOWN_CONTRACT_ADDRESS }}
|
||||
{% endif %}
|
||||
{% if ORACLE_SHUTDOWN_CONTRACT_METHOD | default('') != '' %}
|
||||
ORACLE_SHUTDOWN_CONTRACT_METHOD={{ ORACLE_SHUTDOWN_CONTRACT_METHOD }}
|
||||
{% endif %}
|
||||
|
||||
{% if ORACLE_HOME_START_BLOCK | default('') != '' %}
|
||||
ORACLE_HOME_START_BLOCK={{ ORACLE_HOME_START_BLOCK }}
|
||||
{% endif %}
|
||||
{% if ORACLE_FOREIGN_START_BLOCK | default('') != '' %}
|
||||
ORACLE_FOREIGN_START_BLOCK={{ ORACLE_FOREIGN_START_BLOCK }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,11 @@
|
||||
$FileCreateMode 0644
|
||||
template(name="DockerLogFileName_Oracle" type="list") {
|
||||
constant(value="/var/log/docker/")
|
||||
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="bridge_(.*)\\/[a-zA-Z0-9]+\\[")
|
||||
constant(value="/docker.log")
|
||||
}
|
||||
|
||||
if $programname startswith 'oracle_bridge_' then \
|
||||
?DockerLogFileName_Oracle
|
||||
|
||||
$FileCreateMode 0600
|
||||
@@ -0,0 +1,11 @@
|
||||
$FileCreateMode 0644
|
||||
template(name="DockerLogFileName_Redis" type="list") {
|
||||
constant(value="/var/log/docker/")
|
||||
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*redis.*)\\/[a-zA-Z0-9]+\\[")
|
||||
constant(value="/docker.log")
|
||||
}
|
||||
|
||||
if $programname contains 'oracle' and $programname contains 'redis' then \
|
||||
?DockerLogFileName_Redis
|
||||
|
||||
$FileCreateMode 0600
|
||||
@@ -0,0 +1,11 @@
|
||||
$FileCreateMode 0644
|
||||
template(name="DockerLogFileName_Rabbit" type="list") {
|
||||
constant(value="/var/log/docker/")
|
||||
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*rabbit.*)\\/[a-zA-Z0-9]+\\[")
|
||||
constant(value="/docker.log")
|
||||
}
|
||||
|
||||
if $programname contains 'oracle' and $programname contains 'rabbit' then \
|
||||
?DockerLogFileName_Rabbit
|
||||
|
||||
$FileCreateMode 0600
|
||||
@@ -0,0 +1,15 @@
|
||||
if $programname startswith 'oracle_bridge_' then {
|
||||
action(
|
||||
type="omfwd"
|
||||
protocol="{{ syslog_server_port.split(":")[0] }}"
|
||||
target="{{ (syslog_server_port.split(":")[1])[2:] }}"
|
||||
port="{{ syslog_server_port.split(":")[2] }}"
|
||||
template="RemoteForwardFormat"
|
||||
queue.SpoolDirectory="/var/spool/rsyslog"
|
||||
queue.FileName="remote"
|
||||
queue.MaxDiskSpace="1g"
|
||||
queue.SaveOnShutdown="on"
|
||||
queue.Type="LinkedList"
|
||||
ResendLastMSGOnReconnect="on"
|
||||
)
|
||||
}
|
||||
144
deployment/roles/oracle_swarm/templates/docker-compose.yml.j2
Normal file
144
deployment/roles/oracle_swarm/templates/docker-compose.yml.j2
Normal file
@@ -0,0 +1,144 @@
|
||||
version: '3.9'
|
||||
x-deploy: &x-deploy
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.3'
|
||||
memory: 500M
|
||||
reservations:
|
||||
memory: 100M
|
||||
x-keystore-access: &x-keystore-access
|
||||
environment:
|
||||
ORACLE_VALIDATOR_KEYSTORE_PATH: /run/secrets/oracle_keystore
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD:
|
||||
secrets:
|
||||
- oracle_keystore
|
||||
x-logging: &x-logging
|
||||
driver: 'syslog'
|
||||
options: {tag: '{{ '{{.Name}}/{{.ID}}' }}' }
|
||||
services:
|
||||
rabbit:
|
||||
image: rabbitmq:3
|
||||
hostname: rabbit
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
environment: [ 'RABBITMQ_NODENAME=node@rabbit' ]
|
||||
networks:
|
||||
- net_rabbit_bridge_request
|
||||
- net_rabbit_bridge_collected
|
||||
- net_rabbit_bridge_affirmation
|
||||
- net_rabbit_bridge_senderhome
|
||||
- net_rabbit_bridge_senderforeign
|
||||
volumes: [ '{{ bridge_data_path }}/rabbitmq:/var/lib/rabbitmq/mnesia' ]
|
||||
redis:
|
||||
image: redis:4
|
||||
hostname: redis
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
command: [ redis-server, --appendonly, 'yes' ]
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_db_bridge_collected
|
||||
- net_db_bridge_affirmation
|
||||
- net_db_bridge_senderhome
|
||||
- net_db_bridge_senderforeign
|
||||
- net_db_bridge_shutdown
|
||||
volumes: [ '{{ bridge_data_path }}/redis:/data' ]
|
||||
bridge_request:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
env_file: ./.env
|
||||
<<: *x-keystore-access
|
||||
entrypoint: yarn watcher:signature-request
|
||||
networks:
|
||||
- net_db_bridge_request
|
||||
- net_rabbit_bridge_request
|
||||
bridge_collected:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
env_file: ./.env
|
||||
entrypoint: yarn watcher:collected-signatures
|
||||
networks:
|
||||
- net_db_bridge_collected
|
||||
- net_rabbit_bridge_collected
|
||||
bridge_affirmation:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
env_file: ./.env
|
||||
entrypoint: yarn watcher:affirmation-request
|
||||
networks:
|
||||
- net_db_bridge_affirmation
|
||||
- net_rabbit_bridge_affirmation
|
||||
bridge_senderhome:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
env_file: ./.env
|
||||
<<: *x-keystore-access
|
||||
entrypoint: yarn sender:home
|
||||
networks:
|
||||
- net_db_bridge_senderhome
|
||||
- net_rabbit_bridge_senderhome
|
||||
bridge_senderforeign:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
env_file: ./.env
|
||||
<<: *x-keystore-access
|
||||
entrypoint: yarn sender:foreign
|
||||
networks:
|
||||
- net_db_bridge_senderforeign
|
||||
- net_rabbit_bridge_senderforeign
|
||||
bridge_shutdown:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
env_file: ./.env
|
||||
entrypoint: yarn manager:shutdown
|
||||
networks:
|
||||
- net_db_bridge_shutdown
|
||||
{% if ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" %}
|
||||
bridge_transfer:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
env_file: ./.env
|
||||
entrypoint: yarn watcher:transfer
|
||||
networks:
|
||||
- net_db_bridge_transfer
|
||||
- net_rabbit_bridge_transfer
|
||||
{% endif %}
|
||||
{% if ORACLE_BRIDGE_MODE == "ARBITRARY_MESSAGE" %}
|
||||
bridge_information:
|
||||
image: {{ oracle_image }}
|
||||
deploy: *x-deploy
|
||||
logging: *x-logging
|
||||
env_file: ./.env
|
||||
entrypoint: yarn watcher:information-request
|
||||
networks:
|
||||
- net_db_bridge_information
|
||||
- net_rabbit_bridge_information
|
||||
{% endif %}
|
||||
networks:
|
||||
net_db_bridge_request:
|
||||
net_db_bridge_collected:
|
||||
net_db_bridge_affirmation:
|
||||
net_db_bridge_senderhome:
|
||||
net_db_bridge_senderforeign:
|
||||
net_db_bridge_shutdown:
|
||||
{% if ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE" %}
|
||||
net_db_bridge_transfer:
|
||||
net_rabbit_bridge_transfer:
|
||||
{% endif %}
|
||||
{% if ORACLE_BRIDGE_MODE == "ARBITRARY_MESSAGE" %}
|
||||
net_db_bridge_information:
|
||||
net_rabbit_bridge_information:
|
||||
{% endif %}
|
||||
net_rabbit_bridge_request:
|
||||
net_rabbit_bridge_collected:
|
||||
net_rabbit_bridge_affirmation:
|
||||
net_rabbit_bridge_senderhome:
|
||||
net_rabbit_bridge_senderforeign:
|
||||
|
||||
secrets:
|
||||
oracle_keystore:
|
||||
external: true
|
||||
2
deployment/roles/oracle_swarm/templates/key.j2
Normal file
2
deployment/roles/oracle_swarm/templates/key.j2
Normal file
@@ -0,0 +1,2 @@
|
||||
## Validator-specific options
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD={{ ORACLE_VALIDATOR_KEYSTORE_PASSWORD }}
|
||||
66
deployment/roles/oracle_swarm/templates/poabridge.j2
Normal file
66
deployment/roles/oracle_swarm/templates/poabridge.j2
Normal file
@@ -0,0 +1,66 @@
|
||||
#! /bin/bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: poabridge
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start daemon at boot time
|
||||
# Description: Enable service provided by daemon.
|
||||
### END INIT INFO
|
||||
|
||||
WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridge_path + '/oracle' if bridge_path[:1] != "/" else bridge_path + '/oracle' }}"
|
||||
|
||||
#Getting path to private key file and variable name for parsing key file
|
||||
source {{ keyfile_path }}
|
||||
|
||||
start(){
|
||||
echo "Starting bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" docker stack rm oracle
|
||||
sudo -u "{{ compose_service_user }}" "ORACLE_VALIDATOR_KEYSTORE_PASSWORD=$ORACLE_VALIDATOR_KEYSTORE_PASSWORD" docker stack deploy oracle -c docker-compose.yml
|
||||
}
|
||||
|
||||
stop(){
|
||||
echo "Stopping bridge.."
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" docker stack rm oracle
|
||||
sleep 2
|
||||
}
|
||||
|
||||
status(){
|
||||
echo "Bridge status:"
|
||||
cd $WORKDIR
|
||||
sudo -u "{{ compose_service_user }}" docker service ls
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
|
||||
start)
|
||||
start
|
||||
;;
|
||||
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
|
||||
status)
|
||||
status
|
||||
;;
|
||||
|
||||
restart)
|
||||
echo "Restarting bridge.."
|
||||
stop
|
||||
start
|
||||
;;
|
||||
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -4,6 +4,11 @@
|
||||
become: true
|
||||
roles:
|
||||
- { role: oracle }
|
||||
- name: Install Oracle as a Docker Swarm service
|
||||
hosts: oracle_swarm
|
||||
become: true
|
||||
roles:
|
||||
- { role: oracle_swarm }
|
||||
- name: Install Monitor
|
||||
hosts: monitor
|
||||
become: true
|
||||
|
||||
@@ -6,14 +6,12 @@ COMMON_HOME_RPC_URL=http://parity1:8545
|
||||
COMMON_FOREIGN_RPC_URL=http://parity2:8545
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0x8397be90BCF57b0B71219f555Fe121b22e5a994C
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E
|
||||
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=
|
||||
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=1000000000
|
||||
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
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_FALLBACK=10000000000
|
||||
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_HOME_BRIDGE_ADDRESS=0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c
|
||||
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_SPEED_TYPE=standard
|
||||
COMMON_HOME_GAS_PRICE_FALLBACK=1
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"ercToNativeBridge": {
|
||||
"home": "0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c",
|
||||
"foreign": "0x32198D570fffC7033641F8A9094FFDCaAEF42624",
|
||||
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
|
||||
"foreignToken": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"monitor": "http://monitor-erc20-native:3012/bridge"
|
||||
},
|
||||
"amb": {
|
||||
@@ -49,6 +49,12 @@
|
||||
"blockedHomeBox": "0xF9698Eb93702dfdd0e2d802088d4c21822a8A977",
|
||||
"monitor": "http://monitor-amb:3013/bridge"
|
||||
},
|
||||
"amb2": {
|
||||
"home": "0x5A42E119990c3F3A80Fea20aAF4c3Ff4DB240Cc9",
|
||||
"foreign": "0x897527391ad3837604973d78D3514f44c36AB9FC",
|
||||
"homeBox": "0xb008E9076fCbDB2C3AF84225Bc07Eb35B2bE5ECD",
|
||||
"foreignBox": "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127"
|
||||
},
|
||||
"homeRPC": {
|
||||
"URL": "http://parity1:8545",
|
||||
"ID": "77"
|
||||
|
||||
@@ -32,7 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
BLOCK_REWARD_ADDRESS=0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B
|
||||
ERC20_TOKEN_ADDRESS=0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9
|
||||
ERC20_TOKEN_ADDRESS=0x6B175474E89094C44Da98b954EedeAC495271d0F
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
|
||||
@@ -1,7 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
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
|
||||
docker-compose down
|
||||
|
||||
1
e2e-commons/keystore.json
Normal file
1
e2e-commons/keystore.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"version":3,"id":"e7e64a1b-5e61-4c17-a473-963d2bbb59e5","address":"d138a69eb2da1c3518e792737c820b23cce62e4b","crypto":{"ciphertext":"f6ddf0b2638fb9fd5777de2aa07937b5ee9bc17acc74c8e6e6580e2dfd0d3de6","cipherparams":{"iv":"bcdbc5af4582887e5cdcf264e8d5b80d"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"f9e621918990e64e278e0fb8cf0343219e1cceaea8547d50fae452ad8f42f231","n":8192,"r":8,"p":1},"mac":"34149cd0b3ddea52588825d403fb75cfb8b864b616d455f75f2de001cc2601ed"}}]
|
||||
@@ -35,3 +35,9 @@ echo -e "\n\n############ Deploying one more test contract for amb ############\
|
||||
cd "$DEPLOY_PATH"
|
||||
node src/utils/deployTestBox.js
|
||||
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
|
||||
|
||||
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 $4 rabbit
|
||||
docker-compose $1 run -d --name $2 redis
|
||||
docker-compose $1 run -d --name $3 rabbit
|
||||
|
||||
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 $2 $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 $2 $db_env -d oracle-erc20-native yarn watcher:transfer
|
||||
docker-compose $1 run $oraclePK $db_env -d oracle-erc20-native yarn watcher:signature-request
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-erc20-native yarn watcher:collected-signatures
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-erc20-native yarn watcher:affirmation-request
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-erc20-native yarn watcher:transfer
|
||||
fi
|
||||
if [[ -z "$MODE" || "$MODE" == amb ]]; then
|
||||
docker-compose $1 run $2 $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 $2 $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 $oraclePK $db_env -d oracle-amb yarn watcher:signature-request
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:collected-signatures
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:affirmation-request
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn watcher:information-request
|
||||
fi
|
||||
|
||||
docker-compose $1 run $2 $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 $2 $db_env -d oracle-amb yarn manager:shutdown
|
||||
docker-compose $1 run $oraclePK $db_env -d oracle-amb yarn sender:home
|
||||
docker-compose $1 run $oraclePK $db_env -d oracle-amb yarn sender:foreign
|
||||
docker-compose $1 run $oracleAddr $db_env -d oracle-amb yarn manager:shutdown
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
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
|
||||
|
||||
if [ "$1" == "oracle-validator-2" ]; then
|
||||
oracle2Values="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4 -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
|
||||
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4"
|
||||
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=5a5c3645d0f04e9eb4f27f94ed4c244a225587405b8838e7456f7781ce3a9513"
|
||||
startValidator "-p validator2" redis2 rabbit2
|
||||
fi
|
||||
|
||||
if [ "$1" == "oracle-validator-3" ]; then
|
||||
oracle3Values="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
|
||||
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d"
|
||||
oraclePK="-e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=f877f62a1c19f852cff1d29f0fb1ecac18821c0080d4cc0520c60c098293dca1"
|
||||
startValidator "-p validator3" redis3 rabbit3
|
||||
fi
|
||||
|
||||
if [ "$1" == "alm" ]; then
|
||||
@@ -84,13 +88,38 @@ while [ "$1" != "" ]; do
|
||||
if [ "$1" == "alm-e2e" ]; then
|
||||
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"
|
||||
startValidator "-p validator2" "$oracle2Values" redis2 rabbit2
|
||||
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xdCC784657C78054aa61FbcFFd2605F32374816A4"
|
||||
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"
|
||||
startValidator "-p validator3" "$oracle3Values" redis3 rabbit3
|
||||
oracleAddr="-e ORACLE_VALIDATOR_ADDRESS=0xDcef88209a20D52165230104B245803C3269454d"
|
||||
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
|
||||
|
||||
shift # Shift all the parameters down by one
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "mocha --timeout 120000",
|
||||
"start": "mocha --timeout 120000 --exit",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore"
|
||||
},
|
||||
"author": "",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
require('dotenv').config()
|
||||
const logger = require('./logger')('detectMediators.js')
|
||||
const { isHomeContract, isForeignContract } = require('./utils/web3Cache')
|
||||
const eventsInfo = require('./utils/events')
|
||||
const { getHomeTxSender, getForeignTxSender } = require('./utils/web3Cache')
|
||||
const { addExecutionStatus } = require('./utils/message')
|
||||
const { normalizeAMBMessageEvent } = require('../commons')
|
||||
const { getHomeTxSender, getForeignTxSender, isHomeContract, isForeignContract } = require('./utils/web3Cache')
|
||||
const { addExecutionStatus, addRetrievalStatus } = require('./utils/message')
|
||||
const { normalizeAMBMessageEvent, normalizeAMBInfoRequest } = require('../commons')
|
||||
|
||||
function countInteractions(requests) {
|
||||
const stats = {}
|
||||
@@ -30,6 +29,41 @@ function countInteractions(requests) {
|
||||
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 => ({
|
||||
...normalizeAMBMessageEvent(event),
|
||||
txHash: event.transactionHash,
|
||||
@@ -88,10 +122,13 @@ async function main(mode) {
|
||||
homeToForeignRequests,
|
||||
foreignToHomeRequests,
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations
|
||||
foreignToHomeConfirmations,
|
||||
informationRequests,
|
||||
informationResponses
|
||||
} = await eventsInfo(mode)
|
||||
const homeToForeign = homeToForeignRequests.map(normalize).map(addExecutionStatus(homeToForeignConfirmations))
|
||||
const foreignToHome = foreignToHomeRequests.map(normalize).map(addExecutionStatus(foreignToHomeConfirmations))
|
||||
const infoRequests = informationRequests.map(normalizeAMBInfoRequest).map(addRetrievalStatus(informationResponses))
|
||||
|
||||
for (const event of homeToForeign) {
|
||||
// AMB contract emits a single UserRequestForSignature event for every home->foreign request.
|
||||
@@ -146,6 +183,7 @@ async function main(mode) {
|
||||
floatingMediators,
|
||||
remotelyControlledMediators,
|
||||
unknown,
|
||||
informationReceivers: countInfoRequests(infoRequests),
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,29 +46,19 @@ async function main(bridgeMode, eventsInfo) {
|
||||
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
const erc20Address = await foreignBridge.methods.erc20token().call()
|
||||
const erc20Contract = new web3Foreign.eth.Contract(ERC20_ABI, erc20Address)
|
||||
let investedAmountInDai = 0
|
||||
let bridgeDsrBalance = 0
|
||||
let displayChaiToken = false
|
||||
|
||||
try {
|
||||
logger.debug('calling foreignBridge.methods.isChaiTokenEnabled')
|
||||
if (await foreignBridge.methods.isChaiTokenEnabled().call()) {
|
||||
displayChaiToken = true
|
||||
logger.debug('calling foreignBridge.methods.investedAmountInDai')
|
||||
investedAmountInDai = await foreignBridge.methods.investedAmountInDai().call()
|
||||
logger.debug('calling foreignBridge.methods.dsrBalance')
|
||||
bridgeDsrBalance = await foreignBridge.methods.dsrBalance().call()
|
||||
} else {
|
||||
logger.debug('Chai token is currently disabled')
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Methods for chai token are not present')
|
||||
}
|
||||
|
||||
logger.debug('calling erc20Contract.methods.balanceOf')
|
||||
const foreignErc20Balance = await erc20Contract.methods
|
||||
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
.call({}, foreignDelayedBlockNumber)
|
||||
let foreignErc20BalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue)
|
||||
try {
|
||||
logger.debug('calling foreignBridge.methods.investedAmount')
|
||||
const invested = await foreignBridge.methods.investedAmount(erc20Address).call({}, foreignDelayedBlockNumber)
|
||||
foreignErc20BalanceBN = foreignErc20BalanceBN.plus(invested)
|
||||
} catch (_) {
|
||||
logger.debug('compounding related methods are not present in the foreign bridge')
|
||||
}
|
||||
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
logger.debug('calling homeBridge.methods.blockRewardContract')
|
||||
@@ -84,30 +74,16 @@ async function main(bridgeMode, eventsInfo) {
|
||||
const mintedCoinsBN = new BN(mintedCoins)
|
||||
const burntCoinsBN = new BN(burntCoins)
|
||||
const totalSupplyBN = mintedCoinsBN.minus(burntCoinsBN)
|
||||
const foreignErc20BalanceBN = new BN(foreignErc20Balance).plus(lateForeignConfirmationsTotalValue)
|
||||
const investedAmountInDaiBN = new BN(investedAmountInDai)
|
||||
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
|
||||
|
||||
const diff = foreignErc20BalanceBN
|
||||
.plus(investedAmountInDaiBN)
|
||||
.minus(totalSupplyBN)
|
||||
.toFixed()
|
||||
|
||||
const foreign = {
|
||||
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
|
||||
}
|
||||
|
||||
if (displayChaiToken) {
|
||||
foreign.investedErc20Balance = Web3Utils.fromWei(investedAmountInDai)
|
||||
foreign.accumulatedInterest = Web3Utils.fromWei(bridgeDsrBalanceBN.minus(investedAmountInDaiBN).toString(10))
|
||||
}
|
||||
|
||||
const diff = foreignErc20BalanceBN.minus(totalSupplyBN).toFixed()
|
||||
logger.debug('Done')
|
||||
return {
|
||||
home: {
|
||||
totalSupply: Web3Utils.fromWei(totalSupplyBN.toFixed())
|
||||
},
|
||||
foreign,
|
||||
foreign: {
|
||||
erc20Balance: Web3Utils.fromWei(foreignErc20Balance)
|
||||
},
|
||||
balanceDiff: Number(Web3Utils.fromWei(diff)),
|
||||
...blockRanges,
|
||||
lastChecked: Math.floor(Date.now() / 1000)
|
||||
|
||||
@@ -21,7 +21,9 @@ async function main(bridgeMode, eventsInfo) {
|
||||
homeToForeignConfirmations,
|
||||
homeToForeignRequests,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests
|
||||
foreignToHomeRequests,
|
||||
informationRequests,
|
||||
informationResponses
|
||||
} = eventsInfo
|
||||
|
||||
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
@@ -34,7 +36,9 @@ async function main(bridgeMode, eventsInfo) {
|
||||
fromForeignToHomeDiff: foreignToHomeConfirmations.length - foreignToHomeRequests.length,
|
||||
home: {
|
||||
toForeign: homeToForeignRequests.length,
|
||||
fromForeign: foreignToHomeConfirmations.length
|
||||
fromForeign: foreignToHomeConfirmations.length,
|
||||
informationRequests: informationRequests.length,
|
||||
informationResponses: informationResponses.length
|
||||
},
|
||||
foreign: {
|
||||
fromHome: homeToForeignConfirmations.length,
|
||||
|
||||
@@ -11,7 +11,6 @@ const {
|
||||
OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI
|
||||
} = require('../../commons')
|
||||
const { normalizeEventInformation } = require('./message')
|
||||
const { filterTransferBeforeES } = require('./tokenUtils')
|
||||
const { writeFile, readCacheFile } = require('./file')
|
||||
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./web3')
|
||||
const { getPastEvents } = require('./web3Cache')
|
||||
@@ -129,6 +128,27 @@ async function main(mode) {
|
||||
})).map(normalizeEvent)
|
||||
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) {
|
||||
logger.debug("calling erc20Contract.getPastEvents('Transfer')")
|
||||
let transferEvents = (await getPastEvents(erc20Contract, {
|
||||
@@ -139,80 +159,32 @@ async function main(mode) {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
},
|
||||
chain: 'foreign'
|
||||
}))
|
||||
.map(normalizeEvent)
|
||||
.filter(e => e.recipient !== ZERO_ADDRESS) // filter mint operation during SCD-to-MCD swaps
|
||||
.filter(e => e.recipient.toLowerCase() !== '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643') // filter cDai withdraws during compounding
|
||||
|
||||
// Get transfer events for each previously used Sai token
|
||||
const saiTokenAddress = '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'
|
||||
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, saiTokenAddress)
|
||||
logger.debug('Half duplex token:', saiTokenAddress)
|
||||
logger.debug("calling halfDuplexTokenContract.getPastEvents('Transfer')")
|
||||
// https://etherscan.io/tx/0xd0c3c92c94e05bc71256055ce8c4c993e047f04e04f3283a04e4cb077b71f6c6
|
||||
const blockNumberHalfDuplexDisabled = 9884448
|
||||
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: Math.min(blockNumberHalfDuplexDisabled, foreignDelayedBlockNumber),
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
},
|
||||
chain: 'foreign'
|
||||
})).map(normalizeEvent)
|
||||
|
||||
let directTransfers = transferEvents
|
||||
const tokensSwappedAbiExists = FOREIGN_ABI.filter(e => e.type === 'event' && e.name === 'TokensSwapped')[0]
|
||||
if (tokensSwappedAbiExists) {
|
||||
logger.debug('collecting half duplex tokens participated in the bridge balance')
|
||||
logger.debug("calling foreignBridge.getPastEvents('TokensSwapped')")
|
||||
const tokensSwappedEvents = await getPastEvents(foreignBridge, {
|
||||
event: 'TokensSwapped',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignBlockNumber,
|
||||
chain: 'foreign',
|
||||
safeToBlock: foreignDelayedBlockNumber
|
||||
})
|
||||
|
||||
// Get token swap events emitted by foreign bridge
|
||||
const bridgeTokensSwappedEvents = tokensSwappedEvents.filter(e => e.address === COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
|
||||
// Get transfer events for each previous erc20
|
||||
const uniqueTokenAddressesSet = new Set(bridgeTokensSwappedEvents.map(e => e.returnValues.from))
|
||||
|
||||
// Exclude chai token from previous erc20
|
||||
try {
|
||||
logger.debug('calling foreignBridge.chaiToken() to remove it from half duplex tokens list')
|
||||
const chaiToken = await foreignBridge.methods.chaiToken().call()
|
||||
uniqueTokenAddressesSet.delete(chaiToken)
|
||||
} catch (e) {
|
||||
logger.debug('call to foreignBridge.chaiToken() failed')
|
||||
}
|
||||
// Exclude dai token from previous erc20
|
||||
try {
|
||||
logger.debug('calling foreignBridge.erc20token() to remove it from half duplex tokens list')
|
||||
const daiToken = await foreignBridge.methods.erc20token().call()
|
||||
uniqueTokenAddressesSet.delete(daiToken)
|
||||
} catch (e) {
|
||||
logger.debug('call to foreignBridge.erc20token() failed')
|
||||
}
|
||||
|
||||
const uniqueTokenAddresses = [...uniqueTokenAddressesSet]
|
||||
await Promise.all(
|
||||
uniqueTokenAddresses.map(async tokenAddress => {
|
||||
const halfDuplexTokenContract = new web3Foreign.eth.Contract(ERC20_ABI, tokenAddress)
|
||||
|
||||
logger.debug('Half duplex token:', tokenAddress)
|
||||
logger.debug("calling halfDuplexTokenContract.getPastEvents('Transfer')")
|
||||
const halfDuplexTransferEvents = (await getPastEvents(halfDuplexTokenContract, {
|
||||
event: 'Transfer',
|
||||
fromBlock: MONITOR_FOREIGN_START_BLOCK,
|
||||
toBlock: foreignDelayedBlockNumber,
|
||||
options: {
|
||||
filter: { to: COMMON_FOREIGN_BRIDGE_ADDRESS }
|
||||
},
|
||||
chain: 'foreign'
|
||||
})).map(normalizeEvent)
|
||||
|
||||
// Remove events after the ES
|
||||
logger.debug('filtering half duplex transfers happened before ES')
|
||||
const validHalfDuplexTransfers = await filterTransferBeforeES(halfDuplexTransferEvents)
|
||||
|
||||
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
|
||||
})
|
||||
)
|
||||
|
||||
// filter transfer that is part of a token swap
|
||||
directTransfers = transferEvents.filter(
|
||||
e =>
|
||||
bridgeTokensSwappedEvents.findIndex(
|
||||
t => t.transactionHash === e.referenceTx && e.recipient === ZERO_ADDRESS
|
||||
) === -1
|
||||
)
|
||||
}
|
||||
transferEvents = [...halfDuplexTransferEvents, ...transferEvents]
|
||||
|
||||
// Get transfer events that didn't have a UserRequestForAffirmation event in the same transaction
|
||||
directTransfers = directTransfers.filter(
|
||||
const directTransfers = transferEvents.filter(
|
||||
e => foreignToHomeRequests.findIndex(t => t.referenceTx === e.referenceTx) === -1
|
||||
)
|
||||
|
||||
@@ -225,6 +197,8 @@ async function main(mode) {
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations,
|
||||
foreignToHomeRequests,
|
||||
informationRequests,
|
||||
informationResponses,
|
||||
isExternalErc20,
|
||||
bridgeMode,
|
||||
homeBlockNumber,
|
||||
|
||||
@@ -8,8 +8,8 @@ function readFile(filePath, parseJson = true) {
|
||||
const json = JSON.parse(content)
|
||||
const timeDiff = Math.floor(Date.now() / 1000) - json.lastChecked
|
||||
return Object.assign({}, json, { timeDiff })
|
||||
} catch (e) {
|
||||
console.error('readFlle', e)
|
||||
} catch (_) {
|
||||
console.error(`File ${filePath} does not exist`)
|
||||
return {
|
||||
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
|
||||
* @param {Object} event
|
||||
@@ -89,6 +104,7 @@ module.exports = {
|
||||
deliveredMsgNotProcessed,
|
||||
processedMsgNotDelivered,
|
||||
addExecutionStatus,
|
||||
addRetrievalStatus,
|
||||
normalizeEventInformation,
|
||||
eventWithoutReference,
|
||||
unclaimedHomeToForeignRequests,
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// https://etherscan.io/tx/0xd0c3c92c94e05bc71256055ce8c4c993e047f04e04f3283a04e4cb077b71f6c6
|
||||
const blockNumberHalfDuplexDisabled = 9884448
|
||||
|
||||
/**
|
||||
* Returns true if the event was before the bridge stopped supporting half duplex transfers.
|
||||
*/
|
||||
async function transferBeforeES(event) {
|
||||
return event.blockNumber < blockNumberHalfDuplexDisabled
|
||||
}
|
||||
|
||||
async function filterTransferBeforeES(array) {
|
||||
const newArray = []
|
||||
// Iterate events from newer to older
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const beforeES = await transferBeforeES(array[i])
|
||||
if (beforeES) {
|
||||
// add element to first position so the new array will have the same order
|
||||
newArray.unshift(array[i])
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterTransferBeforeES,
|
||||
blockNumberHalfDuplexDisabled
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
require('dotenv').config()
|
||||
const Web3Utils = require('web3').utils
|
||||
const fetch = require('node-fetch')
|
||||
const logger = require('./logger')('validators')
|
||||
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromSupplier } = require('../commons')
|
||||
const { web3Home, web3Foreign, getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')
|
||||
@@ -81,7 +80,7 @@ async function main(bridgeMode) {
|
||||
if (MONITOR_VALIDATOR_HOME_TX_LIMIT) {
|
||||
logger.debug('calling home getGasPrices')
|
||||
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)
|
||||
homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
|
||||
homeTxCost = homeGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_HOME_TX_LIMIT))
|
||||
@@ -93,13 +92,9 @@ async function main(bridgeMode) {
|
||||
|
||||
if (MONITOR_VALIDATOR_FOREIGN_TX_LIMIT) {
|
||||
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 =
|
||||
(await gasPriceFromSupplier(fetchFn, foreignGasPriceSupplierOpts)) ||
|
||||
(await gasPriceFromSupplier(COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL, foreignGasPriceSupplierOpts)) ||
|
||||
Web3Utils.toBN(COMMON_FOREIGN_GAS_PRICE_FALLBACK)
|
||||
foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
|
||||
foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(MONITOR_VALIDATOR_FOREIGN_TX_LIMIT))
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "mocha --exit",
|
||||
"start": "mocha",
|
||||
"generate-amb-tx": "node ./scripts/generate-amb-tx.js",
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"amb": "mocha test/amb.js",
|
||||
"erc-to-native": "mocha test/ercToNative.js",
|
||||
|
||||
@@ -10,7 +10,7 @@ case "$mode" in
|
||||
;;
|
||||
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
|
||||
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()
|
||||
@@ -1,8 +1,9 @@
|
||||
const Web3 = require('web3')
|
||||
const assert = require('assert')
|
||||
const { ASYNC_CALL_ERRORS } = require('../../oracle/src/utils/constants')
|
||||
const { user, homeRPC, foreignRPC, amb, validator } = require('../../e2e-commons/constants.json')
|
||||
const { 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 { toBN } = Web3.utils
|
||||
@@ -26,27 +27,49 @@ const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox, opts)
|
||||
const homeBridge = new homeWeb3.eth.Contract(HOME_AMB_ABI, amb.home, opts)
|
||||
const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, amb.foreign, opts)
|
||||
|
||||
function validateBlock(web3, serialized, block) {
|
||||
assert.strictEqual(serialized.length, 2 + 64 * 12)
|
||||
const values = web3.eth.abi.decodeParameter(
|
||||
'(uint256,bytes32,address,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256)',
|
||||
serialized
|
||||
)
|
||||
assert.strictEqual(values[0], block.number.toString(), 'wrong block number returned')
|
||||
assert.strictEqual(values[1], block.hash, 'wrong block hash returned')
|
||||
assert.strictEqual(values[2], block.miner, 'wrong block miner returned')
|
||||
assert.strictEqual(values[3], block.gasUsed.toString(), 'wrong block gasUsed returned')
|
||||
assert.strictEqual(values[4], block.gasLimit.toString(), 'wrong block gasLimit returned')
|
||||
assert.strictEqual(values[5], block.parentHash, 'wrong block parentHash returned')
|
||||
assert.strictEqual(values[6], block.receiptsRoot, 'wrong block receiptsRoot returned')
|
||||
assert.strictEqual(values[7], block.stateRoot, 'wrong block stateRoot returned')
|
||||
assert.strictEqual(values[8], block.transactionsRoot, 'wrong block transactionsRoot returned')
|
||||
assert.strictEqual(values[9], block.timestamp.toString(), 'wrong block timestamp returned')
|
||||
assert.strictEqual(values[10], block.difficulty, 'wrong block difficulty returned')
|
||||
assert.strictEqual(values[11], block.totalDifficulty, 'wrong block totalDifficulty returned')
|
||||
}
|
||||
|
||||
function validateTransaction(web3, serialized, tx) {
|
||||
assert.strictEqual(serialized.length, 64 * 13 + tx.input.length + 56)
|
||||
const values = web3.eth.abi.decodeParameter(
|
||||
'(bytes32,uint256,bytes32,uint256,address,address,uint256,uint256,uint256,uint256,bytes)',
|
||||
serialized
|
||||
)
|
||||
assert.strictEqual(values[0], tx.hash, 'wrong txHash returned')
|
||||
assert.strictEqual(values[1], tx.blockNumber.toString(), 'wrong tx blockNumber returned')
|
||||
assert.strictEqual(values[2], tx.blockHash.toString(), 'wrong tx blockHash returned')
|
||||
assert.strictEqual(values[3], tx.transactionIndex.toString(), 'wrong tx transactionIndex returned')
|
||||
assert.strictEqual(values[4], tx.from, 'wrong tx from returned')
|
||||
assert.strictEqual(values[5], tx.to, 'wrong tx to returned')
|
||||
assert.strictEqual(values[6], tx.value, 'wrong tx value returned')
|
||||
assert.strictEqual(values[7], tx.nonce.toString(), 'wrong tx nonce returned')
|
||||
assert.strictEqual(values[8], tx.gas.toString(), 'wrong tx gas returned')
|
||||
assert.strictEqual(values[9], tx.gasPrice, 'wrong tx gasPrice returned')
|
||||
assert.strictEqual(values[10], tx.input, 'wrong tx data returned')
|
||||
}
|
||||
|
||||
describe('arbitrary message bridging', () => {
|
||||
let requiredSignatures = 1
|
||||
before(async () => {
|
||||
const allowedMethods = [
|
||||
'eth_call(address,bytes)',
|
||||
'eth_call(address,bytes,uint256)',
|
||||
'eth_call(address,address,uint256,bytes)',
|
||||
'eth_blockNumber()',
|
||||
'eth_getBlockByNumber()',
|
||||
'eth_getBlockByNumber(uint256)',
|
||||
'eth_getBlockByHash(bytes32)',
|
||||
'eth_getBalance(address)',
|
||||
'eth_getBalance(address,uint256)',
|
||||
'eth_getTransactionCount(address)',
|
||||
'eth_getTransactionCount(address,uint256)',
|
||||
'eth_getTransactionByHash(bytes32)',
|
||||
'eth_getTransactionReceipt(bytes32)',
|
||||
'eth_getStorageAt(address,bytes32)',
|
||||
'eth_getStorageAt(address,bytes32,uint256)'
|
||||
]
|
||||
for (const method of allowedMethods) {
|
||||
for (const method of ambInformationSignatures) {
|
||||
const selector = homeWeb3.utils.soliditySha3(method)
|
||||
await homeBridge.methods.enableAsyncRequestSelector(selector, true).send({ from: validator.address })
|
||||
}
|
||||
@@ -80,6 +103,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('Subsidized Mode', () => {
|
||||
it('should bridge message', async () => {
|
||||
@@ -88,12 +124,13 @@ describe('arbitrary message bridging', () => {
|
||||
const initialValue = await foreignBox.methods.value().call()
|
||||
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)
|
||||
.send()
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
console.log(res.transactionHash)
|
||||
|
||||
// check that value changed and balance decreased
|
||||
await uniformRetry(async retry => {
|
||||
@@ -186,12 +223,13 @@ describe('arbitrary message bridging', () => {
|
||||
const initialValue = await homeBox.methods.value().call()
|
||||
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)
|
||||
.send()
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
console.log(res.transactionHash)
|
||||
|
||||
// check that value changed and balance decreased
|
||||
await uniformRetry(async retry => {
|
||||
@@ -266,7 +304,7 @@ describe('arbitrary message bridging', () => {
|
||||
await makeAsyncCall(selector, data2)
|
||||
|
||||
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||
assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
|
||||
assert.strictEqual(await homeBox.methods.data().call(), ASYNC_CALL_ERRORS.REVERT, 'returned data is incorrect')
|
||||
|
||||
const data3 = homeWeb3.eth.abi.encodeParameters(
|
||||
['address', 'address', 'uint256', 'bytes'],
|
||||
@@ -276,7 +314,7 @@ describe('arbitrary message bridging', () => {
|
||||
await makeAsyncCall(selector, data3)
|
||||
|
||||
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||
assert.strictEqual(await homeBox.methods.data().call(), null, 'returned data is incorrect')
|
||||
assert.strictEqual(await homeBox.methods.data().call(), ASYNC_CALL_ERRORS.REVERT, 'returned data is incorrect')
|
||||
})
|
||||
|
||||
it('should make async eth_call for specific block', async () => {
|
||||
@@ -285,7 +323,7 @@ describe('arbitrary message bridging', () => {
|
||||
const selector = homeWeb3.utils.soliditySha3('eth_call(address,bytes,uint256)')
|
||||
const data1 = homeWeb3.eth.abi.encodeParameters(
|
||||
['address', 'bytes', 'uint256'],
|
||||
[amb.foreignBox, foreignBox.methods.value().encodeABI(), 60]
|
||||
[amb.foreignBox, foreignBox.methods.value().encodeABI(), 25]
|
||||
)
|
||||
const data2 = homeWeb3.eth.abi.encodeParameters(
|
||||
['address', 'bytes', 'uint256'],
|
||||
@@ -317,6 +355,11 @@ describe('arbitrary message bridging', () => {
|
||||
await makeAsyncCall(selector, data3)
|
||||
|
||||
assert(!(await homeBox.methods.status().call()), 'status is true')
|
||||
assert.strictEqual(
|
||||
await homeBox.methods.data().call(),
|
||||
ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE,
|
||||
'returned data is incorrect'
|
||||
)
|
||||
})
|
||||
|
||||
it('should make async eth_blockNumber', async () => {
|
||||
@@ -336,15 +379,8 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
assert(await homeBox.methods.status().call(), 'status is false')
|
||||
const data = await homeBox.methods.data().call()
|
||||
assert.strictEqual(data.length, 2 + 64 * 3)
|
||||
const { 0: number, 1: hash, 2: miner } = homeWeb3.eth.abi.decodeParameters(
|
||||
['uint256', 'bytes32', 'address'],
|
||||
data
|
||||
)
|
||||
const block = await foreignWeb3.eth.getBlock(blockNumber)
|
||||
assert.strictEqual(number, blockNumber, 'wrong block number returned')
|
||||
assert.strictEqual(hash, block.hash, 'wrong block hash returned')
|
||||
assert.strictEqual(miner, block.miner, 'wrong block miner returned')
|
||||
validateBlock(homeWeb3, data, block)
|
||||
})
|
||||
|
||||
it('should make async eth_getBlockByNumber and return latest block', async () => {
|
||||
@@ -354,7 +390,7 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
assert(await homeBox.methods.status().call(), 'status is false')
|
||||
const data = await homeBox.methods.data().call()
|
||||
assert.strictEqual(data.length, 2 + 64 * 3)
|
||||
assert.strictEqual(data.length, 2 + 64 * 12)
|
||||
})
|
||||
|
||||
it('should make async eth_getBlockByHash', async () => {
|
||||
@@ -366,16 +402,7 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
assert(await homeBox.methods.status().call(), 'status is false')
|
||||
const data = await homeBox.methods.data().call()
|
||||
assert.strictEqual(data.length, 2 + 64 * 3)
|
||||
|
||||
const { 0: number, 1: hash, 2: miner } = homeWeb3.eth.abi.decodeParameters(
|
||||
['uint256', 'bytes32', 'address'],
|
||||
data
|
||||
)
|
||||
|
||||
assert.strictEqual(number, blockNumber, 'wrong block number returned')
|
||||
assert.strictEqual(hash, block.hash, 'wrong block hash returned')
|
||||
assert.strictEqual(miner, block.miner, 'wrong block miner returned')
|
||||
validateBlock(homeWeb3, data, block)
|
||||
})
|
||||
|
||||
it('should make async eth_getBalance', async () => {
|
||||
@@ -463,7 +490,7 @@ describe('arbitrary message bridging', () => {
|
||||
})
|
||||
|
||||
it('should make async eth_getTransactionByHash', async () => {
|
||||
const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
||||
const txHash = '0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd'
|
||||
const tx = await foreignWeb3.eth.getTransaction(txHash)
|
||||
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionByHash(bytes32)')
|
||||
|
||||
@@ -471,32 +498,11 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
assert(await homeBox.methods.status().call(), 'status is false')
|
||||
const data = await homeBox.methods.data().call()
|
||||
const dataTypes = [
|
||||
'bytes32',
|
||||
'uint256',
|
||||
'address',
|
||||
'address',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'bytes'
|
||||
]
|
||||
const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
|
||||
|
||||
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
|
||||
assert.strictEqual(values[1], tx.blockNumber.toString(), 'wrong tx blockNumber returned')
|
||||
assert.strictEqual(values[2], tx.from, 'wrong tx from returned')
|
||||
assert.strictEqual(values[3], tx.to, 'wrong tx to returned')
|
||||
assert.strictEqual(values[4], tx.value, 'wrong tx value returned')
|
||||
assert.strictEqual(values[5], tx.nonce.toString(), 'wrong tx nonce returned')
|
||||
assert.strictEqual(values[6], tx.gas.toString(), 'wrong tx gas returned')
|
||||
assert.strictEqual(values[7], tx.gasPrice, 'wrong tx gasPrice returned')
|
||||
assert.strictEqual(values[8], tx.input, 'wrong tx data returned')
|
||||
validateTransaction(homeWeb3, data, tx)
|
||||
})
|
||||
|
||||
it('should make async eth_getTransactionReceipt', async () => {
|
||||
const txHash = '0x09dfb947dbd17e27bcc117773b6e133829f7cef9646199a93ef019c4f7c0fec6'
|
||||
const txHash = '0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd'
|
||||
const receipt = await foreignWeb3.eth.getTransactionReceipt(txHash)
|
||||
const selector = homeWeb3.utils.soliditySha3('eth_getTransactionReceipt(bytes32)')
|
||||
|
||||
@@ -504,18 +510,25 @@ describe('arbitrary message bridging', () => {
|
||||
|
||||
assert(await homeBox.methods.status().call(), 'status is false')
|
||||
const data = await homeBox.methods.data().call()
|
||||
const dataTypes = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
|
||||
const values = homeWeb3.eth.abi.decodeParameters(dataTypes, data)
|
||||
const values = homeWeb3.eth.abi.decodeParameter(
|
||||
'(bytes32,uint256,bytes32,uint256,address,address,uint256,bool,(address,bytes32[],bytes)[])',
|
||||
data
|
||||
)
|
||||
|
||||
assert.strictEqual(values[0], txHash, 'wrong txHash returned')
|
||||
assert.strictEqual(values[1], receipt.blockNumber.toString(), 'wrong tx blockNumber returned')
|
||||
assert.strictEqual(values[2], receipt.status, 'wrong tx status returned')
|
||||
assert.strictEqual(values[3].length, 1, 'wrong logs length returned')
|
||||
assert.strictEqual(values[3][0][0], receipt.logs[0].address, 'wrong log address returned')
|
||||
assert.strictEqual(values[3][0][1].length, 2, 'wrong log topics length returned')
|
||||
assert.strictEqual(values[3][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
|
||||
assert.strictEqual(values[3][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
|
||||
assert.strictEqual(values[3][0][2], receipt.logs[0].data, 'wrong log data returned')
|
||||
assert.strictEqual(values[2], receipt.blockHash, 'wrong tx blockHash returned')
|
||||
assert.strictEqual(values[3], receipt.transactionIndex.toString(), 'wrong tx transactionIndex returned')
|
||||
assert.strictEqual(values[4].toLowerCase(), receipt.from, 'wrong tx from returned')
|
||||
assert.strictEqual(values[5].toLowerCase(), receipt.to, 'wrong tx to returned')
|
||||
assert.strictEqual(values[6], receipt.gasUsed.toString(), 'wrong gasUsed to returned')
|
||||
assert.strictEqual(values[7], receipt.status, 'wrong tx status returned')
|
||||
assert.strictEqual(values[8].length, 1, 'wrong logs length returned')
|
||||
assert.strictEqual(values[8][0][0], receipt.logs[0].address, 'wrong log address returned')
|
||||
assert.strictEqual(values[8][0][1].length, 2, 'wrong log topics length returned')
|
||||
assert.strictEqual(values[8][0][1][0], receipt.logs[0].topics[0], 'wrong event signature returned')
|
||||
assert.strictEqual(values[8][0][1][1], receipt.logs[0].topics[1], 'wrong message id returned')
|
||||
assert.strictEqual(values[8][0][2], receipt.logs[0].data, 'wrong log data returned')
|
||||
})
|
||||
|
||||
it('should make async eth_getStorageAt', async () => {
|
||||
|
||||
@@ -33,6 +33,10 @@ const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME
|
||||
|
||||
describe('erc to native', () => {
|
||||
before(async () => {
|
||||
console.log('Initializing interest')
|
||||
await foreignBridge.methods
|
||||
.initializeInterest(ercToNativeBridge.foreignToken, 1, 1, validator.address)
|
||||
.send({ from: validator.address, gas: '4000000' })
|
||||
if (process.env.ULTIMATE === 'true') {
|
||||
return
|
||||
}
|
||||
@@ -100,6 +104,8 @@ describe('erc to native', () => {
|
||||
|
||||
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
|
||||
await erc20Token.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
@@ -110,6 +116,7 @@ describe('erc to native', () => {
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
await foreignBridge.methods.investDai().send({ from: validator.address, gas: '4000000' })
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
|
||||
@@ -1 +1 @@
|
||||
--timeout 120000
|
||||
--timeout 120000 --exit
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
...baseConfig,
|
||||
main: baseConfig.foreign,
|
||||
event: 'UserRequestForAffirmation',
|
||||
sender: 'home',
|
||||
queue: 'home-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
|
||||
@@ -8,12 +8,15 @@ const {
|
||||
FOREIGN_AMB_ABI
|
||||
} = require('../../commons')
|
||||
const { web3Home, web3Foreign } = require('../src/services/web3')
|
||||
const { privateKeyToAddress } = require('../src/utils/utils')
|
||||
const { add0xPrefix, privateKeyToAddress, loadKeystore } = require('../src/utils/utils')
|
||||
const { EXIT_CODES } = require('../src/utils/constants')
|
||||
|
||||
const {
|
||||
ORACLE_BRIDGE_MODE,
|
||||
ORACLE_VALIDATOR_ADDRESS,
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
||||
ORACLE_VALIDATOR_KEYSTORE_PATH,
|
||||
ORACLE_VALIDATOR_KEYSTORE_PASSWORD,
|
||||
ORACLE_MAX_PROCESSING_TIME,
|
||||
COMMON_HOME_BRIDGE_ADDRESS,
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
@@ -79,9 +82,33 @@ const foreignConfig = {
|
||||
const maxProcessingTime =
|
||||
parseInt(ORACLE_MAX_PROCESSING_TIME, 10) || 4 * Math.max(homeConfig.pollingInterval, foreignConfig.pollingInterval)
|
||||
|
||||
let validatorPrivateKey
|
||||
let validatorAddress = ORACLE_VALIDATOR_ADDRESS
|
||||
if (ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY) {
|
||||
validatorPrivateKey = add0xPrefix(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY)
|
||||
const derived = privateKeyToAddress(validatorPrivateKey)
|
||||
if (ORACLE_VALIDATOR_ADDRESS && derived.toLowerCase() !== ORACLE_VALIDATOR_ADDRESS.toLowerCase()) {
|
||||
console.error(
|
||||
`Derived address from private key - ${derived} is different from ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}`
|
||||
)
|
||||
process.exit(EXIT_CODES.INCOMPATIBILITY)
|
||||
}
|
||||
validatorAddress = derived
|
||||
} else if (ORACLE_VALIDATOR_KEYSTORE_PATH) {
|
||||
try {
|
||||
const keystore = loadKeystore(ORACLE_VALIDATOR_KEYSTORE_PATH, ORACLE_VALIDATOR_KEYSTORE_PASSWORD)
|
||||
validatorPrivateKey = keystore.privateKey
|
||||
validatorAddress = keystore.address
|
||||
} catch (e) {
|
||||
console.error(`Can't load keystore file: ${e.message}`)
|
||||
process.exit(EXIT_CODES.INCOMPATIBILITY)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
eventFilter: {},
|
||||
validatorAddress: ORACLE_VALIDATOR_ADDRESS || privateKeyToAddress(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY),
|
||||
validatorPrivateKey,
|
||||
validatorAddress,
|
||||
maxProcessingTime,
|
||||
shutdownKey: 'oracle-shutdown',
|
||||
home: homeConfig,
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
...baseConfig,
|
||||
main: baseConfig.home,
|
||||
event: 'CollectedSignatures',
|
||||
sender: 'foreign',
|
||||
queue: 'foreign-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
|
||||
@@ -8,6 +8,7 @@ module.exports = {
|
||||
web3ForeignArchive: web3ForeignArchive || baseConfig.foreign.web3,
|
||||
main: baseConfig.home,
|
||||
event: 'UserRequestForInformation',
|
||||
sender: 'home',
|
||||
queue: 'home-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
...baseConfig,
|
||||
main: baseConfig.home,
|
||||
event: 'UserRequestForSignature',
|
||||
sender: 'home',
|
||||
queue: 'home-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
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 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`
|
||||
|
||||
if (baseConfig.id !== 'erc-native') {
|
||||
@@ -22,14 +9,15 @@ if (baseConfig.id !== 'erc-native') {
|
||||
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 = {
|
||||
...baseConfig,
|
||||
main: {
|
||||
...baseConfig.foreign,
|
||||
eventContract: new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, initialChecks.bridgeableTokenAddress)
|
||||
},
|
||||
main: baseConfig.foreign,
|
||||
event: 'Transfer',
|
||||
eventFilter: { to: process.env.COMMON_FOREIGN_BRIDGE_ADDRESS },
|
||||
eventFilter: { to: baseConfig.foreign.bridgeAddress },
|
||||
sender: 'home',
|
||||
queue: 'home-prioritized',
|
||||
name: `watcher-${id}`,
|
||||
id
|
||||
|
||||
15
oracle/docker-compose-helpers.yml
Normal file
15
oracle/docker-compose-helpers.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
interestFetcher:
|
||||
cpus: 0.1
|
||||
mem_limit: 500m
|
||||
image: poanetwork/tokenbridge-oracle:latest
|
||||
env_file: ./.env
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
INTEREST_FETCHER_PRIVATE_KEY: ${INTEREST_FETCHER_PRIVATE_KEY}
|
||||
INTEREST_FETCH_CONTRACT_ADDRESS: '0xCd152c7Bd4189Ddee97EaBb783FC5cD93CF2D230'
|
||||
INTERVAL: 300000
|
||||
restart: unless-stopped
|
||||
entrypoint: yarn helper:interestFether
|
||||
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,7 +13,12 @@
|
||||
"sender:home": "./scripts/start-worker.sh sender home-sender",
|
||||
"sender:foreign": "./scripts/start-worker.sh sender foreign-sender",
|
||||
"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",
|
||||
"helper:interestFether": "node ./scripts/interestFetcher.js",
|
||||
"dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'",
|
||||
"test": "NODE_ENV=test mocha",
|
||||
"test:watch": "NODE_ENV=test mocha --watch --reporter=min",
|
||||
|
||||
@@ -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
|
||||
65
oracle/scripts/interestFetcher.js
Normal file
65
oracle/scripts/interestFetcher.js
Normal file
@@ -0,0 +1,65 @@
|
||||
require('../env')
|
||||
const { isAddress } = require('web3').utils
|
||||
const { privateKeyToAddress, setIntervalAndRun } = require('../src/utils/utils')
|
||||
const { EXIT_CODES } = require('../src/utils/constants')
|
||||
const { web3Home } = require('../src/services/web3')
|
||||
const { sendTx } = require('../src/tx/sendTx')
|
||||
|
||||
const privateKey = process.env.INTEREST_FETCHER_PRIVATE_KEY
|
||||
const interval = process.env.INTERVAL ? parseInt(process.env.INTERVAL, 10) : 3600 * 1000
|
||||
const contractAddress = process.env.INTEREST_FETCH_CONTRACT_ADDRESS
|
||||
|
||||
if (!privateKey) {
|
||||
console.error('Environment variable INTEREST_FETCHER_PRIVATE_KEY is not set')
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
if (interval < 300 * 1000) {
|
||||
console.error('Interval is to small, should be at least 5 minutes')
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
if (!isAddress(contractAddress)) {
|
||||
console.error('Invalid contract address provided', contractAddress)
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
const gasPrice = process.env.COMMON_HOME_GAS_PRICE_FALLBACK || '1000000000'
|
||||
|
||||
async function main() {
|
||||
// assuming that we are using this contract - https://github.com/omni/interest-fetcher-contract
|
||||
const contract = new web3Home.eth.Contract([{ name: 'fetchInterest', type: 'function', inputs: [] }], contractAddress)
|
||||
const chainId = await web3Home.eth.getChainId()
|
||||
const data = contract.methods.fetchInterest().encodeABI()
|
||||
const senderAddress = privateKeyToAddress(privateKey)
|
||||
console.log(
|
||||
`Initialized, chainId=${chainId}, data=${data}, contract=${contractAddress}, interval=${interval / 1000}s`
|
||||
)
|
||||
|
||||
await setIntervalAndRun(async () => {
|
||||
let gasLimit
|
||||
try {
|
||||
gasLimit = await contract.methods.fetchInterest().estimateGas()
|
||||
} catch (e) {
|
||||
console.log('Gas limit estimation failed, will retry later', new Date())
|
||||
return
|
||||
}
|
||||
|
||||
const nonce = await web3Home.eth.getTransactionCount(senderAddress)
|
||||
|
||||
const txHash = await sendTx({
|
||||
privateKey,
|
||||
to: contractAddress,
|
||||
data,
|
||||
nonce,
|
||||
gasPrice,
|
||||
gasLimit: Math.round(gasLimit * 1.5),
|
||||
amount: '0',
|
||||
chainId,
|
||||
web3: web3Home
|
||||
})
|
||||
console.log('Sent transaction with fetch interest', txHash, new Date())
|
||||
}, interval)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -8,12 +8,10 @@ LOGS_DIR="logs/"
|
||||
WORKER="${WORKERS_DIR}${1}.js"
|
||||
CONFIG="${2}.config.js"
|
||||
LOG="${LOGS_DIR}${2}.txt"
|
||||
TX_HASH="${3}"
|
||||
|
||||
CHECKS=$(node scripts/initialChecks.js)
|
||||
TX_HASH=${@:3}
|
||||
|
||||
if [ "${NODE_ENV}" = "production" ]; then
|
||||
exec node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH"
|
||||
exec node "${WORKER}" "${CONFIG}" $TX_HASH
|
||||
else
|
||||
node "${WORKER}" "${CONFIG}" "$CHECKS" "$TX_HASH" | tee -a "${LOG}" | pino-pretty
|
||||
node "${WORKER}" "${CONFIG}" $TX_HASH | tee -a "${LOG}" | pino-pretty
|
||||
fi
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
require('../env')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { isAttached, connectWatcherToQueue, connection } = require('./services/amqpClient')
|
||||
const logger = require('./services/logger')
|
||||
const GasPrice = require('./services/gasPrice')
|
||||
const { getNonce, getChainId, getEventsFromTx } = require('./tx/web3')
|
||||
const { sendTx } = require('./tx/sendTx')
|
||||
const { getTokensState } = require('./utils/tokenState')
|
||||
const { checkHTTPS, watchdog, syncForEach, addExtraGas } = require('./utils/utils')
|
||||
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')
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR)
|
||||
}
|
||||
|
||||
const config = require(path.join('../config/', process.argv[2]))
|
||||
const txHash = process.argv[4]
|
||||
const { web3, eventContract, chain, bridgeContract } = 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 processCollectedSignatures = require('./events/processCollectedSignatures')(config)
|
||||
@@ -27,15 +44,13 @@ const processAMBCollectedSignatures = require('./events/processAMBCollectedSigna
|
||||
const processAMBAffirmationRequests = require('./events/processAMBAffirmationRequests')(config)
|
||||
const processAMBInformationRequests = require('./events/processAMBInformationRequests')(config)
|
||||
|
||||
const { web3, eventContract } = config.main
|
||||
|
||||
let attached
|
||||
|
||||
async function initialize() {
|
||||
try {
|
||||
const checkHttps = checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger)
|
||||
|
||||
web3.currentProvider.urls.forEach(checkHttps(config.chain))
|
||||
web3.currentProvider.urls.forEach(checkHttps(chain))
|
||||
|
||||
attached = await isAttached()
|
||||
if (attached) {
|
||||
@@ -59,12 +74,12 @@ async function runMain({ sendToQueue }) {
|
||||
const sendJob = attached ? sendToQueue : sendJobTx
|
||||
if (!attached || connection.isConnected()) {
|
||||
if (config.maxProcessingTime) {
|
||||
await watchdog(() => main({ sendJob, txHash }), config.maxProcessingTime, () => {
|
||||
await watchdog(() => main({ sendJob, txHashes }), config.maxProcessingTime, () => {
|
||||
logger.fatal('Max processing time reached')
|
||||
process.exit(EXIT_CODES.MAX_TIME_REACHED)
|
||||
})
|
||||
} else {
|
||||
await main({ sendJob, txHash })
|
||||
await main({ sendJob, txHashes })
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
@@ -99,27 +114,37 @@ function processEvents(events) {
|
||||
}
|
||||
}
|
||||
|
||||
async function main({ sendJob, txHash }) {
|
||||
try {
|
||||
const events = await getEventsFromTx({
|
||||
web3,
|
||||
contract: eventContract,
|
||||
event: config.event,
|
||||
txHash,
|
||||
filter: config.eventFilter
|
||||
})
|
||||
logger.info(`Found ${events.length} ${config.event} events`)
|
||||
async function main({ sendJob, txHashes }) {
|
||||
if (config.id === 'erc-native-transfer') {
|
||||
logger.debug('Getting token address to listen Transfer events')
|
||||
const state = await getTokensState(bridgeContract, logger)
|
||||
eventContract.options.address = state.bridgeableTokenAddress
|
||||
}
|
||||
|
||||
if (events.length) {
|
||||
const job = await processEvents(events)
|
||||
logger.info('Transactions to send:', job.length)
|
||||
logger.info(`Processing ${txHashes.length} input transactions`)
|
||||
for (const txHash of txHashes) {
|
||||
try {
|
||||
logger.info({ txHash }, `Processing transaction`)
|
||||
const events = await getEventsFromTx({
|
||||
web3,
|
||||
contract: eventContract,
|
||||
event: config.event,
|
||||
txHash,
|
||||
filter: config.eventFilter
|
||||
})
|
||||
logger.info({ txHash }, `Found ${events.length} ${config.event} events`)
|
||||
|
||||
if (job.length) {
|
||||
await sendJob(job)
|
||||
if (events.length) {
|
||||
const job = await processEvents(events)
|
||||
logger.info({ txHash }, 'Transactions to send:', job.length)
|
||||
|
||||
if (job.length) {
|
||||
await sendJob(job)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
await connection.close()
|
||||
@@ -128,9 +153,13 @@ async function main({ sendJob, txHash }) {
|
||||
}
|
||||
|
||||
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)
|
||||
let nonce = await getNonce(web3, ORACLE_VALIDATOR_ADDRESS)
|
||||
let nonce = await getNonce(web3, config.validatorAddress)
|
||||
|
||||
await syncForEach(jobs, async job => {
|
||||
let gasLimit
|
||||
@@ -145,10 +174,10 @@ async function sendJobTx(jobs) {
|
||||
const txHash = await sendTx({
|
||||
data: job.data,
|
||||
nonce,
|
||||
gasPrice: gasPrice.toString(10),
|
||||
gasPrice,
|
||||
amount: '0',
|
||||
gasLimit,
|
||||
privateKey: ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
|
||||
privateKey: config.validatorPrivateKey,
|
||||
to: job.to,
|
||||
chainId,
|
||||
web3
|
||||
@@ -167,7 +196,7 @@ async function sendJobTx(jobs) {
|
||||
)
|
||||
|
||||
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)
|
||||
logger.error(
|
||||
`Insufficient funds: ${currentBalance}. Stop processing messages until the balance is at least ${minimumBalance}.`
|
||||
|
||||
@@ -15,7 +15,7 @@ async function estimateGas({ web3, homeBridge, validatorContract, message, addre
|
||||
const gasEstimate = await homeBridge.methods.executeAffirmation(message).estimateGas({
|
||||
from: address
|
||||
})
|
||||
const msgGasLimit = parseAMBHeader(message).gasLimit
|
||||
const msgGasLimit = Math.ceil((parseAMBHeader(message).gasLimit * 64) / 63)
|
||||
// message length in bytes
|
||||
const len = strip0x(message).length / 2 - MIN_AMB_HEADER_LENGTH
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { toBN } = require('web3').utils
|
||||
|
||||
const { zipToObject } = require('../../../utils/utils')
|
||||
const { ASYNC_CALL_ERRORS, ASYNC_ETH_CALL_MAX_GAS_LIMIT } = require('../../../utils/constants')
|
||||
const { zipToObject, isRevertError } = require('../../../utils/utils')
|
||||
|
||||
const argTypes = {
|
||||
to: 'address',
|
||||
@@ -17,14 +18,23 @@ function makeCall(argNames) {
|
||||
const { blockNumber, ...opts } = zipToObject(argNames, args)
|
||||
|
||||
if (blockNumber && toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||
}
|
||||
|
||||
const [status, result] = await web3.eth
|
||||
.call(opts, blockNumber || foreignBlock.number)
|
||||
.then(result => [true, result], err => [false, err.data])
|
||||
// different clients might use different default gas limits, so it makes sense to limit it by some large number
|
||||
if (!opts.gas || toBN(opts.gas).gt(toBN(ASYNC_ETH_CALL_MAX_GAS_LIMIT))) {
|
||||
opts.gas = ASYNC_ETH_CALL_MAX_GAS_LIMIT
|
||||
}
|
||||
|
||||
return [status, web3.eth.abi.encodeParameter('bytes', result)]
|
||||
return web3.eth
|
||||
.call(opts, blockNumber || foreignBlock.number)
|
||||
.then(result => [true, web3.eth.abi.encodeParameter('bytes', result)])
|
||||
.catch(e => {
|
||||
if (isRevertError(e)) {
|
||||
return [false, ASYNC_CALL_ERRORS.REVERT]
|
||||
}
|
||||
throw e
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const { toBN } = require('web3').utils
|
||||
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
const address = web3.eth.abi.decodeParameter('address', data)
|
||||
|
||||
@@ -12,7 +14,7 @@ async function callArchive(web3, data, foreignBlock) {
|
||||
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
||||
|
||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||
}
|
||||
|
||||
const balance = await web3.eth.getBalance(address, blockNumber)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
const { serializeBlock } = require('./serializers')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
@@ -6,7 +7,7 @@ async function call(web3, data, foreignBlock) {
|
||||
const block = await web3.eth.getBlock(blockHash)
|
||||
|
||||
if (block === null || block.number > foreignBlock.number) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.NOT_FOUND]
|
||||
}
|
||||
|
||||
return [true, serializeBlock(web3, block)]
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
const { toBN } = require('web3').utils
|
||||
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
const { serializeBlock } = require('./serializers')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
const blockNumber = web3.eth.abi.decodeParameter('uint256', data)
|
||||
|
||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||
}
|
||||
|
||||
const block = await web3.eth.getBlock(blockNumber)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const { toBN } = require('web3').utils
|
||||
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data)
|
||||
|
||||
@@ -12,7 +14,7 @@ async function callArchive(web3, data, foreignBlock) {
|
||||
const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data)
|
||||
|
||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||
}
|
||||
|
||||
const value = await web3.eth.getStorageAt(address, slot, blockNumber)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
const { serializeTx } = require('./serializers')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
@@ -6,7 +7,7 @@ async function call(web3, data, foreignBlock) {
|
||||
const tx = await web3.eth.getTransaction(hash)
|
||||
|
||||
if (tx === null || tx.blockNumber > foreignBlock.number) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.NOT_FOUND]
|
||||
}
|
||||
|
||||
return [true, serializeTx(web3, tx)]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const { toBN } = require('web3').utils
|
||||
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
const address = web3.eth.abi.decodeParameter('address', data)
|
||||
|
||||
@@ -12,7 +14,7 @@ async function callArchive(web3, data, foreignBlock) {
|
||||
const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data)
|
||||
|
||||
if (toBN(blockNumber).gt(toBN(foreignBlock.number))) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE]
|
||||
}
|
||||
|
||||
const nonce = await web3.eth.getTransactionCount(address, blockNumber)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { ASYNC_CALL_ERRORS } = require('../../../utils/constants')
|
||||
const { serializeReceipt } = require('./serializers')
|
||||
|
||||
async function call(web3, data, foreignBlock) {
|
||||
@@ -6,7 +7,7 @@ async function call(web3, data, foreignBlock) {
|
||||
const receipt = await web3.eth.getTransactionReceipt(hash)
|
||||
|
||||
if (receipt === null || receipt.blockNumber > foreignBlock.number) {
|
||||
return [false, '0x']
|
||||
return [false, ASYNC_CALL_ERRORS.NOT_FOUND]
|
||||
}
|
||||
|
||||
return [true, serializeReceipt(web3, receipt)]
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
const { ZERO_ADDRESS } = require('../../../../../commons')
|
||||
|
||||
const serializeBlock = (web3, block) => {
|
||||
const args = [block.number, block.hash, block.miner]
|
||||
const types = ['uint256', 'bytes32', 'address']
|
||||
return web3.eth.abi.encodeParameters(types, args)
|
||||
const args = [
|
||||
block.number,
|
||||
block.hash,
|
||||
block.miner,
|
||||
block.gasUsed,
|
||||
block.gasLimit,
|
||||
block.parentHash,
|
||||
block.receiptsRoot,
|
||||
block.stateRoot,
|
||||
block.transactionsRoot,
|
||||
block.timestamp,
|
||||
block.difficulty,
|
||||
block.totalDifficulty
|
||||
]
|
||||
const type = '(uint256,bytes32,address,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256)'
|
||||
return web3.eth.abi.encodeParameter(type, args)
|
||||
}
|
||||
|
||||
const serializeTx = (web3, tx) => {
|
||||
const args = [
|
||||
tx.hash,
|
||||
tx.blockNumber,
|
||||
tx.blockHash,
|
||||
tx.transactionIndex,
|
||||
tx.from,
|
||||
tx.to || ZERO_ADDRESS,
|
||||
tx.value,
|
||||
@@ -18,16 +33,26 @@ const serializeTx = (web3, tx) => {
|
||||
tx.gasPrice,
|
||||
tx.input
|
||||
]
|
||||
const types = ['bytes32', 'uint256', 'address', 'address', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes']
|
||||
return web3.eth.abi.encodeParameters(types, args)
|
||||
const type = '(bytes32,uint256,bytes32,uint256,address,address,uint256,uint256,uint256,uint256,bytes)'
|
||||
return web3.eth.abi.encodeParameter(type, args)
|
||||
}
|
||||
|
||||
const normalizeLog = log => [log.address, log.topics, log.data]
|
||||
|
||||
const serializeReceipt = (web3, receipt) => {
|
||||
const args = [receipt.transactionHash, receipt.blockNumber, receipt.status, receipt.logs.map(normalizeLog)]
|
||||
const types = ['bytes32', 'uint256', 'bool', '(address,bytes32[],bytes)[]']
|
||||
return web3.eth.abi.encodeParameters(types, args)
|
||||
const args = [
|
||||
receipt.transactionHash,
|
||||
receipt.blockNumber,
|
||||
receipt.blockHash,
|
||||
receipt.transactionIndex,
|
||||
receipt.from,
|
||||
receipt.to || ZERO_ADDRESS,
|
||||
receipt.gasUsed,
|
||||
receipt.status,
|
||||
receipt.logs.map(normalizeLog)
|
||||
]
|
||||
const type = '(bytes32,uint256,bytes32,uint256,address,address,uint256,bool,(address,bytes32[],bytes)[])'
|
||||
return web3.eth.abi.encodeParameter(type, args)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -6,7 +6,16 @@ const logger = require('../../services/logger').child({
|
||||
const { strip0x } = require('../../../../commons')
|
||||
const { AMB_AFFIRMATION_REQUEST_EXTRA_GAS_ESTIMATOR: estimateExtraGas } = require('../../utils/constants')
|
||||
|
||||
async function estimateGas({ web3, homeBridge, validatorContract, messageId, status, result, address }) {
|
||||
async function estimateGas({
|
||||
web3,
|
||||
homeBridge,
|
||||
validatorContract,
|
||||
messageId,
|
||||
status,
|
||||
result,
|
||||
address,
|
||||
homeBlockNumber
|
||||
}) {
|
||||
try {
|
||||
const gasEstimate = await homeBridge.methods.confirmInformation(messageId, status, result).estimateGas({
|
||||
from: address
|
||||
@@ -15,7 +24,8 @@ async function estimateGas({ web3, homeBridge, validatorContract, messageId, sta
|
||||
// message length in bytes
|
||||
const len = strip0x(result).length / 2
|
||||
|
||||
const callbackGasLimit = parseInt(await homeBridge.methods.maxGasPerTx().call(), 10)
|
||||
let callbackGasLimit = parseInt(await homeBridge.methods.maxGasPerTx().call(), 10)
|
||||
callbackGasLimit = Math.ceil((callbackGasLimit * 64) / 63)
|
||||
|
||||
return gasEstimate + callbackGasLimit + estimateExtraGas(len)
|
||||
} catch (e) {
|
||||
@@ -51,6 +61,20 @@ async function estimateGas({ web3, homeBridge, validatorContract, messageId, sta
|
||||
throw new InvalidValidatorError(`${address} is not a validator`)
|
||||
}
|
||||
|
||||
logger.debug('Check if InformationRetrieved event for this message already exists')
|
||||
const logs = await homeBridge.getPastEvents('InformationRetrieved', {
|
||||
fromBlock: homeBlockNumber,
|
||||
toBlock: 'latest',
|
||||
filter: { messageId }
|
||||
})
|
||||
if (logs.length > 0) {
|
||||
logger.warn(
|
||||
'This particular message was already signed and processed by other validators.' +
|
||||
'However, evaluated async call result is different from the one recorded on-chain.'
|
||||
)
|
||||
throw new AlreadyProcessedError(e.message)
|
||||
}
|
||||
|
||||
throw new Error('Unknown error while processing message')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,13 @@ const { soliditySha3 } = require('web3').utils
|
||||
const { HttpListProviderError } = require('../../services/HttpListProvider')
|
||||
const rootLogger = require('../../services/logger')
|
||||
const makeBlockFinder = require('../../services/blockFinder')
|
||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants')
|
||||
const {
|
||||
EXIT_CODES,
|
||||
MAX_CONCURRENT_EVENTS,
|
||||
EXTRA_GAS_ABSOLUTE,
|
||||
ASYNC_CALL_ERRORS,
|
||||
MAX_ASYNC_CALL_RESULT_LENGTH
|
||||
} = require('../../utils/constants')
|
||||
const estimateGas = require('./estimateGas')
|
||||
const { getValidatorContract, getBlock, getBlockNumber, getRequiredBlockConfirmations } = require('../../tx/web3')
|
||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||
@@ -79,12 +85,17 @@ function processInformationRequestsBuilder(config) {
|
||||
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
|
||||
|
||||
const call = asyncCalls[asyncCallMethod]
|
||||
const [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
|
||||
let [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
|
||||
if (e instanceof HttpListProviderError) {
|
||||
throw e
|
||||
}
|
||||
return [false, '0x']
|
||||
logger.error({ error: e.message }, 'Unknown error during async call execution')
|
||||
throw e
|
||||
})
|
||||
if (result.length > 2 + MAX_ASYNC_CALL_RESULT_LENGTH * 2) {
|
||||
status = false
|
||||
result = ASYNC_CALL_ERRORS.RESULT_IS_TOO_LONG
|
||||
}
|
||||
logger.info({ requestSelector, method: asyncCallMethod, status, result }, 'Request result obtained')
|
||||
|
||||
let gasEstimate
|
||||
@@ -97,7 +108,8 @@ function processInformationRequestsBuilder(config) {
|
||||
messageId,
|
||||
status,
|
||||
result,
|
||||
address: config.validatorAddress
|
||||
address: config.validatorAddress,
|
||||
homeBlockNumber: homeBlock.number
|
||||
})
|
||||
logger.debug({ gasEstimate }, 'Gas estimated')
|
||||
} catch (e) {
|
||||
|
||||
@@ -8,8 +8,6 @@ const estimateGas = require('../processSignatureRequests/estimateGas')
|
||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||
|
||||
const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
|
||||
|
||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||
|
||||
function processSignatureRequestsBuilder(config) {
|
||||
@@ -37,7 +35,7 @@ function processSignatureRequestsBuilder(config) {
|
||||
const { sender, executor } = parseAMBMessage(message)
|
||||
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
|
||||
try {
|
||||
|
||||
@@ -8,8 +8,6 @@ const estimateGas = require('./estimateGas')
|
||||
const { AlreadyProcessedError, AlreadySignedError, InvalidValidatorError } = require('../../utils/errors')
|
||||
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
|
||||
|
||||
const { ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env
|
||||
|
||||
const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
|
||||
|
||||
function processSignatureRequestsBuilder(config) {
|
||||
@@ -48,7 +46,7 @@ function processSignatureRequestsBuilder(config) {
|
||||
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
|
||||
try {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user