Compare commits

..

21 Commits

Author SHA1 Message Date
Alexander Kolotov
075f74ed89 don't manage tx pool size 2022-06-06 18:12:21 +03:00
Alexander Kolotov
64daf4bfd1 proper form of the variable with validator address 2022-06-06 18:11:50 +03:00
Alexander Kolotov
1aed6ff34b correct name for worker to get AMB validator address 2022-06-06 17:53:42 +03:00
Alexander Kolotov
c07138f69f deployment of an AMB oracle 2022-06-06 15:47:02 +03:00
Alexander Kolotov
6692a5fc87 Merge branch 'develop' into combined-bridge-oracle 2022-06-03 14:38:45 +03:00
Alexander Kolotov
ff9f3fb7d6 Merge the develop branch to the master branch, preparation to v3.7.0
This merge contains the following set of changes:
  * [Oracle, Improvement] Periodic check for RPC sync state (#656)
2022-05-25 14:09:36 +03:00
Kirill Fedoseev
297cb67895 Periodic check for RPC sync state (#656) 2022-05-25 13:54:47 +03:00
Alexander Kolotov
85e23bd992 ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT added to an oracle's configuration 2022-05-24 15:40:19 +03:00
Alexander Kolotov
a2e110dbc7 switch to nethermind as a local node 2022-05-24 12:36:03 +03:00
Alexander Kolotov
bcf16144c1 Merge the develop branch to the master branch, preparation to v3.6.0
This merge contains the following set of changes:
  * [Oracle, Fix] Add missing Ganache nonce error message (#651)
  * [ALM, Improvement] ALM: show manually added signatures (#653)
2022-04-29 19:13:41 +03:00
Kirill Fedoseev
16f3e9add6 ALM: show manually added signatures (#653) 2022-04-29 16:38:08 +03:00
Ho Xuan Cuong
4af882b0ff Add missing Ganache nonce error message (#651) 2022-04-08 13:26:55 +04:00
Alexander Kolotov
dbaf7feca7 Merge the develop branch to the master branch, preparation to v3.5.0
This merge contains the following set of changes:
  * [Oracle, Improvement] Helpers for overriding AMB signatures (#640)
  * [Oracle, Improvement] Support manual signatures in erc to native mode (#646)
  * [ALM, Improvement] ALM: Do not show unneeded failed confirmations (#641)
  * [ALM, Improvement] ALM: warning for auto-relayed messages (#644)
  * [ALM, Fix] ALM: reorder warnings
2022-03-18 17:07:42 +03:00
Kirill Fedoseev
735aa75f81 ALM: reorder warnings 2022-03-17 17:41:33 +04:00
Kirill Fedoseev
910c3759c1 ALM: warning for auto-relayed messages (#644) 2022-03-17 14:11:14 +03:00
Kirill Fedoseev
9c2d2f404c ALM: Do not show unneeded failed confirmations (#641) 2022-03-13 13:54:33 +03:00
Kirill Fedoseev
2b51d4c209 Support manual signatures in erc to native mode (#646) 2022-03-10 14:18:19 +03:00
Alexander Kolotov
aa56744e8d incorrect network config for tb senderhome 2022-02-25 15:18:58 +03:00
Alexander Kolotov
2393d6c554 proper configuration for openethereum service 2022-02-25 14:53:39 +03:00
Alexander Kolotov
0de29b5847 TB and AMB combined on the deployment stage 2022-02-22 22:29:40 +03:00
Kirill Fedoseev
981231fb47 Helpers for overriding AMB signatures (#640) 2022-02-18 17:35:12 +03:00
45 changed files with 1392 additions and 521 deletions

View File

@@ -56,9 +56,11 @@ ORACLE_JSONRPC_ERROR_CODES | Override default JSON rpc error codes that can trig
ORACLE_HOME_EVENTS_REPROCESSING | If set to `true`, home events happened in the past will be refetched and processed once again, to ensure that nothing was missed on the first pass. | `bool`
ORACLE_HOME_EVENTS_REPROCESSING_BATCH_SIZE | Batch size for one `eth_getLogs` request when reprocessing old logs in the home chain. Defaults to `1000` | `integer`
ORACLE_HOME_EVENTS_REPROCESSING_BLOCK_DELAY | Block confirmations number, after which old logs are being reprocessed in the home chain. Defaults to `500` | `integer`
ORACLE_HOME_RPC_SYNC_STATE_CHECK_INTERVAL | Interval for checking JSON RPC sync state, by requesting the latest block number. Oracle will switch to the fallback JSON RPC in case sync process is stuck | `integer`
ORACLE_FOREIGN_EVENTS_REPROCESSING | If set to `true`, foreign events happened in the past will be refetched and processed once again, to ensure that nothing was missed on the first pass. | `bool`
ORACLE_FOREIGN_EVENTS_REPROCESSING_BATCH_SIZE | Batch size for one `eth_getLogs` request when reprocessing old logs in the foreign chain. Defaults to `500` | `integer`
ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY | Block confirmations number, after which old logs are being reprocessed in the foreign chain. Defaults to `250` | `integer`
ORACLE_FOREIGN_RPC_SYNC_STATE_CHECK_INTERVAL | Interval for checking JSON RPC sync state, by requesting the latest block number. Oracle will switch to the fallback JSON RPC in case sync process is stuck | `integer`
## Monitor configuration

View File

@@ -1,9 +1,9 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { TransactionReceipt } from 'web3-eth'
import { useMessageConfirmations } from '../hooks/useMessageConfirmations'
import { MessageObject } from '../utils/web3'
import styled from 'styled-components'
import { CONFIRMATIONS_STATUS } from '../config/constants'
import { CONFIRMATIONS_STATUS, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { CONFIRMATIONS_STATUS_LABEL, CONFIRMATIONS_STATUS_LABEL_HOME } from '../config/descriptions'
import { SimpleLoading } from './commons/Loading'
import { ValidatorsConfirmations } from './ValidatorsConfirmations'
@@ -54,7 +54,9 @@ export const ConfirmationsContainer = ({
home: { name: homeName },
foreign: { name: foreignName }
} = useStateProvider()
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
const src = useValidatorContract(fromHome, receipt ? receipt.blockNumber : 0)
const [executionBlockNumber, setExecutionBlockNumber] = useState(0)
const dst = useValidatorContract(!fromHome, executionBlockNumber || 'latest')
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
const {
confirmations,
@@ -71,11 +73,21 @@ export const ConfirmationsContainer = ({
fromHome,
homeStartBlock,
foreignStartBlock,
requiredSignatures,
validatorList,
requiredSignatures: src.requiredSignatures,
validatorList: src.validatorList,
targetValidatorList: dst.validatorList,
blockConfirmations
})
useEffect(
() => {
if (executionBlockNumber || executionData.status !== VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS) return
setExecutionBlockNumber(executionData.blockNumber)
},
[executionData.status, executionBlockNumber, executionData.blockNumber]
)
const statusLabel = fromHome ? CONFIRMATIONS_STATUS_LABEL_HOME : CONFIRMATIONS_STATUS_LABEL
const parseDescription = () => {
@@ -114,20 +126,22 @@ export const ConfirmationsContainer = ({
</MultiLine>
</StatusDescription>
<ValidatorsConfirmations
confirmations={confirmations}
requiredSignatures={requiredSignatures}
validatorList={validatorList}
confirmations={fromHome ? confirmations.filter(c => dst.validatorList.includes(c.validator)) : confirmations}
requiredSignatures={dst.requiredSignatures}
validatorList={dst.validatorList}
waitingBlocksResolved={waitingBlocksResolved}
/>
{signatureCollected && (
<ExecutionConfirmation
messageData={message.data}
message={message}
executionData={executionData}
isHome={!fromHome}
signatureCollected={signatureCollected}
confirmations={confirmations}
setExecutionData={setExecutionData}
executionEventsFetched={executionEventsFetched}
setPendingExecution={setPendingExecution}
dstRequiredSignatures={dst.requiredSignatures}
dstValidatorList={dst.validatorList}
/>
)}
</StyledConfirmationContainer>

View File

@@ -4,38 +4,47 @@ import { useWindowWidth } from '@react-hook/window-size'
import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS, ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from '../config/constants'
import { SimpleLoading } from './commons/Loading'
import styled from 'styled-components'
import { ExecutionData } from '../hooks/useMessageConfirmations'
import { ConfirmationParam, ExecutionData } from '../hooks/useMessageConfirmations'
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'
import { matchesRule, MessageObject, WarnRule } from '../utils/web3'
import { WarningAlert } from './commons/WarningAlert'
import { ErrorAlert } from './commons/ErrorAlert'
const StyledExecutionConfirmation = styled.div`
margin-top: 30px;
`
export interface ExecutionConfirmationParams {
messageData: string
message: MessageObject
executionData: ExecutionData
setExecutionData: Function
signatureCollected: boolean | string[]
confirmations: ConfirmationParam[]
isHome: boolean
executionEventsFetched: boolean
setPendingExecution: Function
dstRequiredSignatures: number
dstValidatorList: string[]
}
export const ExecutionConfirmation = ({
messageData,
message,
executionData,
setExecutionData,
signatureCollected,
confirmations,
isHome,
executionEventsFetched,
setPendingExecution
setPendingExecution,
dstRequiredSignatures,
dstValidatorList
}: ExecutionConfirmationParams) => {
const { foreign } = useStateProvider()
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
const [error, setError] = useState('')
const [warning, setWarning] = useState('')
const availableManualExecution =
!isHome &&
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
@@ -67,9 +76,27 @@ export const ExecutionConfirmation = ({
[availableManualExecution, foreign.bridgeContract]
)
useEffect(
() => {
if (!message.data || !executionData || !availableManualExecution) return
try {
const fileName = 'warnRules'
const rules: WarnRule[] = require(`../snapshots/${fileName}.json`)
for (let rule of rules) {
if (matchesRule(rule, message)) {
setWarning(rule.message)
return
}
}
} catch (e) {}
},
[availableManualExecution, executionData, message, message.data, setWarning]
)
const getExecutionStatusElement = (validatorStatus = '') => {
switch (validatorStatus) {
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS:
return <SuccessLabel>{validatorStatus}</SuccessLabel>
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
return <RedLabel>{validatorStatus}</RedLabel>
@@ -87,6 +114,8 @@ export const ExecutionConfirmation = ({
return (
<StyledExecutionConfirmation>
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
{warning && <WarningAlert onClick={() => setWarning('')} error={warning} />}
<table>
<Thead>
<tr>
@@ -125,10 +154,13 @@ export const ExecutionConfirmation = ({
<td>
<ManualExecutionButton
safeExecutionAvailable={safeExecutionAvailable}
messageData={messageData}
messageData={message.data}
setExecutionData={setExecutionData}
signatureCollected={signatureCollected as string[]}
confirmations={confirmations}
setPendingExecution={setPendingExecution}
setError={setError}
requiredSignatures={dstRequiredSignatures}
validatorList={dstValidatorList}
/>
</td>
)}

View File

@@ -8,7 +8,6 @@ import { TransactionReceipt } from 'web3-eth'
import { InfoAlert } from './commons/InfoAlert'
import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
import { ErrorAlert } from './commons/ErrorAlert'
const StyledMainPage = styled.div`
text-align: center;
@@ -52,7 +51,7 @@ export interface FormSubmitParams {
export const MainPage = () => {
const history = useHistory()
const { home, foreign, error, setError } = useStateProvider()
const { home, foreign } = useStateProvider()
const [networkName, setNetworkName] = useState('')
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
const [showInfoAlert, setShowInfoAlert] = useState(false)
@@ -132,7 +131,6 @@ export const MainPage = () => {
</AlertP>
</InfoAlert>
)}
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
<Route
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}

View File

@@ -14,6 +14,7 @@ import { useStateProvider } from '../state/StateProvider'
import { signatureToVRS, packSignatures } from '../utils/signatures'
import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
import { TransactionReceipt } from 'web3-eth'
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
const ActionButton = styled.button`
color: var(--button-color);
@@ -30,22 +31,75 @@ interface ManualExecutionButtonParams {
safeExecutionAvailable: boolean
messageData: string
setExecutionData: Function
signatureCollected: string[]
confirmations: ConfirmationParam[]
setPendingExecution: Function
setError: Function
requiredSignatures: number
validatorList: string[]
}
export const ManualExecutionButton = ({
safeExecutionAvailable,
messageData,
setExecutionData,
signatureCollected,
setPendingExecution
confirmations,
setPendingExecution,
setError,
requiredSignatures,
validatorList
}: ManualExecutionButtonParams) => {
const { foreign, setError } = useStateProvider()
const { foreign } = useStateProvider()
const { library, activate, account, active } = useWeb3React()
const [manualExecution, setManualExecution] = useState(false)
const [allowFailures, setAllowFailures] = useState(false)
const notReady = !foreign.bridgeContract || !signatureCollected || !signatureCollected.length
const [ready, setReady] = useState(false)
const [title, setTitle] = useState('Loading')
const [validSignatures, setValidSignatures] = useState<string[]>([])
useEffect(
() => {
if (
!foreign.bridgeContract ||
!foreign.web3 ||
!confirmations ||
!confirmations.length ||
!requiredSignatures ||
!validatorList ||
!validatorList.length
)
return
const signatures = []
for (let i = 0; i < confirmations.length && signatures.length < requiredSignatures; i++) {
const sig = confirmations[i].signature
if (!sig) {
continue
}
const { v, r, s } = signatureToVRS(sig)
const signer = foreign.web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`)
if (validatorList.includes(signer)) {
signatures.push(sig)
}
}
if (signatures.length >= requiredSignatures) {
setValidSignatures(signatures.slice(0, requiredSignatures))
setTitle('Execute')
setReady(true)
} else {
setTitle('Unavailable')
}
},
[
foreign.bridgeContract,
foreign.web3,
validatorList,
requiredSignatures,
messageData,
setValidSignatures,
confirmations
]
)
useEffect(
() => {
@@ -73,9 +127,9 @@ export const ManualExecutionButton = ({
return
}
if (!library || !foreign.bridgeContract || !signatureCollected || !signatureCollected.length) return
if (!library || !foreign.bridgeContract || !foreign.web3 || !validSignatures || !validSignatures.length) return
const signatures = packSignatures(signatureCollected.map(signatureToVRS))
const signatures = packSignatures(validSignatures.map(signatureToVRS))
const messageId = messageData.slice(0, 66)
const bridge = foreign.bridgeContract
const executeMethod =
@@ -140,19 +194,20 @@ export const ManualExecutionButton = ({
foreign.bridgeContract,
setError,
messageData,
signatureCollected,
setExecutionData,
setPendingExecution,
safeExecutionAvailable,
allowFailures
allowFailures,
foreign.web3,
validSignatures
]
)
return (
<div>
<div className="is-center">
<ActionButton disabled={notReady} className="button outline" onClick={() => setManualExecution(true)}>
Execute
<ActionButton disabled={!ready} className="button outline" onClick={() => setManualExecution(true)}>
{title}
</ActionButton>
</div>
{safeExecutionAvailable && (

View File

@@ -1,7 +1,7 @@
import React from 'react'
import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks'
import { useWindowWidth } from '@react-hook/window-size'
import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { RECENT_AGE, SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { SimpleLoading } from './commons/Loading'
import styled from 'styled-components'
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
@@ -31,7 +31,9 @@ export const ValidatorsConfirmations = ({
const getValidatorStatusElement = (validatorStatus = '') => {
switch (validatorStatus) {
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
return <SuccessLabel>{validatorStatus}</SuccessLabel>
case VALIDATOR_CONFIRMATION_STATUS.MANUAL:
case VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID:
return <SuccessLabel>{VALIDATOR_CONFIRMATION_STATUS.SUCCESS}</SuccessLabel>
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
return <RedLabel>{validatorStatus}</RedLabel>
case VALIDATOR_CONFIRMATION_STATUS.PENDING:
@@ -58,26 +60,28 @@ export const ValidatorsConfirmations = ({
</tr>
</Thead>
<tbody>
{validatorList.map((validator, i) => {
const filteredConfirmation = confirmations.filter(c => c.validator === validator)
const confirmation = filteredConfirmation.length > 0 ? filteredConfirmation[0] : null
const displayedStatus = confirmation && confirmation.status ? confirmation.status : ''
const explorerLink = confirmation && confirmation.txHash ? getExplorerTxUrl(confirmation.txHash, true) : ''
const elementIfNoTimestamp =
displayedStatus !== VALIDATOR_CONFIRMATION_STATUS.WAITING &&
displayedStatus !== VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED ? (
(displayedStatus === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || displayedStatus === '') &&
waitingBlocksResolved ? (
SEARCHING_TX
) : (
<SimpleLoading />
)
) : (
''
)
{confirmations.map((confirmation, i) => {
const displayedStatus = confirmation.status
const explorerLink = getExplorerTxUrl(confirmation.txHash, true)
let elementIfNoTimestamp: any = <SimpleLoading />
switch (displayedStatus) {
case '':
case VALIDATOR_CONFIRMATION_STATUS.UNDEFINED:
if (waitingBlocksResolved) {
elementIfNoTimestamp = SEARCHING_TX
}
break
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
case VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED:
elementIfNoTimestamp = ''
break
case VALIDATOR_CONFIRMATION_STATUS.MANUAL:
elementIfNoTimestamp = RECENT_AGE
break
}
return (
<tr key={i}>
<td>{windowWidth < 850 ? formatTxHash(validator) : validator}</td>
<td>{windowWidth < 850 ? formatTxHash(confirmation.validator) : confirmation.validator}</td>
<StatusTd className="text-center">{getValidatorStatusElement(displayedStatus)}</StatusTd>
<AgeTd className="text-center">
{confirmation && confirmation.timestamp > 0 ? (
@@ -94,7 +98,7 @@ export const ValidatorsConfirmations = ({
</tbody>
</table>
<RequiredConfirmations>
{requiredSignatures} of {validatorList.length} confirmations required
At least <strong>{requiredSignatures}</strong> of <strong>{validatorList.length}</strong> confirmations required
</RequiredConfirmations>
</div>
)

View File

@@ -33,7 +33,7 @@ export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: str
}
return (
<div className="row is-center">
<StyledErrorAlert className="col-10 is-vertical-align row">
<StyledErrorAlert className="col-12 is-vertical-align row">
<InfoIcon color="var(--failed-color)" />
<TextContainer className="col-10">
{text}

View File

@@ -0,0 +1,34 @@
import React from 'react'
import styled from 'styled-components'
import { InfoIcon } from './InfoIcon'
import { CloseIcon } from './CloseIcon'
const StyledErrorAlert = styled.div`
border: 1px solid var(--warning-color);
border-radius: 4px;
margin-bottom: 20px;
padding-top: 10px;
`
const CloseIconContainer = styled.div`
cursor: pointer;
`
const TextContainer = styled.div`
white-space: pre-wrap;
flex-direction: column;
`
export const WarningAlert = ({ onClick, error }: { onClick: () => void; error: string }) => {
return (
<div className="row is-center">
<StyledErrorAlert className="col-12 is-vertical-align row">
<InfoIcon color="var(--warning-color)" />
<TextContainer className="col-10">{error}</TextContainer>
<CloseIconContainer className="col-1 is-vertical-align is-center" onClick={onClick}>
<CloseIcon color="var(--warning-color)" />
</CloseIconContainer>
</StyledErrorAlert>
</div>
)
}

View File

@@ -54,14 +54,19 @@ export const CONFIRMATIONS_STATUS = {
}
export const VALIDATOR_CONFIRMATION_STATUS = {
SUCCESS: 'Success',
SUCCESS: 'Confirmed',
MANUAL: 'Manual',
EXECUTION_SUCCESS: 'Executed',
FAILED: 'Failed',
FAILED_VALID: 'Failed valid',
PENDING: 'Pending',
WAITING: 'Waiting',
NOT_REQUIRED: 'Not required',
UNDEFINED: 'UNDEFINED'
}
export const RECENT_AGE = 'Recent'
export const SEARCHING_TX = 'Searching Transaction...'
export const INCORRECT_CHAIN_ERROR = `Incorrect chain chosen. Switch to ${FOREIGN_NETWORK_NAME} in the wallet.`

View File

@@ -29,17 +29,16 @@ export interface useMessageConfirmationsParams {
foreignStartBlock: Maybe<number>
requiredSignatures: number
validatorList: string[]
targetValidatorList: string[]
blockConfirmations: number
}
export interface BasicConfirmationParam {
export interface ConfirmationParam {
validator: string
status: string
}
export interface ConfirmationParam extends BasicConfirmationParam {
txHash: string
timestamp: number
signature?: string
}
export interface ExecutionData {
@@ -48,6 +47,7 @@ export interface ExecutionData {
txHash: string
timestamp: number
executionResult: boolean
blockNumber: number
}
export const useMessageConfirmations = ({
@@ -58,6 +58,7 @@ export const useMessageConfirmations = ({
foreignStartBlock,
requiredSignatures,
validatorList,
targetValidatorList,
blockConfirmations
}: useMessageConfirmationsParams) => {
const { home, foreign } = useStateProvider()
@@ -65,7 +66,7 @@ export const useMessageConfirmations = ({
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
const [waitingBlocks, setWaitingBlocks] = useState(false)
const [waitingBlocksResolved, setWaitingBlocksResolved] = useState(false)
const [signatureCollected, setSignatureCollected] = useState<boolean | string[]>(false)
const [signatureCollected, setSignatureCollected] = useState(false)
const [executionEventsFetched, setExecutionEventsFetched] = useState(false)
const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null)
const [executionData, setExecutionData] = useState<ExecutionData>({
@@ -73,7 +74,8 @@ export const useMessageConfirmations = ({
validator: '',
txHash: '',
timestamp: 0,
executionResult: false
executionResult: false,
blockNumber: 0
})
const [waitingBlocksForExecution, setWaitingBlocksForExecution] = useState(false)
const [waitingBlocksForExecutionResolved, setWaitingBlocksForExecutionResolved] = useState(false)
@@ -140,10 +142,9 @@ export const useMessageConfirmations = ({
// The collected signature event is only fetched once the signatures are collected on tx from home to foreign, to calculate if
// the execution tx on the foreign network is waiting for block confirmations
// This is executed if the message is in Home to Foreign direction only
const hasCollectedSignatures = !!signatureCollected // true or string[]
useEffect(
() => {
if (!fromHome || !receipt || !home.web3 || !home.bridgeContract || !hasCollectedSignatures) return
if (!fromHome || !receipt || !home.web3 || !home.bridgeContract || !signatureCollected) return
let timeoutId: number
let isCancelled = false
@@ -179,7 +180,7 @@ export const useMessageConfirmations = ({
isCancelled = true
}
},
[fromHome, home.bridgeContract, home.web3, message.data, receipt, hasCollectedSignatures]
[fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected]
)
// Check if the responsible validator is waiting for block confirmations to execute the message on foreign network
@@ -252,6 +253,35 @@ export const useMessageConfirmations = ({
let timeoutId: number
let isCancelled = false
if (fromHome) {
if (!targetValidatorList || !targetValidatorList.length) return
const msgHash = home.web3.utils.sha3(message.data)!
const allValidators = [...validatorList, ...targetValidatorList].filter((v, i, s) => s.indexOf(v) === i)
const manualConfirmations = []
for (let i = 0; i < allValidators.length; i++) {
try {
const overrideSignatures: {
[key: string]: string
} = require(`../snapshots/signatures_${allValidators[i]}.json`)
if (overrideSignatures[msgHash]) {
console.log(`Adding manual signature from ${allValidators[i]}`)
manualConfirmations.push({
status: VALIDATOR_CONFIRMATION_STATUS.MANUAL,
validator: allValidators[i],
timestamp: 0,
txHash: '',
signature: overrideSignatures[msgHash]
})
} else {
console.log(`No manual signature from ${allValidators[i]} was found`)
}
} catch (e) {
console.log(`Signatures overrides are not present for ${allValidators[i]}`)
}
}
setConfirmations(manualConfirmations)
}
getConfirmationsForTx(
message.data,
home.web3,
@@ -284,7 +314,8 @@ export const useMessageConfirmations = ({
home.bridgeContract,
requiredSignatures,
waitingBlocksResolved,
homeStartBlock
homeStartBlock,
targetValidatorList
]
)
@@ -343,7 +374,10 @@ export const useMessageConfirmations = ({
// Sets the message status based in the collected information
useEffect(
() => {
if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS && existsConfirmation(confirmations)) {
if (
executionData.status === VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS &&
existsConfirmation(confirmations)
) {
const newStatus = executionData.executionResult
? CONFIRMATIONS_STATUS.SUCCESS
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED

View File

@@ -4,19 +4,13 @@ import Web3 from 'web3'
import { getRequiredSignatures, getValidatorAddress, getValidatorList } from '../utils/contract'
import { BRIDGE_VALIDATORS_ABI } from '../abis'
import { useStateProvider } from '../state/StateProvider'
import { TransactionReceipt } from 'web3-eth'
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
export interface useValidatorContractParams {
fromHome: boolean
receipt: Maybe<TransactionReceipt>
}
export const useValidatorContract = ({ receipt, fromHome }: useValidatorContractParams) => {
export const useValidatorContract = (isHome: boolean, blockNumber: number | 'latest') => {
const [validatorContract, setValidatorContract] = useState<Maybe<Contract>>(null)
const [requiredSignatures, setRequiredSignatures] = useState(0)
const [validatorList, setValidatorList] = useState([])
const [validatorList, setValidatorList] = useState<string[]>([])
const { home, foreign } = useStateProvider()
@@ -29,34 +23,34 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
const callRequiredSignatures = async (
contract: Maybe<Contract>,
receipt: TransactionReceipt,
blockNumber: number | 'latest',
setResult: Function,
snapshotProvider: SnapshotProvider,
web3: Web3,
api: string
) => {
if (!contract) return
const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider, web3, api)
const result = await getRequiredSignatures(contract, blockNumber, snapshotProvider, web3, api)
setResult(result)
}
const callValidatorList = async (
contract: Maybe<Contract>,
receipt: TransactionReceipt,
blockNumber: number | 'latest',
setResult: Function,
snapshotProvider: SnapshotProvider,
web3: Web3,
api: string
) => {
if (!contract) return
const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider, web3, api)
const result = await getValidatorList(contract, blockNumber, snapshotProvider, web3, api)
setResult(result)
}
const web3 = fromHome ? home.web3 : foreign.web3
const api = fromHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
const web3 = isHome ? home.web3 : foreign.web3
const api = isHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
const bridgeContract = isHome ? home.bridgeContract : foreign.bridgeContract
const snapshotProvider = isHome ? homeSnapshotProvider : foreignSnapshotProvider
useEffect(
() => {
@@ -68,11 +62,11 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
useEffect(
() => {
if (!web3 || !receipt) return
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider, web3, api)
callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider, web3, api)
if (!web3 || !blockNumber) return
callRequiredSignatures(validatorContract, blockNumber, setRequiredSignatures, snapshotProvider, web3, api)
callValidatorList(validatorContract, blockNumber, setValidatorList, snapshotProvider, web3, api)
},
[validatorContract, receipt, web3, snapshotProvider, api]
[validatorContract, blockNumber, web3, snapshotProvider, api]
)
return {

View File

@@ -1,4 +1,4 @@
import React, { createContext, ReactNode, useState } from 'react'
import React, { createContext, ReactNode } from 'react'
import { useNetwork } from '../hooks/useNetwork'
import {
HOME_RPC_URL,
@@ -25,8 +25,6 @@ export interface StateContext {
home: BaseNetworkParams
foreign: BaseNetworkParams
loading: boolean
error: string
setError: Function
}
const initialState = {
@@ -44,9 +42,7 @@ const initialState = {
bridgeAddress: FOREIGN_BRIDGE_ADDRESS,
bridgeContract: null
},
loading: true,
error: '',
setError: () => {}
loading: true
}
const StateContext = createContext<StateContext>(initialState)
@@ -58,7 +54,6 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
homeWeb3: homeNetwork.web3,
foreignWeb3: foreignNetwork.web3
})
const [error, setError] = useState('')
const value = {
home: {
@@ -73,9 +68,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
bridgeContract: foreignBridge,
...foreignNetwork
},
loading: homeNetwork.loading || foreignNetwork.loading,
error,
setError
loading: homeNetwork.loading || foreignNetwork.loading
}
return <StateContext.Provider value={value}>{children}</StateContext.Provider>

View File

@@ -28,5 +28,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
--failed-color: ${props => props.theme.failed.textColor};
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
--warning-color: ${props => props.theme.warning.textColor};
--warning-bg-color: ${props => props.theme.warning.backgroundColor};
}
`

View File

@@ -17,6 +17,10 @@ const theme = {
failed: {
textColor: '#de4437',
backgroundColor: 'rgba(222,68,55,.1)'
},
warning: {
textColor: '#ffa758',
backgroundColor: 'rgba(222,68,55,.1)'
}
}
export default theme

View File

@@ -14,37 +14,40 @@ const messageData = '0x123456'
const OTHER_HASH = 'aabbccdd'
const bridgeAddress = '0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560'
const otherAddress = '0xD4075FB57fCf038bFc702c915Ef9592534bED5c1'
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
const validator2 = '0xAe8bFfc8BBc6AAa9E21ED1E4e4957fe798BEA25f'
const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244'
describe('getFailedTransactions', () => {
test('should only return failed transactions', async () => {
const to = otherAddress
const transactions = [
{ isError: '0', to },
{ isError: '1', to },
{ isError: '0', to },
{ isError: '1', to },
{ isError: '1', to }
{ isError: '0', to, from: validator1 },
{ isError: '1', to, from: validator1 },
{ isError: '0', to, from: validator2 },
{ isError: '1', to, from: validator2 },
{ isError: '1', to, from: validator3 }
]
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
const result = await getFailedTransactions('', to, 0, 1, '', fetchAccountTransactions)
expect(result.length).toEqual(3)
const result = await getFailedTransactions(validator1, to, 0, 1, '', fetchAccountTransactions)
expect(result.length).toEqual(1)
})
})
describe('getSuccessTransactions', () => {
test('should only return success transactions', async () => {
const to = otherAddress
const transactions = [
{ isError: '0', to },
{ isError: '1', to },
{ isError: '0', to },
{ isError: '1', to },
{ isError: '1', to }
{ isError: '0', to, from: validator1 },
{ isError: '1', to, from: validator1 },
{ isError: '0', to, from: validator2 },
{ isError: '1', to, from: validator2 },
{ isError: '1', to, from: validator3 }
]
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
const result = await getSuccessTransactions('', to, 0, 1, '', fetchAccountTransactions)
expect(result.length).toEqual(2)
const result = await getSuccessTransactions(validator1, to, 0, 1, '', fetchAccountTransactions)
expect(result.length).toEqual(1)
})
})
describe('filterValidatorSignatureTransaction', () => {

View File

@@ -5,7 +5,7 @@ import Web3 from 'web3'
import { Contract } from 'web3-eth-contract'
import { APIPendingTransaction, APITransaction } from '../explorer'
import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants'
import { BasicConfirmationParam } from '../../hooks/useMessageConfirmations'
import { ConfirmationParam } from '../../hooks/useMessageConfirmations'
jest.mock('../validatorConfirmationHelpers')
@@ -18,6 +18,9 @@ const messageData = '0x111111111'
const web3 = {
utils: {
soliditySha3Raw: (data: string) => `0xaaaa${data.replace('0x', '')}`
},
eth: {
accounts: new Web3().eth.accounts
}
} as Web3
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
@@ -25,7 +28,7 @@ const validator2 = '0xAe8bFfc8BBc6AAa9E21ED1E4e4957fe798BEA25f'
const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244'
const validatorList = [validator1, validator2, validator3]
const signature =
'0x519d704bceed17423daa79c20531cc34fc27a4be6e53fc5069a8023019188ca4519d704bceed17423daa79c20531cc34fc27a4be6e53fc5069a8023019188ca4'
'0x6f5b74905669999f1abdb52e1e215506907e1849aac7b31854da458b33a5954e15b165007c3703cfd16e61ca46a96a56727ed11fa47be359d3834515accd016e1b'
const bridgeContract = {
methods: {
signature: () => ({
@@ -61,19 +64,19 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: '',
timestamp: 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
timestamp: 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
@@ -110,9 +113,8 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(2)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -135,7 +137,7 @@ describe('getConfirmationsForTx', () => {
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED, txHash: '', timestamp: 0 }
])
)
})
@@ -144,19 +146,19 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator === validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: '',
timestamp: 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
timestamp: 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
@@ -208,19 +210,19 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 ? '0x123' : '',
timestamp: validatorData.validator !== validator3 ? 123 : 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
timestamp: 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
@@ -257,9 +259,8 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(3)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -281,16 +282,16 @@ describe('getConfirmationsForTx', () => {
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED, txHash: '', timestamp: 0 }
])
)
})
@@ -304,22 +305,22 @@ describe('getConfirmationsForTx', () => {
? VALIDATOR_CONFIRMATION_STATUS.SUCCESS
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? '0x123' : '',
timestamp: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? 123 : 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator3
? VALIDATOR_CONFIRMATION_STATUS.FAILED
? VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator3 ? '0x123' : '',
timestamp: validatorData.validator === validator3 ? 123 : 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
@@ -356,9 +357,8 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).toBeCalledTimes(1)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(1)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
@@ -382,26 +382,26 @@ describe('getConfirmationsForTx', () => {
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res4).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED, txHash: '', timestamp: 0 }
])
)
})
@@ -414,22 +414,22 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator === validator1 ? '0x123' : '',
timestamp: validatorData.validator === validator1 ? 123 : 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator2
? VALIDATOR_CONFIRMATION_STATUS.FAILED
? VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator2 ? '0x123' : '',
timestamp: validatorData.validator === validator2 ? 123 : 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator3
@@ -492,22 +492,22 @@ describe('getConfirmationsForTx', () => {
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
expect(res4).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
])
)
@@ -521,13 +521,13 @@ describe('getConfirmationsForTx', () => {
validator,
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator === validator1 ? '0x123' : '',
timestamp: validatorData.validator === validator1 ? 123 : 0
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator !== validator1
@@ -536,7 +536,7 @@ describe('getConfirmationsForTx', () => {
txHash: validatorData.validator !== validator1 ? '0x123' : '',
timestamp: validatorData.validator !== validator1 ? 123 : 0
}))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
@@ -596,9 +596,9 @@ describe('getConfirmationsForTx', () => {
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res3).toEqual(
@@ -610,9 +610,13 @@ describe('getConfirmationsForTx', () => {
)
})
test('should remove pending state after transaction mined', async () => {
// Validator1 success
// Validator2 failed
// Validator3 Pending
const validator4 = '0x9d2dC11C342F4eF3C5491A048D0f0eBCd2D8f7C3'
const validatorList = [validator1, validator2, validator3, validator4]
// Validator1 success (ts=100)
// Validator2 failed (ts=200)
// Validator3 Pending (ts=300)
// Validator4 Excess confirmation (Failed) (ts=400)
getValidatorConfirmation
.mockImplementationOnce(() => async (validator: string) => ({
@@ -623,41 +627,57 @@ describe('getConfirmationsForTx', () => {
.mockImplementation(() => async (validator: string) => ({
validator,
status:
validator !== validator2 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
validator === validator1 || validator === validator3
? VALIDATOR_CONFIRMATION_STATUS.SUCCESS
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
}))
getSuccessExecutionTransaction
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
.mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator === validator1 ? '0x123' : '',
timestamp: validatorData.validator === validator1 ? 123 : 0
txHash: validatorData.validator === validator1 ? '0x100' : '',
timestamp: validatorData.validator === validator1 ? 100 : 0
}))
.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator2 ? '0x123' : '',
timestamp: validatorData.validator !== validator2 ? 123 : 0
txHash:
validatorData.validator === validator1 ? '0x100' : validatorData.validator === validator3 ? '0x300' : '',
timestamp: validatorData.validator === validator1 ? 100 : validatorData.validator === validator3 ? 300 : ''
}))
getValidatorFailedTransaction
.mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator2
? VALIDATOR_CONFIRMATION_STATUS.FAILED
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator2 ? '0x200' : '',
timestamp: validatorData.validator === validator2 ? 200 : 0
}))
.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator2 || validatorData.validator === validator4
? validatorData.validator === validator2
? VALIDATOR_CONFIRMATION_STATUS.FAILED
: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash:
validatorData.validator === validator2 ? '0x200' : validatorData.validator === validator4 ? '0x400' : '',
timestamp: validatorData.validator === validator2 ? 200 : validatorData.validator === validator4 ? 400 : ''
}))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator2
? VALIDATOR_CONFIRMATION_STATUS.FAILED
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator2 ? '0x123' : '',
timestamp: validatorData.validator === validator2 ? 123 : 0
}))
getValidatorPendingTransaction
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
.mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status:
validatorData.validator === validator3
? VALIDATOR_CONFIRMATION_STATUS.PENDING
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator3 ? '0x123' : '',
timestamp: validatorData.validator === validator3 ? 123 : 0
txHash: validatorData.validator === validator3 ? '0x300' : '',
timestamp: validatorData.validator === validator3 ? 300 : 0
}))
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
.mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '',
@@ -698,7 +718,7 @@ describe('getConfirmationsForTx', () => {
expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1)
expect(setFailedConfirmations.mock.calls[0][0]).toEqual(false)
expect(setFailedConfirmations.mock.calls[0][0]).toEqual(true)
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
expect(setPendingConfirmations).toBeCalledTimes(1)
@@ -712,28 +732,32 @@ describe('getConfirmationsForTx', () => {
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res2).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res3).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x300', timestamp: 300 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res4).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x300', timestamp: 300 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
@@ -761,14 +785,13 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(7)
expect(getValidatorConfirmation).toBeCalledTimes(2)
expect(getSuccessExecutionTransaction).toBeCalledTimes(2)
expect(setSignatureCollected).toBeCalledTimes(3)
expect(setSignatureCollected).toBeCalledTimes(2)
expect(setSignatureCollected.mock.calls[0][0]).toEqual(false)
expect(setSignatureCollected.mock.calls[1][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[2][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(2)
expect(setFailedConfirmations).toBeCalledTimes(2)
expect(setFailedConfirmations.mock.calls[0][0]).toEqual(false)
expect(setFailedConfirmations.mock.calls[0][0]).toEqual(true)
expect(setFailedConfirmations.mock.calls[1][0]).toEqual(false)
expect(getValidatorPendingTransaction).toBeCalledTimes(1)
@@ -781,23 +804,26 @@ describe('getConfirmationsForTx', () => {
const res7 = setResult.mock.calls[6][0](res6)
expect(res5).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res6).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
])
)
expect(res7).toEqual(
expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x400', timestamp: 400 }
])
)
})

View File

@@ -84,10 +84,11 @@ describe('getFinalizationEvent', () => {
expect(setResult).toBeCalledTimes(1)
expect(setResult.mock.calls[0][0]).toEqual({
validator: validator1,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS,
txHash,
timestamp,
executionResult: true
executionResult: true,
blockNumber: 5523145
})
expect(getFailedExecution).toBeCalledTimes(0)
@@ -237,7 +238,8 @@ describe('getFinalizationEvent', () => {
status: VALIDATOR_CONFIRMATION_STATUS.PENDING,
txHash,
timestamp: expect.any(Number),
executionResult: false
executionResult: false,
blockNumber: 0
})
expect(getFailedExecution).toBeCalledTimes(0)
@@ -294,7 +296,8 @@ describe('getFinalizationEvent', () => {
status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
txHash,
timestamp: expect.any(Number),
executionResult: false
executionResult: false,
blockNumber: expect.any(Number)
})
expect(getFailedExecution).toBeCalledTimes(1)

View File

@@ -52,11 +52,15 @@ export const getValidatorAddress = (contract: Contract) => contract.methods.vali
export const getRequiredSignatures = async (
contract: Contract,
blockNumber: number,
blockNumber: number | 'latest',
snapshotProvider: SnapshotProvider,
web3: Web3 | null = null,
api: string = ''
) => {
if (blockNumber === 'latest') {
return contract.methods.requiredSignatures().call()
}
const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber)
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
@@ -78,11 +82,15 @@ export const getRequiredSignatures = async (
export const getValidatorList = async (
contract: Contract,
blockNumber: number,
blockNumber: number | 'latest',
snapshotProvider: SnapshotProvider,
web3: Web3 | null = null,
api: string = ''
) => {
if (blockNumber === 'latest') {
return contract.methods.validatorList().call()
}
const addedEventsFromSnapshot = snapshotProvider.validatorAddedEvents(blockNumber)
const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber)
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()

View File

@@ -12,6 +12,7 @@ import Web3 from 'web3'
import { Contract } from 'web3-eth-contract'
export interface APITransaction {
from: string
timeStamp: string
isError: string
input: string
@@ -54,7 +55,7 @@ export const fetchAccountTransactions = async ({ account, startBlock, endBlock,
url.searchParams.append('module', 'account')
url.searchParams.append('action', 'txlist')
url.searchParams.append('address', account)
url.searchParams.append('filterby', 'from')
url.searchParams.append('filterby', 'to')
url.searchParams.append('startblock', startBlock.toString())
url.searchParams.append('endblock', endBlock.toString())
@@ -64,7 +65,7 @@ export const fetchAccountTransactions = async ({ account, startBlock, endBlock,
return []
}
return result.result
return result.result || []
}
export const fetchPendingTransactions = async ({
@@ -180,7 +181,9 @@ export const getLogs = async (
if (topics[i] !== null) {
url.searchParams.append(`topic${i}`, topics[i] as string)
for (let j = 0; j < i; j++) {
url.searchParams.append(`topic${j}_${i}_opr`, 'and')
if (topics[j] !== null) {
url.searchParams.append(`topic${j}_${i}_opr`, 'and')
}
}
}
}
@@ -194,7 +197,7 @@ export const getLogs = async (
}))
}
const filterReceiver = (to: string) => (tx: APITransaction) => tx.to.toLowerCase() === to.toLowerCase()
const filterSender = (from: string) => (tx: APITransaction) => tx.from.toLowerCase() === from.toLowerCase()
export const getFailedTransactions = async (
account: string,
@@ -204,9 +207,9 @@ export const getFailedTransactions = async (
api: string,
getAccountTransactionsMethod = getAccountTransactions
): Promise<APITransaction[]> => {
const transactions = await getAccountTransactionsMethod({ account, startBlock, endBlock, api })
const transactions = await getAccountTransactionsMethod({ account: to, startBlock, endBlock, api })
return transactions.filter(t => t.isError !== '0').filter(filterReceiver(to))
return transactions.filter(t => t.isError !== '0').filter(filterSender(account))
}
export const getSuccessTransactions = async (
@@ -217,9 +220,9 @@ export const getSuccessTransactions = async (
api: string,
getAccountTransactionsMethod = getAccountTransactions
): Promise<APITransaction[]> => {
const transactions = await getAccountTransactionsMethod({ account, startBlock, endBlock, api })
const transactions = await getAccountTransactionsMethod({ account: to, startBlock, endBlock, api })
return transactions.filter(t => t.isError === '0').filter(filterReceiver(to))
return transactions.filter(t => t.isError === '0').filter(filterSender(account))
}
export const filterValidatorSignatureTransaction = (

View File

@@ -2,26 +2,37 @@ import Web3 from 'web3'
import { Contract } from 'web3-eth-contract'
import { HOME_RPC_POLLING_INTERVAL, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { GetTransactionParams, APITransaction, APIPendingTransaction, GetPendingTransactionParams } from './explorer'
import { getAffirmationsSigned, getMessagesSigned } from './contract'
import {
getValidatorConfirmation,
getValidatorFailedTransaction,
getValidatorPendingTransaction,
getSuccessExecutionTransaction
} from './validatorConfirmationHelpers'
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
import { signatureToVRS } from './signatures'
const mergeConfirmations = (oldConfirmations: BasicConfirmationParam[], newConfirmations: BasicConfirmationParam[]) => {
const mergeConfirmations = (oldConfirmations: ConfirmationParam[], newConfirmations: ConfirmationParam[]) => {
const confirmations = [...oldConfirmations]
newConfirmations.forEach(validatorData => {
const index = confirmations.findIndex(e => e.validator === validatorData.validator)
if (index === -1) {
confirmations.push(validatorData)
return
}
const currentStatus = confirmations[index].status
const newStatus = validatorData.status
if (
(validatorData as ConfirmationParam).txHash ||
validatorData.txHash ||
!!validatorData.signature ||
(newStatus !== currentStatus && newStatus !== VALIDATOR_CONFIRMATION_STATUS.UNDEFINED)
) {
confirmations[index] = validatorData
confirmations[index] = {
status: validatorData.status,
validator: validatorData.validator,
timestamp: confirmations[index].timestamp || validatorData.timestamp,
txHash: confirmations[index].txHash || validatorData.txHash,
signature: confirmations[index].signature || validatorData.signature
}
}
})
return confirmations
@@ -45,19 +56,17 @@ export const getConfirmationsForTx = async (
setPendingConfirmations: Function,
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
) => {
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
const hashMsg = web3.utils.soliditySha3Raw(messageData)
let validatorConfirmations = await Promise.all(
validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, confirmationContractMethod))
validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, fromHome))
)
const updateConfirmations = (confirmations: BasicConfirmationParam[]) => {
const updateConfirmations = (confirmations: ConfirmationParam[]) => {
if (confirmations.length === 0) {
return
}
validatorConfirmations = mergeConfirmations(validatorConfirmations, confirmations)
setResult((currentConfirmations: BasicConfirmationParam[]) => {
setResult((currentConfirmations: ConfirmationParam[]) => {
if (currentConfirmations && currentConfirmations.length) {
return mergeConfirmations(currentConfirmations, confirmations)
}
@@ -67,11 +76,37 @@ export const getConfirmationsForTx = async (
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const hasEnoughSignatures = successConfirmations.length === requiredSignatures
const hasEnoughSignatures = successConfirmations.length >= requiredSignatures
updateConfirmations(validatorConfirmations)
setSignatureCollected(hasEnoughSignatures)
if (hasEnoughSignatures) {
setPendingConfirmations(false)
if (fromHome) {
// fetch collected signatures for possible manual processing
const signatures = await Promise.all(
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call())
)
const confirmations = signatures.flatMap(sig => {
const { v, r, s } = signatureToVRS(sig)
const address = web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`)
return successConfirmations.filter(c => c.validator === address).map(c => ({ ...c, signature: sig }))
})
updateConfirmations(confirmations)
}
}
// get transactions from success signatures
const successConfirmationWithData = await Promise.all(
successConfirmations.map(
getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, startBlock, getSuccessTransactions)
)
)
const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '')
updateConfirmations(successConfirmationWithTxFound)
// If signatures not collected, look for pending transactions
if (!hasEnoughSignatures) {
// Check if confirmation is pending
@@ -84,16 +119,6 @@ export const getConfirmationsForTx = async (
)
updateConfirmations(validatorPendingConfirmations)
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(
@@ -103,13 +128,27 @@ export const getConfirmationsForTx = async (
// Check if confirmation failed
const validatorFailedConfirmationsChecks = await Promise.all(
undefinedConfirmations.map(
getValidatorFailedTransaction(bridgeContract, messageData, startBlock, getFailedTransactions)
getValidatorFailedTransaction(web3, bridgeContract, messageData, startBlock, getFailedTransactions)
)
)
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
let validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED || c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
)
if (hasEnoughSignatures && !fromHome) {
const lastTS = Math.max(...successConfirmationWithTxFound.map(c => c.timestamp || 0))
validatorFailedConfirmations = validatorFailedConfirmations.map(
c =>
c.timestamp < lastTS
? c
: {
...c,
status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
}
)
}
setFailedConfirmations(
!hasEnoughSignatures && validatorFailedConfirmations.some(c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED)
)
setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures)
updateConfirmations(validatorFailedConfirmations)
const missingConfirmations = validatorConfirmations.filter(
@@ -120,21 +159,13 @@ export const getConfirmationsForTx = async (
// If signatures collected, it should set other signatures not found as not required
const notRequiredConfirmations = missingConfirmations.map(c => ({
validator: c.validator,
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED,
timestamp: 0,
txHash: ''
}))
updateConfirmations(notRequiredConfirmations)
}
// get transactions from success signatures
const successConfirmationWithData = await Promise.all(
successConfirmations.map(
getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, startBlock, getSuccessTransactions)
)
)
const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '')
updateConfirmations(successConfirmationWithTxFound)
// retry if not all signatures are collected and some confirmations are still missing
// or some success transactions were not fetched successfully
if (

View File

@@ -59,11 +59,12 @@ export const getSuccessExecutionData = async (
const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
return {
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS,
validator: validatorAddress,
txHash: event.transactionHash,
timestamp: blockTimestamp,
executionResult: event.returnValues.status
executionResult: event.returnValues.status,
blockNumber: event.blockNumber
}
}
return null
@@ -115,7 +116,8 @@ export const getFinalizationEvent = async (
validator: validator,
txHash: pendingTx.hash,
timestamp: nowTimestamp,
executionResult: false
executionResult: false,
blockNumber: 0
})
setPendingExecution(true)
} else {
@@ -144,7 +146,8 @@ export const getFinalizationEvent = async (
validator: validator,
txHash: failedTx.hash,
timestamp,
executionResult: false
executionResult: false,
blockNumber: parseInt(failedTx.blockNumber)
})
setFailedExecution(true)
}

View File

@@ -1,38 +1,45 @@
import Web3 from 'web3'
import { Contract } from 'web3-eth-contract'
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
import validatorsCache from '../services/ValidatorsCache'
import { CACHE_KEY_FAILED, CACHE_KEY_SUCCESS, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { APIPendingTransaction, APITransaction, GetTransactionParams, GetPendingTransactionParams } from './explorer'
import { homeBlockNumberProvider } from '../services/BlockNumberProvider'
import { getAffirmationsSigned, getMessagesSigned } from './contract'
export const getValidatorConfirmation = (
web3: Web3,
hashMsg: string,
bridgeContract: Contract,
confirmationContractMethod: Function
) => async (validator: string): Promise<BasicConfirmationParam> => {
fromHome: boolean
) => async (validator: string): Promise<ConfirmationParam> => {
const hashSenderMsg = web3.utils.soliditySha3Raw(validator, hashMsg)
const signatureFromCache = validatorsCache.get(hashSenderMsg)
if (signatureFromCache) {
return {
validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
}
const fromCache = validatorsCache.getData(hashSenderMsg)
if (fromCache) {
return fromCache
}
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
const confirmed = await confirmationContractMethod(bridgeContract, hashSenderMsg)
const status = confirmed ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
// If validator confirmed signature, we cache the result to avoid doing future requests for a result that won't change
if (confirmed) {
validatorsCache.set(hashSenderMsg, confirmed)
const confirmation: ConfirmationParam = {
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
validator,
timestamp: 0,
txHash: ''
}
validatorsCache.setData(hashSenderMsg, confirmation)
return confirmation
}
return {
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
validator,
status
timestamp: 0,
txHash: ''
}
}
@@ -43,7 +50,7 @@ export const getSuccessExecutionTransaction = (
messageData: string,
startBlock: number,
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
) => async (validatorData: ConfirmationParam): Promise<ConfirmationParam> => {
const { validator } = validatorData
const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}`
const fromCache = validatorsCache.getData(validatorCacheKey)
@@ -87,11 +94,12 @@ export const getSuccessExecutionTransaction = (
}
export const getValidatorFailedTransaction = (
web3: Web3,
bridgeContract: Contract,
messageData: string,
startBlock: number,
getFailedTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
) => async (validatorData: ConfirmationParam): Promise<ConfirmationParam> => {
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}`
const failedFromCache = validatorsCache.getData(validatorCacheKey)
@@ -106,30 +114,33 @@ export const getValidatorFailedTransaction = (
startBlock,
endBlock: homeBlockNumberProvider.get() || 0
})
const newStatus =
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
let txHashTimestamp = 0
let txHash = ''
// If validator signature failed, we cache the result to avoid doing future requests for a result that won't change
if (failedTransactions.length > 0) {
const failedTx = failedTransactions[0]
txHashTimestamp = parseInt(failedTx.timeStamp)
txHash = failedTx.hash
validatorsCache.setData(validatorCacheKey, {
const confirmation: ConfirmationParam = {
status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
validator: validatorData.validator,
status: newStatus,
txHash,
timestamp: txHashTimestamp
})
txHash: failedTx.hash,
timestamp: parseInt(failedTx.timeStamp)
}
if (failedTx.input && failedTx.input.length > 10) {
try {
const res = web3.eth.abi.decodeParameters(['bytes', 'bytes'], `0x${failedTx.input.slice(10)}`)
confirmation.signature = res[0]
confirmation.status = VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
console.log(`Adding manual signature from failed message from ${validatorData.validator}`)
} catch {}
}
validatorsCache.setData(validatorCacheKey, confirmation)
return confirmation
}
return {
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
validator: validatorData.validator,
status: newStatus,
txHash,
timestamp: txHashTimestamp
txHash: '',
timestamp: 0
}
}
@@ -137,7 +148,7 @@ export const getValidatorPendingTransaction = (
bridgeContract: Contract,
messageData: string,
getPendingTransactions: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
) => async (validatorData: ConfirmationParam): Promise<ConfirmationParam> => {
const failedTransactions = await getPendingTransactions({
account: validatorData.validator,
to: bridgeContract.options.address,

View File

@@ -10,6 +10,37 @@ import { SnapshotProvider } from '../services/SnapshotProvider'
export interface MessageObject {
id: string
data: string
sender?: string
executor?: string
obToken?: string
obReceiver?: string
}
export interface WarnRule {
message: string
sender?: string
executor?: string
obToken?: string
obReceiver?: string
}
export const matchesRule = (rule: WarnRule, msg: MessageObject) => {
if (!msg.executor || !msg.sender) {
return false
}
if (!!rule.executor && rule.executor.toLowerCase() !== msg.executor.toLowerCase()) {
return false
}
if (!!rule.sender && rule.sender.toLowerCase() !== msg.sender.toLowerCase()) {
return false
}
if (!!rule.obToken && (!msg.obToken || rule.obToken.toLowerCase() !== msg.obToken.toLowerCase())) {
return false
}
if (!!rule.obReceiver && (!msg.obReceiver || rule.obReceiver.toLowerCase() !== msg.obReceiver.toLowerCase())) {
return false
}
return true
}
const rawGetWeb3 = (url: string) => new Web3(new Web3.providers.HttpProvider(url))
@@ -26,15 +57,33 @@ export const filterEventsByAbi = (
const eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash)
if (!eventAbi || !eventAbi.inputs || !eventAbi.inputs.length) {
return []
}
const inputs = eventAbi.inputs
return events.map(e => {
let decodedLogs: { [p: string]: string } = {
messageId: '',
encodedData: ''
const { messageId, encodedData } = web3.eth.abi.decodeLog(inputs, e.data, [e.topics[1]])
let sender, executor, obToken, obReceiver
if (encodedData.length >= 160) {
sender = `0x${encodedData.slice(66, 106)}`
executor = `0x${encodedData.slice(106, 146)}`
const dataOffset =
160 + (parseInt(encodedData.slice(154, 156), 16) + parseInt(encodedData.slice(156, 158), 16)) * 2 + 8
if (encodedData.length >= dataOffset + 64) {
obToken = `0x${encodedData.slice(dataOffset + 24, dataOffset + 64)}`
}
if (encodedData.length >= dataOffset + 128) {
obReceiver = `0x${encodedData.slice(dataOffset + 88, dataOffset + 128)}`
}
}
if (eventAbi && eventAbi.inputs && eventAbi.inputs.length) {
decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]])
return {
id: messageId || '',
data: encodedData || '',
sender,
executor,
obToken,
obReceiver
}
return { id: decodedLogs.messageId, data: decodedLogs.encodedData }
})
}

View File

@@ -5,7 +5,7 @@ sokol-kovan:
hosts:
127.0.0.1:
ansible_user: ubuntu
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
#syslog_server_port: "udp://127.0.0.1:514"
monitor:
hosts:

View File

@@ -1,6 +1,3 @@
---
bridge_path: "/home/{{ compose_service_user }}/bridge"
ORACLE_ALLOW_HTTP_FOR_RPC: no
ORACLE_QUEUE_URL: amqp://rabbit
ORACLE_REDIS_URL: redis://redis
keyfile_path: "/root/.key"

View File

@@ -2,8 +2,6 @@
- include_tasks: logging_by_syslog.yml
with_items:
- docker-compose
- docker-compose-transfer
- docker-compose-amb
loop_control:
loop_var: file

View File

@@ -1,39 +1,31 @@
---
- name: Get blocks
- name: Get blocks for AMB
become_user: "{{ compose_service_user }}"
shell: docker-compose run --rm --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_affirmation
shell: docker-compose run --rm --entrypoint "node scripts/getValidatorStartBlocks.js" bridge_amb_affirmation
args:
chdir: "{{ bridge_path }}/oracle"
register: BLOCKS
when: (ORACLE_HOME_START_BLOCK is not defined) or (ORACLE_FOREIGN_START_BLOCK is not defined)
register: AMBBLOCKS
when: (AMB_HOME_START_BLOCK is not defined) or (AMB_FOREIGN_START_BLOCK is not defined)
- name: Write blocks
- name: Write blocks for AMB
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)
AMB_HOME_START_BLOCK={{ (AMBBLOCKS.stdout | from_json).homeStartBlock }}
AMB_FOREIGN_START_BLOCK={{ (AMBBLOCKS.stdout | from_json).foreignStartBlock }}
when: (AMB_HOME_START_BLOCK is not defined) or (AMB_FOREIGN_START_BLOCK is not defined)
- name: Get validator address
- name: Get validator address for AMB
become_user: "{{ compose_service_user }}"
shell: docker-compose run --rm -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="{{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_affirmation
shell: docker-compose run --rm -e ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="{{ AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}" --entrypoint "node scripts/privateKeyToAddress.js" bridge_amb_affirmation
args:
chdir: "{{ bridge_path }}/oracle"
register: VADDRESS
register: AMBVADDRESS
- name: Set ORACLE_VALIDATOR_ADDRESS variable
- name: Set AMB_ORACLE_VALIDATOR_ADDRESS variable
set_fact:
ORACLE_VALIDATOR_ADDRESS: "{{ VADDRESS.stdout }}"
- name: Extend docker compose file for erc to native
set_fact: composefileoverride="-f docker-compose-transfer.yml"
when: ORACLE_BRIDGE_MODE == "ERC_TO_NATIVE"
- name: Extend docker compose file for amb
set_fact: composefileoverride="-f docker-compose-amb.yml"
when: ORACLE_BRIDGE_MODE == "ARBITRARY_MESSAGE"
AMB_ORACLE_VALIDATOR_ADDRESS: "{{ AMBVADDRESS.stdout }}"
- name: Install .key config
template:

View File

@@ -12,12 +12,8 @@
owner: "{{ compose_service_user }}"
mode: '0640'
- name: Copy docker-compose files
- name: Copy docker-compose file
copy:
src: ../../../../oracle/{{ item }}
dest: "{{ bridge_path }}/oracle/"
src: ../../../../oracle/docker-compose-amb.yml
dest: "{{ bridge_path }}/oracle/docker-compose.yml"
mode: '0755'
with_items:
- docker-compose.yml
- docker-compose-transfer.yml
- docker-compose-amb.yml

View File

@@ -1,21 +1,26 @@
## 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 }}
AMB_HOME_BRIDGE_ADDRESS={{ AMB_HOME_BRIDGE_ADDRESS }}
ORACLE_HOME_RPC_POLLING_INTERVAL={{ ORACLE_HOME_RPC_POLLING_INTERVAL }}
{% if ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT | default('') != '' %}
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT={{ ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT }}
{% endif %}
## 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 }}
AMB_FOREIGN_BRIDGE_ADDRESS={{ AMB_FOREIGN_BRIDGE_ADDRESS }}
ORACLE_FOREIGN_RPC_POLLING_INTERVAL={{ ORACLE_FOREIGN_RPC_POLLING_INTERVAL }}
{% if ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | default('') != '' %}
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT={{ ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT }}
{% endif %}
{% if ORACLE_TX_REDUNDANCY | default('') != '' %}
ORACLE_TX_REDUNDANCY={{ ORACLE_TX_REDUNDANCY }}
@@ -52,8 +57,8 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
## 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 }}
AMB_QUEUE_URL={{ AMB_QUEUE_URL }}
AMB_REDIS_URL={{ AMB_REDIS_URL }}
{% if ORACLE_FOREIGN_TX_RESEND_INTERVAL | default('') != '' %}
ORACLE_FOREIGN_TX_RESEND_INTERVAL={{ ORACLE_FOREIGN_TX_RESEND_INTERVAL }}
{% endif %}
@@ -61,26 +66,26 @@ ORACLE_FOREIGN_TX_RESEND_INTERVAL={{ ORACLE_FOREIGN_TX_RESEND_INTERVAL }}
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 }}
## Emergency shutdown configuration for AMB
{% if AMB_SHUTDOWN_SERVICE_URL | default('') != '' %}
AMB_SHUTDOWN_SERVICE_URL={{ AMB_SHUTDOWN_SERVICE_URL }}
{% endif %}
{% if ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | default('') != '' %}
ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL={{ ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL }}
{% if AMB_SHUTDOWN_SERVICE_POLLING_INTERVAL | default('') != '' %}
AMB_SHUTDOWN_SERVICE_POLLING_INTERVAL={{ AMB_SHUTDOWN_SERVICE_POLLING_INTERVAL }}
{% endif %}
{% if ORACLE_SIDE_RPC_URL | default('') != '' %}
ORACLE_SIDE_RPC_URL={{ ORACLE_SIDE_RPC_URL }}
{% if AMB_SIDE_RPC_URL | default('') != '' %}
AMB_SIDE_RPC_URL={{ AMB_SIDE_RPC_URL }}
{% endif %}
{% if ORACLE_SHUTDOWN_CONTRACT_ADDRESS | default('') != '' %}
ORACLE_SHUTDOWN_CONTRACT_ADDRESS={{ ORACLE_SHUTDOWN_CONTRACT_ADDRESS }}
{% if AMB_SHUTDOWN_CONTRACT_ADDRESS | default('') != '' %}
AMB_SHUTDOWN_CONTRACT_ADDRESS={{ AMB_SHUTDOWN_CONTRACT_ADDRESS }}
{% endif %}
{% if ORACLE_SHUTDOWN_CONTRACT_METHOD | default('') != '' %}
ORACLE_SHUTDOWN_CONTRACT_METHOD={{ ORACLE_SHUTDOWN_CONTRACT_METHOD }}
{% if AMB_SHUTDOWN_CONTRACT_METHOD | default('') != '' %}
AMB_SHUTDOWN_CONTRACT_METHOD={{ AMB_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 }}
{% if AMB_HOME_START_BLOCK | default('') != '' %}
AMB_HOME_START_BLOCK={{ AMB_HOME_START_BLOCK }}
{% endif %}
{% if AMB_FOREIGN_START_BLOCK | default('') != '' %}
AMB_FOREIGN_START_BLOCK={{ AMB_FOREIGN_START_BLOCK }}
{% endif %}

View File

@@ -1,3 +1,4 @@
## Validator-specific options
ORACLE_VALIDATOR_ADDRESS={{ ORACLE_VALIDATOR_ADDRESS }}
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY={{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}
## Validator-specific options
AMB_ORACLE_VALIDATOR_ADDRESS={{ AMB_ORACLE_VALIDATOR_ADDRESS }}
AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY={{ AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }}

View File

@@ -14,19 +14,18 @@ WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridg
#Getting path to private key file and variable name for parsing key file
keyfile="{{ keyfile_path }}"
vaddr="ORACLE_VALIDATOR_ADDRESS="
vkey="ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="
composefileoverride="{{ composefileoverride | default('') }}"
ambvaddr="AMB_ORACLE_VALIDATOR_ADDRESS="
ambvkey="AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY="
#Parsing file content and add key to variable
while read -r line
do
case $line in
$vaddr*)
vaddr=${line#$vaddr}
$ambvaddr*)
ambvaddr=${line#$ambvaddr}
;;
$vkey*)
vkey=${line#$vkey}
$ambvkey*)
ambvkey=${line#$ambvkey}
;;
esac
done < $keyfile
@@ -34,22 +33,27 @@ done < $keyfile
start(){
echo "Starting bridge.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride rm -fv
sudo -u "{{ compose_service_user }}" ORACLE_VALIDATOR_ADDRESS=$vaddr ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$vkey /usr/local/bin/docker-compose $composefileoverride up --detach
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
sudo -u "{{ compose_service_user }}" \
AMB_ORACLE_VALIDATOR_ADDRESS=$ambvaddr \
AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=$ambvkey \
/usr/local/bin/docker-compose up --detach
}
stop(){
echo "Stopping bridge.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride down -v
sudo -u "{{ compose_service_user }}" \
/usr/local/bin/docker-compose down -v
sleep 2
}
status(){
echo "Bridge status:"
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose $composefileoverride ps
sudo -u "{{ compose_service_user }}" \
/usr/local/bin/docker-compose ps
}

View File

@@ -7,7 +7,15 @@ const {
HOME_AMB_ABI,
FOREIGN_AMB_ABI
} = require('../../commons')
const { web3Home, web3Foreign } = require('../src/services/web3')
const {
web3Home,
web3Foreign,
web3HomeRedundant,
web3HomeFallback,
web3ForeignRedundant,
web3ForeignFallback,
web3ForeignArchive
} = require('../src/services/web3')
const { add0xPrefix, privateKeyToAddress } = require('../src/utils/utils')
const { EXIT_CODES } = require('../src/utils/constants')
@@ -27,9 +35,11 @@ const {
ORACLE_HOME_EVENTS_REPROCESSING,
ORACLE_HOME_EVENTS_REPROCESSING_BATCH_SIZE,
ORACLE_HOME_EVENTS_REPROCESSING_BLOCK_DELAY,
ORACLE_HOME_RPC_SYNC_STATE_CHECK_INTERVAL,
ORACLE_FOREIGN_EVENTS_REPROCESSING,
ORACLE_FOREIGN_EVENTS_REPROCESSING_BATCH_SIZE,
ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY
ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY,
ORACLE_FOREIGN_RPC_SYNC_STATE_CHECK_INTERVAL
} = process.env
let homeAbi
@@ -63,9 +73,12 @@ const homeConfig = {
bridgeAddress: COMMON_HOME_BRIDGE_ADDRESS,
bridgeABI: homeAbi,
pollingInterval: parseInt(ORACLE_HOME_RPC_POLLING_INTERVAL, 10),
syncCheckInterval: parseInt(ORACLE_HOME_RPC_SYNC_STATE_CHECK_INTERVAL, 10) || 60000,
startBlock: parseInt(ORACLE_HOME_START_BLOCK, 10) || 0,
blockPollingLimit: parseInt(ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT, 10),
web3: web3Home,
web3Redundant: web3HomeRedundant,
web3Fallback: web3HomeFallback,
bridgeContract: homeContract,
eventContract: homeContract,
reprocessingOptions: {
@@ -81,9 +94,13 @@ const foreignConfig = {
bridgeAddress: COMMON_FOREIGN_BRIDGE_ADDRESS,
bridgeABI: foreignAbi,
pollingInterval: parseInt(ORACLE_FOREIGN_RPC_POLLING_INTERVAL, 10),
syncCheckInterval: parseInt(ORACLE_FOREIGN_RPC_SYNC_STATE_CHECK_INTERVAL, 10) || 60000,
startBlock: parseInt(ORACLE_FOREIGN_START_BLOCK, 10) || 0,
blockPollingLimit: parseInt(ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT, 10),
web3: web3Foreign,
web3Redundant: web3ForeignRedundant,
web3Fallback: web3ForeignFallback,
web3Archive: web3ForeignArchive || web3Foreign,
bridgeContract: foreignContract,
eventContract: foreignContract,
reprocessingOptions: {

View File

@@ -1,17 +1,14 @@
const baseConfig = require('./base.config')
const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants')
const { web3Foreign, web3ForeignRedundant, web3ForeignFallback } = require('../src/services/web3')
const { ORACLE_FOREIGN_TX_RESEND_INTERVAL } = process.env
module.exports = {
...baseConfig,
main: baseConfig.foreign,
queue: 'foreign-prioritized',
id: 'foreign',
name: 'sender-foreign',
web3: web3Foreign,
web3Redundant: web3ForeignRedundant,
web3Fallback: web3ForeignFallback,
resendInterval: parseInt(ORACLE_FOREIGN_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL
}

View File

@@ -1,17 +1,14 @@
const baseConfig = require('./base.config')
const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants')
const { web3Home, web3HomeRedundant, web3HomeFallback } = require('../src/services/web3')
const { ORACLE_HOME_TX_RESEND_INTERVAL } = process.env
module.exports = {
...baseConfig,
main: baseConfig.home,
queue: 'home-prioritized',
id: 'home',
name: 'sender-home',
web3: web3Home,
web3Redundant: web3HomeRedundant,
web3Fallback: web3HomeFallback,
resendInterval: parseInt(ORACLE_HOME_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL
}

View File

@@ -1,11 +1,9 @@
const baseConfig = require('./base.config')
const { web3ForeignArchive } = require('../src/services/web3')
const id = `${baseConfig.id}-information-request`
module.exports = {
...baseConfig,
web3ForeignArchive: web3ForeignArchive || baseConfig.foreign.web3,
main: baseConfig.home,
event: 'UserRequestForInformation',
sender: 'home',

View File

@@ -1,97 +1,229 @@
---
version: '2.4'
services:
rabbit:
extends:
file: docker-compose.yml
service: rabbit
networks:
- net_rabbit_bridge_information
redis:
extends:
file: docker-compose.yml
service: redis
networks:
- net_db_bridge_information
bridge_request:
extends:
file: docker-compose.yml
service: bridge_request
networks:
- net_db_bridge_request
- net_rabbit_bridge_request
bridge_collected:
extends:
file: docker-compose.yml
service: bridge_collected
networks:
- net_db_bridge_request
- net_rabbit_bridge_request
bridge_affirmation:
extends:
file: docker-compose.yml
service: bridge_affirmation
networks:
- net_db_bridge_request
- net_rabbit_bridge_request
bridge_information:
cpus: 0.1
mem_limit: 500m
image: poanetwork/tokenbridge-oracle:latest
env_file: ./.env
environment:
- NODE_ENV=production
- ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}
restart: unless-stopped
entrypoint: yarn watcher:information-request
networks:
- net_db_bridge_information
- net_rabbit_bridge_information
bridge_senderhome:
extends:
file: docker-compose.yml
service: bridge_senderhome
networks:
- net_db_bridge_request
- net_rabbit_bridge_request
bridge_senderforeign:
extends:
file: docker-compose.yml
service: bridge_senderforeign
networks:
- net_db_bridge_request
- net_rabbit_bridge_request
bridge_shutdown:
extends:
file: docker-compose.yml
service: bridge_shutdown
networks:
- net_db_bridge_shutdown
networks:
net_db_bridge_request:
driver: bridge
net_db_bridge_collected:
driver: bridge
net_db_bridge_affirmation:
driver: bridge
net_db_bridge_information:
driver: bridge
net_db_bridge_senderhome:
driver: bridge
net_db_bridge_senderforeign:
driver: bridge
net_rabbit_bridge_request:
driver: bridge
net_db_bridge_shutdown:
driver: bridge
net_rabbit_bridge_collected:
driver: bridge
net_rabbit_bridge_affirmation:
driver: bridge
net_rabbit_bridge_information:
driver: bridge
net_rabbit_bridge_senderhome:
driver: bridge
net_rabbit_bridge_senderforeign:
driver: bridge
net_db_bridge_amb_green: {driver: bridge}
net_db_bridge_amb_red: {driver: bridge}
net_rabbit_bridge_amb_green: {driver: bridge}
net_rabbit_bridge_amb_red: {driver: bridge}
net_ne_bridge_amb_red: {driver: bridge}
net_ne_bridge_amb_green: {driver: bridge}
services:
bridge_amb_affirmation:
cpus: 0.1
entrypoint: yarn watcher:affirmation-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_FOREIGN_START_BLOCK=${AMB_FOREIGN_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS=${AMB_ORACLE_VALIDATOR_ADDRESS}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_green,
net_rabbit_bridge_amb_green,
net_ne_bridge_amb_green]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
bridge_amb_information:
cpus: 0.1
entrypoint: yarn watcher:information-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_HOME_START_BLOCK=${AMB_HOME_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS=${AMB_ORACLE_VALIDATOR_ADDRESS}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_green,
net_rabbit_bridge_amb_green,
net_ne_bridge_amb_green]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
bridge_amb_request:
cpus: 0.1
entrypoint: yarn watcher:signature-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_HOME_START_BLOCK=${AMB_HOME_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_red,
net_rabbit_bridge_amb_red,
net_ne_bridge_amb_red]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
bridge_amb_shutdown:
cpus: 0.1
entrypoint: yarn manager:shutdown
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL=${AMB_SHUTDOWN_SERVICE_POLLING_INTERVAL}',
'ORACLE_SIDE_RPC_URL=${AMB_SIDE_RPC_URL}',
'ORACLE_SHUTDOWN_CONTRACT_ADDRESS=${AMB_SHUTDOWN_CONTRACT_ADDRESS}',
'ORACLE_SHUTDOWN_CONTRACT_METHOD=${AMB_SHUTDOWN_CONTRACT_METHOD}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_green]
restart: unless-stopped
depends_on:
- redis-amb
bridge_amb_senderhome:
cpus: 0.1
entrypoint: yarn sender:home
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_red,
net_rabbit_bridge_amb_red,
net_ne_bridge_amb_red]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
# bridge_amb_collected:
# cpus: 0.1
# entrypoint: yarn watcher:collected-signatures
# env_file: ./.env
# environment: [NODE_ENV=production,
# ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
# 'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
# 'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
# 'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
# 'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
# 'ORACLE_HOME_START_BLOCK=${AMB_HOME_START_BLOCK}',
# 'ORACLE_ALWAYS_RELAY_SIGNATURES=true'
# 'ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST=/mono/oracle/access-lists/allowance_list.txt',
# 'ORACLE_VALIDATOR_ADDRESS=${ORACLE_VALIDATOR_ADDRESS}']
# image: poanetwork/tokenbridge-oracle:latest
# volumes:
# - '~/amb_bridge_data/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
# logging:
# driver: syslog
# options: {tag: '{{.Name}}/{{.ID}}'}
# mem_limit: 500m
# networks: [net_db_bridge_amb_green,
# net_rabbit_bridge_amb_green,
# net_ne_bridge_amb_green]
# restart: unless-stopped
# bridge_amb_senderforeign:
# cpus: 0.1
# entrypoint: yarn sender:foreign
# env_file: ./.env
# environment: [NODE_ENV=production,
# ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
# 'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
# 'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
# 'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
# 'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
# 'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
# image: poanetwork/tokenbridge-oracle:latest
# logging:
# driver: syslog
# options: {tag: '{{.Name}}/{{.ID}}'}
# mem_limit: 500m
# networks: [net_db_bridge_amb_red,
# net_rabbit_bridge_amb_red]
# restart: unless-stopped
rabbit-amb:
cpus: 0.3
environment: [RABBITMQ_NODENAME=node@rabbit-amb]
hostname: rabbit-amb
image: rabbitmq:3
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_rabbit_bridge_amb_red,
net_rabbit_bridge_amb_green]
restart: unless-stopped
volumes: ['~/amb_bridge_data/rabbitmq:/var/lib/rabbitmq/mnesia']
redis-amb:
command: [redis-server, --appendonly, 'yes']
cpus: 0.1
hostname: redis-amb
image: redis:4
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_red,
net_db_bridge_amb_green]
restart: unless-stopped
volumes: ['~/amb_bridge_data/redis:/data']
ne:
container_name: ne
image: nethermind/nethermind:latest
networks: [net_ne_bridge_amb_green,
net_ne_bridge_amb_red]
command:
--config xdai
--baseDbPath /nethermind
--JsonRpc.Enabled true
--JsonRpc.Host 0.0.0.0
--Init.StoreReceipts false
--Pruning.Mode Hybrid
--Pruning.CacheMb 1024
--Sync.FastSync true
--Sync.FastBlocks true
--Sync.DownloadBodiesInFastSync false
--Sync.DownloadReceiptsInFastSync false
--Sync.DownloadHeadersInFastSync false
volumes:
- ~/ne-data/logs:/nethermind/logs
- ~/ne-data/nethermind_db:/nethermind/nethermind_db
expose:
- "8545"
ports:
- "30304:30303/tcp"
- "30304:30303/udp"
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "1"

View File

@@ -0,0 +1,334 @@
---
version: '2.4'
networks:
net_db_bridge_green: {driver: bridge}
net_db_bridge_red: {driver: bridge}
net_rabbit_bridge_green: {driver: bridge}
net_rabbit_bridge_red: {driver: bridge}
net_ne_bridge_red: {driver: bridge}
net_ne_bridge_green: {driver: bridge}
net_db_bridge_amb_green: {driver: bridge}
net_db_bridge_amb_red: {driver: bridge}
net_rabbit_bridge_amb_green: {driver: bridge}
net_rabbit_bridge_amb_red: {driver: bridge}
net_ne_bridge_amb_red: {driver: bridge}
net_ne_bridge_amb_green: {driver: bridge}
services:
bridge_affirmation:
cpus: 0.1
entrypoint: yarn watcher:affirmation-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ERC_TO_NATIVE,
'COMMON_HOME_BRIDGE_ADDRESS=${TB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${TB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${TB_QUEUE_URL}',
'ORACLE_REDIS_URL=${TB_REDIS_URL}',
'ORACLE_FOREIGN_START_BLOCK=${TB_FOREIGN_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS=${TB_ORACLE_VALIDATOR_ADDRESS}']
image: poanetwork/tokenbridge-oracle:latest
mem_limit: 500m
networks: [net_db_bridge_green,
net_rabbit_bridge_green,
net_ne_bridge_green]
restart: unless-stopped
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
depends_on:
- redis
- rabbit
bridge_shutdown:
cpus: 0.1
mem_limit: 500m
image: poanetwork/tokenbridge-oracle:latest
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ERC_TO_NATIVE,
'ORACLE_REDIS_URL=${TB_REDIS_URL}',
'ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL=${TB_SHUTDOWN_SERVICE_POLLING_INTERVAL}',
'ORACLE_SIDE_RPC_URL=${TB_SIDE_RPC_URL}',
'ORACLE_SHUTDOWN_CONTRACT_ADDRESS=${TB_SHUTDOWN_CONTRACT_ADDRESS}',
'ORACLE_SHUTDOWN_CONTRACT_METHOD=${TB_SHUTDOWN_CONTRACT_METHOD}']
restart: unless-stopped
entrypoint: yarn manager:shutdown
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
networks: [net_db_bridge_green]
depends_on:
- redis
bridge_request:
cpus: 0.1
entrypoint: yarn watcher:signature-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ERC_TO_NATIVE,
'COMMON_HOME_BRIDGE_ADDRESS=${TB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${TB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${TB_QUEUE_URL}',
'ORACLE_REDIS_URL=${TB_REDIS_URL}',
'ORACLE_HOME_START_BLOCK=${TB_HOME_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${TB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_red,
net_rabbit_bridge_red,
net_ne_bridge_red]
restart: unless-stopped
depends_on:
- redis
- rabbit
bridge_senderhome:
cpus: 0.1
entrypoint: yarn sender:home
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ERC_TO_NATIVE,
'COMMON_HOME_BRIDGE_ADDRESS=${TB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${TB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${TB_QUEUE_URL}',
'ORACLE_REDIS_URL=${TB_REDIS_URL}',
'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${TB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
image: poanetwork/tokenbridge-oracle:latest
mem_limit: 500m
networks: [net_db_bridge_red,
net_rabbit_bridge_red,
net_ne_bridge_red]
restart: unless-stopped
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
depends_on:
- redis
- rabbit
bridge_transfer:
cpus: 0.1
entrypoint: yarn watcher:transfer
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ERC_TO_NATIVE,
'COMMON_HOME_BRIDGE_ADDRESS=${TB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${TB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${TB_QUEUE_URL}',
'ORACLE_REDIS_URL=${TB_REDIS_URL}',
'ORACLE_FOREIGN_START_BLOCK=${TB_FOREIGN_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS=${TB_ORACLE_VALIDATOR_ADDRESS}']
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
image: poanetwork/tokenbridge-oracle:latest
mem_limit: 500m
networks: [net_db_bridge_green,
net_rabbit_bridge_green,
net_ne_bridge_green]
restart: unless-stopped
depends_on:
- redis
- rabbit
rabbit:
cpus: 0.3
environment: [RABBITMQ_NODENAME=node@rabbit]
hostname: rabbit
image: rabbitmq:3
mem_limit: 500m
networks: [net_rabbit_bridge_red,
net_rabbit_bridge_green]
restart: unless-stopped
volumes: ['~/bridge_data/rabbitmq:/var/lib/rabbitmq/mnesia']
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
redis:
command: [redis-server, --appendonly, 'yes']
cpus: 0.1
hostname: redis
image: redis:4
mem_limit: 500m
networks: [net_db_bridge_red,
net_db_bridge_green]
restart: unless-stopped
volumes: ['~/bridge_data/redis:/data']
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
ne:
container_name: ne
image: nethermind/nethermind:latest
networks: [net_ne_bridge_green,
net_ne_bridge_red,
net_ne_bridge_amb_green,
net_ne_bridge_amb_red]
command:
--config xdai
--baseDbPath /nethermind
--JsonRpc.Enabled true
--JsonRpc.Host 0.0.0.0
--Init.StoreReceipts false
--TxPool.Size 8192
--Pruning.Mode Hybrid
--Pruning.CacheMb 1024
--Sync.FastSync true
--Sync.FastBlocks true
--Sync.DownloadBodiesInFastSync false
--Sync.DownloadReceiptsInFastSync false
--Sync.DownloadHeadersInFastSync false
volumes:
- ~/ne-data/logs:/nethermind/logs
- ~/ne-data/nethermind_db:/nethermind/nethermind_db
expose:
- "8545"
ports:
- "30304:30303/tcp"
- "30304:30303/udp"
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "1"
bridge_amb_affirmation:
cpus: 0.1
entrypoint: yarn watcher:affirmation-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_FOREIGN_START_BLOCK=${AMB_FOREIGN_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS=${AMB_ORACLE_VALIDATOR_ADDRESS}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_green,
net_rabbit_bridge_amb_green,
net_ne_bridge_amb_green]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
bridge_amb_information:
cpus: 0.1
entrypoint: yarn watcher:information-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_HOME_START_BLOCK=${AMB_HOME_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS=${AMB_ORACLE_VALIDATOR_ADDRESS}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_green,
net_rabbit_bridge_amb_green,
net_ne_bridge_amb_green]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
bridge_amb_request:
cpus: 0.1
entrypoint: yarn watcher:signature-request
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_HOME_START_BLOCK=${AMB_HOME_START_BLOCK}',
'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_red,
net_rabbit_bridge_amb_red,
net_ne_bridge_amb_red]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
bridge_amb_shutdown:
cpus: 0.1
entrypoint: yarn manager:shutdown
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL=${AMB_SHUTDOWN_SERVICE_POLLING_INTERVAL}',
'ORACLE_SIDE_RPC_URL=${AMB_SIDE_RPC_URL}',
'ORACLE_SHUTDOWN_CONTRACT_ADDRESS=${AMB_SHUTDOWN_CONTRACT_ADDRESS}',
'ORACLE_SHUTDOWN_CONTRACT_METHOD=${AMB_SHUTDOWN_CONTRACT_METHOD}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_green]
restart: unless-stopped
depends_on:
- redis-amb
bridge_amb_senderhome:
cpus: 0.1
entrypoint: yarn sender:home
env_file: ./.env
environment: [NODE_ENV=production,
ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE,
'COMMON_HOME_BRIDGE_ADDRESS=${AMB_HOME_BRIDGE_ADDRESS}',
'COMMON_FOREIGN_BRIDGE_ADDRESS=${AMB_FOREIGN_BRIDGE_ADDRESS}',
'ORACLE_QUEUE_URL=${AMB_QUEUE_URL}',
'ORACLE_REDIS_URL=${AMB_REDIS_URL}',
'ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY=${AMB_ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY}']
image: poanetwork/tokenbridge-oracle:latest
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_red,
net_rabbit_bridge_amb_red,
net_ne_bridge_amb_red]
restart: unless-stopped
depends_on:
- redis-amb
- rabbit-amb
rabbit-amb:
cpus: 0.3
environment: [RABBITMQ_NODENAME=node@rabbit-amb]
hostname: rabbit-amb
image: rabbitmq:3
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_rabbit_bridge_amb_red,
net_rabbit_bridge_amb_green]
restart: unless-stopped
volumes: ['~/amb_bridge_data/rabbitmq:/var/lib/rabbitmq/mnesia']
redis-amb:
command: [redis-server, --appendonly, 'yes']
cpus: 0.1
hostname: redis-amb
image: redis:4
logging:
driver: syslog
options: {tag: '{{.Name}}/{{.ID}}'}
mem_limit: 500m
networks: [net_db_bridge_amb_red,
net_db_bridge_amb_green]
restart: unless-stopped
volumes: ['~/amb_bridge_data/redis:/data']

View File

@@ -19,6 +19,7 @@
"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",
"helper:signPendingMessages": "node ./scripts/signPendingMessages.js",
"mev:watcher:collected-signatures": "./scripts/start-worker.sh mevWatcher mev-collected-signatures-watcher",
"mev:sender:foreign": "./scripts/start-worker.sh mevSender foreign-mev-sender",
"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'",

View File

@@ -0,0 +1,83 @@
require('dotenv').config()
const {
COMMON_HOME_BRIDGE_ADDRESS,
COMMON_FOREIGN_BRIDGE_ADDRESS,
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY,
ORACLE_HOME_START_BLOCK,
ORACLE_HOME_END_BLOCK,
ORACLE_BRIDGE_MODE
} = process.env
const fs = require('fs')
const promiseLimit = require('promise-limit')
const { web3Home, web3Foreign } = require('../src/services/web3')
const { getBridgeABIs, getPastEvents, parseAMBMessage, BRIDGE_MODES } = require('../../commons')
const { setLogger } = require('../src/services/injectedLogger')
const mockLogger = { debug: () => {}, info: () => {}, error: () => {}, child: () => mockLogger }
setLogger(mockLogger)
const limit = promiseLimit(50)
const output = process.argv[2]
async function main() {
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(ORACLE_BRIDGE_MODE)
const wallet = web3Home.eth.accounts.wallet.add(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY)
const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS)
const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS)
const fromBlock = parseInt(ORACLE_HOME_START_BLOCK, 10) || 0
let toBlock = parseInt(ORACLE_HOME_END_BLOCK, 10)
if (!toBlock) {
toBlock = await web3Home.eth.getBlockNumber()
}
console.log(`Getting CollectedSignatures events from block ${fromBlock} to block ${toBlock}`)
const events = await getPastEvents(homeBridge, { event: 'CollectedSignatures', fromBlock, toBlock })
console.log(`Found ${events.length} CollectedSignatures events`)
console.log('Getting messages')
let messages = await Promise.all(
events.map((event, i) => () => getMessage(ORACLE_BRIDGE_MODE, homeBridge, foreignBridge, event, i)).map(limit)
)
messages = messages.filter(x => x)
console.log(`Filtered ${messages.length} pending messages`)
const result = {}
messages.forEach(msg => {
result[msg.msgHash] = wallet.sign(msg.message).signature
})
console.log('Writing results')
if (output === '-') {
console.log(JSON.stringify(result))
} else {
fs.writeFileSync(output, JSON.stringify(result))
}
}
async function getMessage(bridgeMode, homeBridge, foreignBridge, event, i) {
if (i % 50 === 0) {
console.log(`Processing event #${i}`)
}
const msgHash = event.returnValues.messageHash
const message = await homeBridge.methods.message(msgHash).call()
let msgId
if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
msgId = parseAMBMessage(message).messageId
} else {
msgId = `0x${message.slice(106, 170)}`
}
const alreadyProcessed = await foreignBridge.methods.relayedMessages(msgId).call()
if (alreadyProcessed) {
return null
}
return {
msgHash,
message
}
}
main()

View File

@@ -35,11 +35,13 @@ Object.keys(asyncCalls).forEach(method => {
})
function processInformationRequestsBuilder(config) {
const { home, foreign, web3ForeignArchive } = config
const { home, foreign } = config
let validatorContract = null
let blockFinder = null
foreign.web3Archive.currentProvider.startSyncStateChecker(foreign.syncCheckInterval)
return async function processInformationRequests(informationRequests) {
const txToSend = []
@@ -49,13 +51,15 @@ function processInformationRequestsBuilder(config) {
if (blockFinder === null) {
rootLogger.debug('Initializing block finder')
blockFinder = await makeBlockFinder('foreign', foreign.web3)
blockFinder = await makeBlockFinder('foreign', foreign.web3Archive)
}
// latest foreign block is requested from an archive RPC, to ensure that it is synced with the network
// block confirmations can be requested from the regular JSON RPC
const foreignBlockNumber =
(await getBlockNumber(foreign.web3)) - (await getRequiredBlockConfirmations(foreign.bridgeContract))
(await getBlockNumber(foreign.web3Archive)) - (await getRequiredBlockConfirmations(foreign.bridgeContract))
const homeBlock = await getBlock(home.web3, informationRequests[0].blockNumber)
const lastForeignBlock = await getBlock(foreign.web3, foreignBlockNumber)
const lastForeignBlock = await getBlock(foreign.web3Archive, foreignBlockNumber)
if (homeBlock.timestamp > lastForeignBlock.timestamp) {
rootLogger.debug(
@@ -85,7 +89,7 @@ function processInformationRequestsBuilder(config) {
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
const call = asyncCalls[asyncCallMethod]
let [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
let [status, result] = await call(foreign.web3Archive, data, foreignClosestBlock).catch(e => {
if (e instanceof HttpListProviderError) {
throw e
}

View File

@@ -32,8 +32,8 @@ if (process.argv.length < 3) {
const config = require(path.join('../config/', process.argv[2]))
const { web3, web3Fallback } = config
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : web3
const { web3, web3Fallback, syncCheckInterval } = config.main
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.main.web3Redundant : web3
const nonceKey = `${config.id}:nonce`
let chainId = 0
@@ -43,6 +43,7 @@ async function initialize() {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
web3.currentProvider.urls.forEach(checkHttps(config.id))
web3.currentProvider.startSyncStateChecker(syncCheckInterval)
GasPrice.start(config.id, web3)

View File

@@ -1,5 +1,6 @@
const fetch = require('node-fetch')
const promiseRetry = require('promise-retry')
const { utils } = require('web3')
const { FALLBACK_RPC_URL_SWITCH_TIMEOUT } = require('../utils/constants')
const { onInjected } = require('./injectedLogger')
@@ -39,19 +40,54 @@ function HttpListProvider(urls, options = {}) {
this.options = { ...defaultOptions, ...options }
this.currentIndex = 0
this.lastTimeUsedPrimary = 0
this.latestBlock = 0
this.syncStateCheckerIntervalId = 0
onInjected(logger => {
this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` })
})
}
HttpListProvider.prototype.switchToFallbackRPC = function() {
if (this.urls.length < 2) {
HttpListProvider.prototype.startSyncStateChecker = function(syncCheckInterval) {
if (this.urls.length > 1 && syncCheckInterval > 0 && this.syncStateCheckerIntervalId === 0) {
this.syncStateCheckerIntervalId = setInterval(this.checkLatestBlock.bind(this), syncCheckInterval)
}
}
HttpListProvider.prototype.checkLatestBlock = function() {
const payload = { jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }
this.send(payload, (error, result) => {
if (error) {
this.logger.warn({ oldBlock: this.latestBlock }, 'Failed to request latest block from all RPC urls')
} else if (result.error) {
this.logger.warn(
{ oldBlock: this.latestBlock, error: result.error.message },
'Failed to make eth_blockNumber request due to unknown error, switching to fallback RPC'
)
this.switchToFallbackRPC()
} else {
const blockNumber = utils.hexToNumber(result.result)
if (blockNumber > this.latestBlock) {
this.logger.debug({ oldBlock: this.latestBlock, newBlock: blockNumber }, 'Updating latest block number')
this.latestBlock = blockNumber
} else {
this.logger.warn(
{ oldBlock: this.latestBlock, newBlock: blockNumber },
'Latest block on the node was not updated since last request, switching to fallback RPC'
)
this.switchToFallbackRPC()
}
}
})
}
HttpListProvider.prototype.switchToFallbackRPC = function(index) {
const prevIndex = this.currentIndex
const newIndex = index || (prevIndex + 1) % this.urls.length
if (this.urls.length < 2 || prevIndex === newIndex) {
return
}
const prevIndex = this.currentIndex
const newIndex = (prevIndex + 1) % this.urls.length
this.logger.info(
{ index: newIndex, oldURL: this.urls[prevIndex], newURL: this.urls[newIndex] },
'Switching to fallback JSON-RPC URL'
@@ -80,11 +116,7 @@ HttpListProvider.prototype.send = async function send(payload, callback) {
// if some of URLs failed to respond, current URL index is updated to the first URL that responded
if (currentIndex !== index) {
this.logger.info(
{ index, oldURL: this.urls[currentIndex], newURL: this.urls[index] },
'Switching to fallback JSON-RPC URL'
)
this.currentIndex = index
this.switchToFallbackRPC(index)
}
callback(null, result)
} catch (e) {

View File

@@ -27,7 +27,6 @@ module.exports = {
MIN_GAS_PRICE_BUMP_FACTOR: 0.1,
DEFAULT_TRANSACTION_RESEND_INTERVAL: 20 * 60 * 1000,
FALLBACK_RPC_URL_SWITCH_TIMEOUT: 60 * 60 * 1000,
BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT: 10,
SENDER_QUEUE_MAX_PRIORITY: 10,
SENDER_QUEUE_SEND_PRIORITY: 5,
SENDER_QUEUE_CHECK_STATUS_PRIORITY: 1,

View File

@@ -169,7 +169,8 @@ function isNonceError(e) {
message.includes('transaction nonce is too low') ||
message.includes('nonce too low') ||
message.includes('transaction with same nonce in the queue') ||
message.includes('oldnonce')
message.includes('oldnonce') ||
message.includes(`the tx doesn't have the correct nonce`)
)
}

View File

@@ -6,11 +6,7 @@ const logger = require('./services/logger')
const { getShutdownFlag } = require('./services/shutdownState')
const { getBlockNumber, getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
const { checkHTTPS, watchdog } = require('./utils/utils')
const {
EXIT_CODES,
BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT,
MAX_HISTORY_BLOCK_TO_REPROCESS
} = require('./utils/constants')
const { EXIT_CODES, MAX_HISTORY_BLOCK_TO_REPROCESS } = require('./utils/constants')
if (process.argv.length < 3) {
logger.error('Please check the number of arguments, config file was not provided')
@@ -38,21 +34,21 @@ const {
pollingInterval,
chain,
reprocessingOptions,
blockPollingLimit
blockPollingLimit,
syncCheckInterval
} = config.main
const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
const lastReprocessedBlockRedisKey = `${config.id}:lastReprocessedBlock`
const seenEventsRedisKey = `${config.id}:seenEvents`
let lastProcessedBlock = Math.max(startBlock - 1, 0)
let lastReprocessedBlock
let lastSeenBlockNumber = 0
let sameBlockNumberCounter = 0
async function initialize() {
try {
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
web3.currentProvider.urls.forEach(checkHttps(chain))
web3.currentProvider.startSyncStateChecker(syncCheckInterval)
await getLastProcessedBlock()
await getLastReprocessedBlock()
@@ -225,28 +221,6 @@ async function getLastBlockToProcess(web3, bridgeContract) {
getBlockNumber(web3),
getRequiredBlockConfirmations(bridgeContract)
])
if (lastBlockNumber < lastSeenBlockNumber) {
sameBlockNumberCounter = 0
logger.warn({ lastBlockNumber, lastSeenBlockNumber }, 'Received block number less than already seen block')
web3.currentProvider.switchToFallbackRPC()
} else if (lastBlockNumber === lastSeenBlockNumber) {
sameBlockNumberCounter++
if (sameBlockNumberCounter > 1) {
logger.info({ lastBlockNumber, sameBlockNumberCounter }, 'Received the same block number more than twice')
if (sameBlockNumberCounter >= BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT) {
sameBlockNumberCounter = 0
logger.warn(
{ lastBlockNumber, n: BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT },
'Received the same block number for too many times. Probably node is not synced anymore'
)
web3.currentProvider.switchToFallbackRPC()
}
}
} else {
sameBlockNumberCounter = 0
lastSeenBlockNumber = lastBlockNumber
}
return lastBlockNumber - requiredBlockConfirmations
}