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
This commit is contained in:
commit
dbaf7feca7
@ -54,7 +54,7 @@ export const ConfirmationsContainer = ({
|
|||||||
home: { name: homeName },
|
home: { name: homeName },
|
||||||
foreign: { name: foreignName }
|
foreign: { name: foreignName }
|
||||||
} = useStateProvider()
|
} = useStateProvider()
|
||||||
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
|
const { requiredSignatures, validatorList } = useValidatorContract(fromHome, receipt ? receipt.blockNumber : 0)
|
||||||
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
|
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
|
||||||
const {
|
const {
|
||||||
confirmations,
|
confirmations,
|
||||||
@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({
|
|||||||
/>
|
/>
|
||||||
{signatureCollected && (
|
{signatureCollected && (
|
||||||
<ExecutionConfirmation
|
<ExecutionConfirmation
|
||||||
messageData={message.data}
|
message={message}
|
||||||
executionData={executionData}
|
executionData={executionData}
|
||||||
isHome={!fromHome}
|
isHome={!fromHome}
|
||||||
signatureCollected={signatureCollected}
|
signatureCollected={signatureCollected}
|
||||||
|
@ -10,13 +10,16 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
|||||||
import { Thead, AgeTd, StatusTd } from './commons/Table'
|
import { Thead, AgeTd, StatusTd } from './commons/Table'
|
||||||
import { ManualExecutionButton } from './ManualExecutionButton'
|
import { ManualExecutionButton } from './ManualExecutionButton'
|
||||||
import { useStateProvider } from '../state/StateProvider'
|
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`
|
const StyledExecutionConfirmation = styled.div`
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export interface ExecutionConfirmationParams {
|
export interface ExecutionConfirmationParams {
|
||||||
messageData: string
|
message: MessageObject
|
||||||
executionData: ExecutionData
|
executionData: ExecutionData
|
||||||
setExecutionData: Function
|
setExecutionData: Function
|
||||||
signatureCollected: boolean | string[]
|
signatureCollected: boolean | string[]
|
||||||
@ -26,7 +29,7 @@ export interface ExecutionConfirmationParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ExecutionConfirmation = ({
|
export const ExecutionConfirmation = ({
|
||||||
messageData,
|
message,
|
||||||
executionData,
|
executionData,
|
||||||
setExecutionData,
|
setExecutionData,
|
||||||
signatureCollected,
|
signatureCollected,
|
||||||
@ -36,6 +39,8 @@ export const ExecutionConfirmation = ({
|
|||||||
}: ExecutionConfirmationParams) => {
|
}: ExecutionConfirmationParams) => {
|
||||||
const { foreign } = useStateProvider()
|
const { foreign } = useStateProvider()
|
||||||
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
|
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
|
||||||
|
const [error, setError] = useState('')
|
||||||
|
const [warning, setWarning] = useState('')
|
||||||
const availableManualExecution =
|
const availableManualExecution =
|
||||||
!isHome &&
|
!isHome &&
|
||||||
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
|
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
|
||||||
@ -67,9 +72,27 @@ export const ExecutionConfirmation = ({
|
|||||||
[availableManualExecution, foreign.bridgeContract]
|
[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 = '') => {
|
const getExecutionStatusElement = (validatorStatus = '') => {
|
||||||
switch (validatorStatus) {
|
switch (validatorStatus) {
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS:
|
||||||
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
|
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
|
||||||
return <RedLabel>{validatorStatus}</RedLabel>
|
return <RedLabel>{validatorStatus}</RedLabel>
|
||||||
@ -87,6 +110,8 @@ export const ExecutionConfirmation = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledExecutionConfirmation>
|
<StyledExecutionConfirmation>
|
||||||
|
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
|
||||||
|
{warning && <WarningAlert onClick={() => setWarning('')} error={warning} />}
|
||||||
<table>
|
<table>
|
||||||
<Thead>
|
<Thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -125,10 +150,11 @@ export const ExecutionConfirmation = ({
|
|||||||
<td>
|
<td>
|
||||||
<ManualExecutionButton
|
<ManualExecutionButton
|
||||||
safeExecutionAvailable={safeExecutionAvailable}
|
safeExecutionAvailable={safeExecutionAvailable}
|
||||||
messageData={messageData}
|
messageData={message.data}
|
||||||
setExecutionData={setExecutionData}
|
setExecutionData={setExecutionData}
|
||||||
signatureCollected={signatureCollected as string[]}
|
signatureCollected={signatureCollected as string[]}
|
||||||
setPendingExecution={setPendingExecution}
|
setPendingExecution={setPendingExecution}
|
||||||
|
setError={setError}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
|
@ -8,7 +8,6 @@ import { TransactionReceipt } from 'web3-eth'
|
|||||||
import { InfoAlert } from './commons/InfoAlert'
|
import { InfoAlert } from './commons/InfoAlert'
|
||||||
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||||
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
|
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
|
||||||
import { ErrorAlert } from './commons/ErrorAlert'
|
|
||||||
|
|
||||||
const StyledMainPage = styled.div`
|
const StyledMainPage = styled.div`
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -52,7 +51,7 @@ export interface FormSubmitParams {
|
|||||||
|
|
||||||
export const MainPage = () => {
|
export const MainPage = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const { home, foreign, error, setError } = useStateProvider()
|
const { home, foreign } = useStateProvider()
|
||||||
const [networkName, setNetworkName] = useState('')
|
const [networkName, setNetworkName] = useState('')
|
||||||
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
||||||
const [showInfoAlert, setShowInfoAlert] = useState(false)
|
const [showInfoAlert, setShowInfoAlert] = useState(false)
|
||||||
@ -132,7 +131,6 @@ export const MainPage = () => {
|
|||||||
</AlertP>
|
</AlertP>
|
||||||
</InfoAlert>
|
</InfoAlert>
|
||||||
)}
|
)}
|
||||||
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
|
|
||||||
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
|
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
|
||||||
<Route
|
<Route
|
||||||
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}
|
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}
|
||||||
|
@ -14,6 +14,7 @@ import { useStateProvider } from '../state/StateProvider'
|
|||||||
import { signatureToVRS, packSignatures } from '../utils/signatures'
|
import { signatureToVRS, packSignatures } from '../utils/signatures'
|
||||||
import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
|
import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
|
||||||
import { TransactionReceipt } from 'web3-eth'
|
import { TransactionReceipt } from 'web3-eth'
|
||||||
|
import { useValidatorContract } from '../hooks/useValidatorContract'
|
||||||
|
|
||||||
const ActionButton = styled.button`
|
const ActionButton = styled.button`
|
||||||
color: var(--button-color);
|
color: var(--button-color);
|
||||||
@ -32,6 +33,7 @@ interface ManualExecutionButtonParams {
|
|||||||
setExecutionData: Function
|
setExecutionData: Function
|
||||||
signatureCollected: string[]
|
signatureCollected: string[]
|
||||||
setPendingExecution: Function
|
setPendingExecution: Function
|
||||||
|
setError: Function
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ManualExecutionButton = ({
|
export const ManualExecutionButton = ({
|
||||||
@ -39,13 +41,82 @@ export const ManualExecutionButton = ({
|
|||||||
messageData,
|
messageData,
|
||||||
setExecutionData,
|
setExecutionData,
|
||||||
signatureCollected,
|
signatureCollected,
|
||||||
setPendingExecution
|
setPendingExecution,
|
||||||
|
setError
|
||||||
}: ManualExecutionButtonParams) => {
|
}: ManualExecutionButtonParams) => {
|
||||||
const { foreign, setError } = useStateProvider()
|
const { foreign } = useStateProvider()
|
||||||
const { library, activate, account, active } = useWeb3React()
|
const { library, activate, account, active } = useWeb3React()
|
||||||
const [manualExecution, setManualExecution] = useState(false)
|
const [manualExecution, setManualExecution] = useState(false)
|
||||||
const [allowFailures, setAllowFailures] = useState(false)
|
const [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[]>([])
|
||||||
|
|
||||||
|
const { requiredSignatures, validatorList } = useValidatorContract(false, 'latest')
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if (
|
||||||
|
!foreign.bridgeContract ||
|
||||||
|
!foreign.web3 ||
|
||||||
|
!signatureCollected ||
|
||||||
|
!signatureCollected.length ||
|
||||||
|
!requiredSignatures ||
|
||||||
|
!validatorList ||
|
||||||
|
!validatorList.length
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
const signatures = []
|
||||||
|
const remainingValidators = Object.fromEntries(validatorList.map(validator => [validator, true]))
|
||||||
|
for (let i = 0; i < signatureCollected.length && signatures.length < requiredSignatures; i++) {
|
||||||
|
const { v, r, s } = signatureToVRS(signatureCollected[i])
|
||||||
|
const signer = foreign.web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`)
|
||||||
|
if (validatorList.includes(signer)) {
|
||||||
|
delete remainingValidators[signer]
|
||||||
|
signatures.push(signatureCollected[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signatures.length < requiredSignatures) {
|
||||||
|
console.log('On-chain collected signatures are not enough for message execution')
|
||||||
|
const manualValidators = Object.keys(remainingValidators)
|
||||||
|
const msgHash = foreign.web3.utils.sha3(messageData)!
|
||||||
|
for (let i = 0; i < manualValidators.length && signatures.length < requiredSignatures; i++) {
|
||||||
|
try {
|
||||||
|
const overrideSignatures: {
|
||||||
|
[key: string]: string
|
||||||
|
} = require(`../snapshots/signatures_${manualValidators[i]}.json`)
|
||||||
|
if (overrideSignatures[msgHash]) {
|
||||||
|
console.log(`Adding manual signature from ${manualValidators[i]}`)
|
||||||
|
signatures.push(overrideSignatures[msgHash])
|
||||||
|
} else {
|
||||||
|
console.log(`No manual signature from ${manualValidators[i]} was found`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Signatures overrides are not present for ${manualValidators[i]}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signatures.length >= requiredSignatures) {
|
||||||
|
setValidSignatures(signatures)
|
||||||
|
setTitle('Execute')
|
||||||
|
setReady(true)
|
||||||
|
} else {
|
||||||
|
setTitle('Unavailable')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
foreign.bridgeContract,
|
||||||
|
foreign.web3,
|
||||||
|
signatureCollected,
|
||||||
|
validatorList,
|
||||||
|
requiredSignatures,
|
||||||
|
messageData,
|
||||||
|
setValidSignatures
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
@ -73,9 +144,9 @@ export const ManualExecutionButton = ({
|
|||||||
return
|
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 messageId = messageData.slice(0, 66)
|
||||||
const bridge = foreign.bridgeContract
|
const bridge = foreign.bridgeContract
|
||||||
const executeMethod =
|
const executeMethod =
|
||||||
@ -140,19 +211,20 @@ export const ManualExecutionButton = ({
|
|||||||
foreign.bridgeContract,
|
foreign.bridgeContract,
|
||||||
setError,
|
setError,
|
||||||
messageData,
|
messageData,
|
||||||
signatureCollected,
|
|
||||||
setExecutionData,
|
setExecutionData,
|
||||||
setPendingExecution,
|
setPendingExecution,
|
||||||
safeExecutionAvailable,
|
safeExecutionAvailable,
|
||||||
allowFailures
|
allowFailures,
|
||||||
|
foreign.web3,
|
||||||
|
validSignatures
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="is-center">
|
<div className="is-center">
|
||||||
<ActionButton disabled={notReady} className="button outline" onClick={() => setManualExecution(true)}>
|
<ActionButton disabled={!ready} className="button outline" onClick={() => setManualExecution(true)}>
|
||||||
Execute
|
{title}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
{safeExecutionAvailable && (
|
{safeExecutionAvailable && (
|
||||||
|
@ -94,7 +94,7 @@ export const ValidatorsConfirmations = ({
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<RequiredConfirmations>
|
<RequiredConfirmations>
|
||||||
{requiredSignatures} of {validatorList.length} confirmations required
|
At least <strong>{requiredSignatures}</strong> of <strong>{validatorList.length}</strong> confirmations required
|
||||||
</RequiredConfirmations>
|
</RequiredConfirmations>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -33,7 +33,7 @@ export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: str
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="row is-center">
|
<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)" />
|
<InfoIcon color="var(--failed-color)" />
|
||||||
<TextContainer className="col-10">
|
<TextContainer className="col-10">
|
||||||
{text}
|
{text}
|
||||||
|
34
alm/src/components/commons/WarningAlert.tsx
Normal file
34
alm/src/components/commons/WarningAlert.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -54,7 +54,8 @@ export const CONFIRMATIONS_STATUS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const VALIDATOR_CONFIRMATION_STATUS = {
|
export const VALIDATOR_CONFIRMATION_STATUS = {
|
||||||
SUCCESS: 'Success',
|
SUCCESS: 'Confirmed',
|
||||||
|
EXECUTION_SUCCESS: 'Executed',
|
||||||
FAILED: 'Failed',
|
FAILED: 'Failed',
|
||||||
PENDING: 'Pending',
|
PENDING: 'Pending',
|
||||||
WAITING: 'Waiting',
|
WAITING: 'Waiting',
|
||||||
|
@ -343,7 +343,10 @@ export const useMessageConfirmations = ({
|
|||||||
// Sets the message status based in the collected information
|
// Sets the message status based in the collected information
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS && existsConfirmation(confirmations)) {
|
if (
|
||||||
|
executionData.status === VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS &&
|
||||||
|
existsConfirmation(confirmations)
|
||||||
|
) {
|
||||||
const newStatus = executionData.executionResult
|
const newStatus = executionData.executionResult
|
||||||
? CONFIRMATIONS_STATUS.SUCCESS
|
? CONFIRMATIONS_STATUS.SUCCESS
|
||||||
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
|
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
|
||||||
|
@ -4,19 +4,13 @@ import Web3 from 'web3'
|
|||||||
import { getRequiredSignatures, getValidatorAddress, getValidatorList } from '../utils/contract'
|
import { getRequiredSignatures, getValidatorAddress, getValidatorList } from '../utils/contract'
|
||||||
import { BRIDGE_VALIDATORS_ABI } from '../abis'
|
import { BRIDGE_VALIDATORS_ABI } from '../abis'
|
||||||
import { useStateProvider } from '../state/StateProvider'
|
import { useStateProvider } from '../state/StateProvider'
|
||||||
import { TransactionReceipt } from 'web3-eth'
|
|
||||||
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider'
|
||||||
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
||||||
|
|
||||||
export interface useValidatorContractParams {
|
export const useValidatorContract = (isHome: boolean, blockNumber: number | 'latest') => {
|
||||||
fromHome: boolean
|
|
||||||
receipt: Maybe<TransactionReceipt>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useValidatorContract = ({ receipt, fromHome }: useValidatorContractParams) => {
|
|
||||||
const [validatorContract, setValidatorContract] = useState<Maybe<Contract>>(null)
|
const [validatorContract, setValidatorContract] = useState<Maybe<Contract>>(null)
|
||||||
const [requiredSignatures, setRequiredSignatures] = useState(0)
|
const [requiredSignatures, setRequiredSignatures] = useState(0)
|
||||||
const [validatorList, setValidatorList] = useState([])
|
const [validatorList, setValidatorList] = useState<string[]>([])
|
||||||
|
|
||||||
const { home, foreign } = useStateProvider()
|
const { home, foreign } = useStateProvider()
|
||||||
|
|
||||||
@ -29,34 +23,34 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
|
|||||||
|
|
||||||
const callRequiredSignatures = async (
|
const callRequiredSignatures = async (
|
||||||
contract: Maybe<Contract>,
|
contract: Maybe<Contract>,
|
||||||
receipt: TransactionReceipt,
|
blockNumber: number | 'latest',
|
||||||
setResult: Function,
|
setResult: Function,
|
||||||
snapshotProvider: SnapshotProvider,
|
snapshotProvider: SnapshotProvider,
|
||||||
web3: Web3,
|
web3: Web3,
|
||||||
api: string
|
api: string
|
||||||
) => {
|
) => {
|
||||||
if (!contract) return
|
if (!contract) return
|
||||||
const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
const result = await getRequiredSignatures(contract, blockNumber, snapshotProvider, web3, api)
|
||||||
setResult(result)
|
setResult(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
const callValidatorList = async (
|
const callValidatorList = async (
|
||||||
contract: Maybe<Contract>,
|
contract: Maybe<Contract>,
|
||||||
receipt: TransactionReceipt,
|
blockNumber: number | 'latest',
|
||||||
setResult: Function,
|
setResult: Function,
|
||||||
snapshotProvider: SnapshotProvider,
|
snapshotProvider: SnapshotProvider,
|
||||||
web3: Web3,
|
web3: Web3,
|
||||||
api: string
|
api: string
|
||||||
) => {
|
) => {
|
||||||
if (!contract) return
|
if (!contract) return
|
||||||
const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider, web3, api)
|
const result = await getValidatorList(contract, blockNumber, snapshotProvider, web3, api)
|
||||||
setResult(result)
|
setResult(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
const web3 = fromHome ? home.web3 : foreign.web3
|
const web3 = isHome ? home.web3 : foreign.web3
|
||||||
const api = fromHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
|
const api = isHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
|
||||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
const bridgeContract = isHome ? home.bridgeContract : foreign.bridgeContract
|
||||||
const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider
|
const snapshotProvider = isHome ? homeSnapshotProvider : foreignSnapshotProvider
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
@ -68,11 +62,11 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
|
|||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
if (!web3 || !receipt) return
|
if (!web3 || !blockNumber) return
|
||||||
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider, web3, api)
|
callRequiredSignatures(validatorContract, blockNumber, setRequiredSignatures, snapshotProvider, web3, api)
|
||||||
callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider, web3, api)
|
callValidatorList(validatorContract, blockNumber, setValidatorList, snapshotProvider, web3, api)
|
||||||
},
|
},
|
||||||
[validatorContract, receipt, web3, snapshotProvider, api]
|
[validatorContract, blockNumber, web3, snapshotProvider, api]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, ReactNode, useState } from 'react'
|
import React, { createContext, ReactNode } from 'react'
|
||||||
import { useNetwork } from '../hooks/useNetwork'
|
import { useNetwork } from '../hooks/useNetwork'
|
||||||
import {
|
import {
|
||||||
HOME_RPC_URL,
|
HOME_RPC_URL,
|
||||||
@ -25,8 +25,6 @@ export interface StateContext {
|
|||||||
home: BaseNetworkParams
|
home: BaseNetworkParams
|
||||||
foreign: BaseNetworkParams
|
foreign: BaseNetworkParams
|
||||||
loading: boolean
|
loading: boolean
|
||||||
error: string
|
|
||||||
setError: Function
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@ -44,9 +42,7 @@ const initialState = {
|
|||||||
bridgeAddress: FOREIGN_BRIDGE_ADDRESS,
|
bridgeAddress: FOREIGN_BRIDGE_ADDRESS,
|
||||||
bridgeContract: null
|
bridgeContract: null
|
||||||
},
|
},
|
||||||
loading: true,
|
loading: true
|
||||||
error: '',
|
|
||||||
setError: () => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const StateContext = createContext<StateContext>(initialState)
|
const StateContext = createContext<StateContext>(initialState)
|
||||||
@ -58,7 +54,6 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
homeWeb3: homeNetwork.web3,
|
homeWeb3: homeNetwork.web3,
|
||||||
foreignWeb3: foreignNetwork.web3
|
foreignWeb3: foreignNetwork.web3
|
||||||
})
|
})
|
||||||
const [error, setError] = useState('')
|
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
home: {
|
home: {
|
||||||
@ -73,9 +68,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
bridgeContract: foreignBridge,
|
bridgeContract: foreignBridge,
|
||||||
...foreignNetwork
|
...foreignNetwork
|
||||||
},
|
},
|
||||||
loading: homeNetwork.loading || foreignNetwork.loading,
|
loading: homeNetwork.loading || foreignNetwork.loading
|
||||||
error,
|
|
||||||
setError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <StateContext.Provider value={value}>{children}</StateContext.Provider>
|
return <StateContext.Provider value={value}>{children}</StateContext.Provider>
|
||||||
|
@ -28,5 +28,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
|
|||||||
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
||||||
--failed-color: ${props => props.theme.failed.textColor};
|
--failed-color: ${props => props.theme.failed.textColor};
|
||||||
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
|
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
|
||||||
|
--warning-color: ${props => props.theme.warning.textColor};
|
||||||
|
--warning-bg-color: ${props => props.theme.warning.backgroundColor};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -17,6 +17,10 @@ const theme = {
|
|||||||
failed: {
|
failed: {
|
||||||
textColor: '#de4437',
|
textColor: '#de4437',
|
||||||
backgroundColor: 'rgba(222,68,55,.1)'
|
backgroundColor: 'rgba(222,68,55,.1)'
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
textColor: '#ffa758',
|
||||||
|
backgroundColor: 'rgba(222,68,55,.1)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default theme
|
export default theme
|
||||||
|
@ -281,9 +281,9 @@ describe('getConfirmationsForTx', () => {
|
|||||||
)
|
)
|
||||||
expect(res2).toEqual(
|
expect(res2).toEqual(
|
||||||
expect.arrayContaining([
|
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.SUCCESS },
|
{ 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.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res3).toEqual(
|
expect(res3).toEqual(
|
||||||
@ -382,25 +382,25 @@ describe('getConfirmationsForTx', () => {
|
|||||||
)
|
)
|
||||||
expect(res2).toEqual(
|
expect(res2).toEqual(
|
||||||
expect.arrayContaining([
|
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.SUCCESS },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
|
||||||
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res3).toEqual(
|
expect(res3).toEqual(
|
||||||
expect.arrayContaining([
|
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.SUCCESS },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
|
||||||
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res4).toEqual(
|
expect(res4).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, 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: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 },
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 },
|
||||||
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -492,15 +492,15 @@ describe('getConfirmationsForTx', () => {
|
|||||||
)
|
)
|
||||||
expect(res2).toEqual(
|
expect(res2).toEqual(
|
||||||
expect.arrayContaining([
|
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: 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(res3).toEqual(
|
||||||
expect.arrayContaining([
|
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.FAILED, 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.PENDING, txHash: '0x123', timestamp: 123 }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@ -596,9 +596,9 @@ describe('getConfirmationsForTx', () => {
|
|||||||
)
|
)
|
||||||
expect(res2).toEqual(
|
expect(res2).toEqual(
|
||||||
expect.arrayContaining([
|
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.FAILED, txHash: '0x123', timestamp: 123 },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res3).toEqual(
|
expect(res3).toEqual(
|
||||||
@ -610,9 +610,13 @@ describe('getConfirmationsForTx', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
test('should remove pending state after transaction mined', async () => {
|
test('should remove pending state after transaction mined', async () => {
|
||||||
// Validator1 success
|
const validator4 = '0x9d2dC11C342F4eF3C5491A048D0f0eBCd2D8f7C3'
|
||||||
// Validator2 failed
|
const validatorList = [validator1, validator2, validator3, validator4]
|
||||||
// Validator3 Pending
|
|
||||||
|
// Validator1 success (ts=100)
|
||||||
|
// Validator2 failed (ts=200)
|
||||||
|
// Validator3 Pending (ts=300)
|
||||||
|
// Validator4 Excess confirmation (Failed) (ts=400)
|
||||||
|
|
||||||
getValidatorConfirmation
|
getValidatorConfirmation
|
||||||
.mockImplementationOnce(() => async (validator: string) => ({
|
.mockImplementationOnce(() => async (validator: string) => ({
|
||||||
@ -623,30 +627,44 @@ describe('getConfirmationsForTx', () => {
|
|||||||
.mockImplementation(() => async (validator: string) => ({
|
.mockImplementation(() => async (validator: string) => ({
|
||||||
validator,
|
validator,
|
||||||
status:
|
status:
|
||||||
validator !== validator2 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
validator === validator1 || validator === validator3
|
||||||
|
? VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||||
|
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
}))
|
}))
|
||||||
getSuccessExecutionTransaction
|
getSuccessExecutionTransaction
|
||||||
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
validator: validatorData.validator,
|
validator: validatorData.validator,
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
txHash: validatorData.validator === validator1 ? '0x123' : '',
|
txHash: validatorData.validator === validator1 ? '0x100' : '',
|
||||||
timestamp: validatorData.validator === validator1 ? 123 : 0
|
timestamp: validatorData.validator === validator1 ? 100 : 0
|
||||||
}))
|
}))
|
||||||
.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
validator: validatorData.validator,
|
validator: validatorData.validator,
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||||
txHash: validatorData.validator !== validator2 ? '0x123' : '',
|
txHash:
|
||||||
timestamp: validatorData.validator !== validator2 ? 123 : 0
|
validatorData.validator === validator1 ? '0x100' : validatorData.validator === validator3 ? '0x300' : '',
|
||||||
|
timestamp: validatorData.validator === validator1 ? 100 : validatorData.validator === validator3 ? 300 : ''
|
||||||
|
}))
|
||||||
|
getValidatorFailedTransaction
|
||||||
|
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
|
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: BasicConfirmationParam) => ({
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status:
|
||||||
|
validatorData.validator === validator2 || validatorData.validator === validator4
|
||||||
|
? VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||||
|
: 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
|
getValidatorPendingTransaction
|
||||||
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
validator: validatorData.validator,
|
validator: validatorData.validator,
|
||||||
@ -654,8 +672,8 @@ describe('getConfirmationsForTx', () => {
|
|||||||
validatorData.validator === validator3
|
validatorData.validator === validator3
|
||||||
? VALIDATOR_CONFIRMATION_STATUS.PENDING
|
? VALIDATOR_CONFIRMATION_STATUS.PENDING
|
||||||
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
txHash: validatorData.validator === validator3 ? '0x123' : '',
|
txHash: validatorData.validator === validator3 ? '0x300' : '',
|
||||||
timestamp: validatorData.validator === validator3 ? 123 : 0
|
timestamp: validatorData.validator === validator3 ? 300 : 0
|
||||||
}))
|
}))
|
||||||
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({
|
||||||
validator: validatorData.validator,
|
validator: validatorData.validator,
|
||||||
@ -712,28 +730,32 @@ describe('getConfirmationsForTx', () => {
|
|||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
|
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
|
||||||
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED },
|
{ 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(res2).toEqual(
|
||||||
expect.arrayContaining([
|
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: 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(res3).toEqual(
|
||||||
expect.arrayContaining([
|
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.FAILED, 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.PENDING, txHash: '0x300', timestamp: 300 },
|
||||||
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res4).toEqual(
|
expect(res4).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ validator: validator1, 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: '0x123', timestamp: 123 },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x300', timestamp: 300 },
|
||||||
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -781,23 +803,26 @@ describe('getConfirmationsForTx', () => {
|
|||||||
const res7 = setResult.mock.calls[6][0](res6)
|
const res7 = setResult.mock.calls[6][0](res6)
|
||||||
expect(res5).toEqual(
|
expect(res5).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ validator: validator1, 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: '0x123', timestamp: 123 },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS },
|
||||||
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res6).toEqual(
|
expect(res6).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ validator: validator1, 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: '0x123', timestamp: 123 },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 },
|
||||||
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
expect(res7).toEqual(
|
expect(res7).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ validator: validator1, 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: '0x123', timestamp: 123 },
|
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
|
||||||
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }
|
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 },
|
||||||
|
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x400', timestamp: 400 }
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -84,7 +84,7 @@ describe('getFinalizationEvent', () => {
|
|||||||
expect(setResult).toBeCalledTimes(1)
|
expect(setResult).toBeCalledTimes(1)
|
||||||
expect(setResult.mock.calls[0][0]).toEqual({
|
expect(setResult.mock.calls[0][0]).toEqual({
|
||||||
validator: validator1,
|
validator: validator1,
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS,
|
||||||
txHash,
|
txHash,
|
||||||
timestamp,
|
timestamp,
|
||||||
executionResult: true
|
executionResult: true
|
||||||
|
@ -52,11 +52,15 @@ export const getValidatorAddress = (contract: Contract) => contract.methods.vali
|
|||||||
|
|
||||||
export const getRequiredSignatures = async (
|
export const getRequiredSignatures = async (
|
||||||
contract: Contract,
|
contract: Contract,
|
||||||
blockNumber: number,
|
blockNumber: number | 'latest',
|
||||||
snapshotProvider: SnapshotProvider,
|
snapshotProvider: SnapshotProvider,
|
||||||
web3: Web3 | null = null,
|
web3: Web3 | null = null,
|
||||||
api: string = ''
|
api: string = ''
|
||||||
) => {
|
) => {
|
||||||
|
if (blockNumber === 'latest') {
|
||||||
|
return contract.methods.requiredSignatures().call()
|
||||||
|
}
|
||||||
|
|
||||||
const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber)
|
const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber)
|
||||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||||
|
|
||||||
@ -78,11 +82,15 @@ export const getRequiredSignatures = async (
|
|||||||
|
|
||||||
export const getValidatorList = async (
|
export const getValidatorList = async (
|
||||||
contract: Contract,
|
contract: Contract,
|
||||||
blockNumber: number,
|
blockNumber: number | 'latest',
|
||||||
snapshotProvider: SnapshotProvider,
|
snapshotProvider: SnapshotProvider,
|
||||||
web3: Web3 | null = null,
|
web3: Web3 | null = null,
|
||||||
api: string = ''
|
api: string = ''
|
||||||
) => {
|
) => {
|
||||||
|
if (blockNumber === 'latest') {
|
||||||
|
return contract.methods.validatorList().call()
|
||||||
|
}
|
||||||
|
|
||||||
const addedEventsFromSnapshot = snapshotProvider.validatorAddedEvents(blockNumber)
|
const addedEventsFromSnapshot = snapshotProvider.validatorAddedEvents(blockNumber)
|
||||||
const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber)
|
const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber)
|
||||||
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber()
|
||||||
|
@ -72,6 +72,28 @@ export const getConfirmationsForTx = async (
|
|||||||
updateConfirmations(validatorConfirmations)
|
updateConfirmations(validatorConfirmations)
|
||||||
setSignatureCollected(hasEnoughSignatures)
|
setSignatureCollected(hasEnoughSignatures)
|
||||||
|
|
||||||
|
if (hasEnoughSignatures) {
|
||||||
|
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())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 signatures not collected, look for pending transactions
|
||||||
if (!hasEnoughSignatures) {
|
if (!hasEnoughSignatures) {
|
||||||
// Check if confirmation is pending
|
// Check if confirmation is pending
|
||||||
@ -84,16 +106,6 @@ export const getConfirmationsForTx = async (
|
|||||||
)
|
)
|
||||||
updateConfirmations(validatorPendingConfirmations)
|
updateConfirmations(validatorPendingConfirmations)
|
||||||
setPendingConfirmations(validatorPendingConfirmations.length > 0)
|
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(
|
const undefinedConfirmations = validatorConfirmations.filter(
|
||||||
@ -106,9 +118,21 @@ export const getConfirmationsForTx = async (
|
|||||||
getValidatorFailedTransaction(bridgeContract, messageData, startBlock, getFailedTransactions)
|
getValidatorFailedTransaction(bridgeContract, messageData, startBlock, getFailedTransactions)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
|
let validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
|
||||||
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
|
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||||
)
|
)
|
||||||
|
if (hasEnoughSignatures) {
|
||||||
|
const lastTS = Math.max(...successConfirmationWithTxFound.map(c => c.timestamp || 0))
|
||||||
|
validatorFailedConfirmations = validatorFailedConfirmations.map(
|
||||||
|
c =>
|
||||||
|
c.timestamp < lastTS
|
||||||
|
? c
|
||||||
|
: {
|
||||||
|
...c,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures)
|
setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures)
|
||||||
updateConfirmations(validatorFailedConfirmations)
|
updateConfirmations(validatorFailedConfirmations)
|
||||||
|
|
||||||
@ -125,16 +149,6 @@ export const getConfirmationsForTx = async (
|
|||||||
updateConfirmations(notRequiredConfirmations)
|
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
|
// retry if not all signatures are collected and some confirmations are still missing
|
||||||
// or some success transactions were not fetched successfully
|
// or some success transactions were not fetched successfully
|
||||||
if (
|
if (
|
||||||
|
@ -59,7 +59,7 @@ export const getSuccessExecutionData = async (
|
|||||||
const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
|
const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS,
|
||||||
validator: validatorAddress,
|
validator: validatorAddress,
|
||||||
txHash: event.transactionHash,
|
txHash: event.transactionHash,
|
||||||
timestamp: blockTimestamp,
|
timestamp: blockTimestamp,
|
||||||
|
@ -10,6 +10,37 @@ import { SnapshotProvider } from '../services/SnapshotProvider'
|
|||||||
export interface MessageObject {
|
export interface MessageObject {
|
||||||
id: string
|
id: string
|
||||||
data: 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))
|
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 eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
|
||||||
const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash)
|
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 => {
|
return events.map(e => {
|
||||||
let decodedLogs: { [p: string]: string } = {
|
const { messageId, encodedData } = web3.eth.abi.decodeLog(inputs, e.data, [e.topics[1]])
|
||||||
messageId: '',
|
let sender, executor, obToken, obReceiver
|
||||||
encodedData: ''
|
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) {
|
return {
|
||||||
decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]])
|
id: messageId || '',
|
||||||
|
data: encodedData || '',
|
||||||
|
sender,
|
||||||
|
executor,
|
||||||
|
obToken,
|
||||||
|
obReceiver
|
||||||
}
|
}
|
||||||
return { id: decodedLogs.messageId, data: decodedLogs.encodedData }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"confirm:information-request": "./scripts/start-worker.sh confirmRelay information-request-watcher",
|
"confirm:information-request": "./scripts/start-worker.sh confirmRelay information-request-watcher",
|
||||||
"manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager",
|
"manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager",
|
||||||
"helper:interestFether": "node ./scripts/interestFetcher.js",
|
"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:watcher:collected-signatures": "./scripts/start-worker.sh mevWatcher mev-collected-signatures-watcher",
|
||||||
"mev:sender:foreign": "./scripts/start-worker.sh mevSender foreign-mev-sender",
|
"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'",
|
"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'",
|
||||||
|
83
oracle/scripts/signPendingMessages.js
Normal file
83
oracle/scripts/signPendingMessages.js
Normal 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()
|
Loading…
Reference in New Issue
Block a user