ALM: show manually added signatures (#653)

This commit is contained in:
Kirill Fedoseev 2022-04-29 15:38:08 +02:00 committed by GitHub
parent 4af882b0ff
commit 16f3e9add6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 291 additions and 208 deletions

@ -1,9 +1,9 @@
import React from 'react' import React, { useEffect, useState } from 'react'
import { TransactionReceipt } from 'web3-eth' import { TransactionReceipt } from 'web3-eth'
import { useMessageConfirmations } from '../hooks/useMessageConfirmations' import { useMessageConfirmations } from '../hooks/useMessageConfirmations'
import { MessageObject } from '../utils/web3' import { MessageObject } from '../utils/web3'
import styled from 'styled-components' 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 { CONFIRMATIONS_STATUS_LABEL, CONFIRMATIONS_STATUS_LABEL_HOME } from '../config/descriptions'
import { SimpleLoading } from './commons/Loading' import { SimpleLoading } from './commons/Loading'
import { ValidatorsConfirmations } from './ValidatorsConfirmations' import { ValidatorsConfirmations } from './ValidatorsConfirmations'
@ -54,7 +54,9 @@ export const ConfirmationsContainer = ({
home: { name: homeName }, home: { name: homeName },
foreign: { name: foreignName } foreign: { name: foreignName }
} = useStateProvider() } = useStateProvider()
const { requiredSignatures, validatorList } = useValidatorContract(fromHome, receipt ? receipt.blockNumber : 0) 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 { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
const { const {
confirmations, confirmations,
@ -71,11 +73,21 @@ export const ConfirmationsContainer = ({
fromHome, fromHome,
homeStartBlock, homeStartBlock,
foreignStartBlock, foreignStartBlock,
requiredSignatures, requiredSignatures: src.requiredSignatures,
validatorList, validatorList: src.validatorList,
targetValidatorList: dst.validatorList,
blockConfirmations 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 statusLabel = fromHome ? CONFIRMATIONS_STATUS_LABEL_HOME : CONFIRMATIONS_STATUS_LABEL
const parseDescription = () => { const parseDescription = () => {
@ -114,9 +126,9 @@ export const ConfirmationsContainer = ({
</MultiLine> </MultiLine>
</StatusDescription> </StatusDescription>
<ValidatorsConfirmations <ValidatorsConfirmations
confirmations={confirmations} confirmations={fromHome ? confirmations.filter(c => dst.validatorList.includes(c.validator)) : confirmations}
requiredSignatures={requiredSignatures} requiredSignatures={dst.requiredSignatures}
validatorList={validatorList} validatorList={dst.validatorList}
waitingBlocksResolved={waitingBlocksResolved} waitingBlocksResolved={waitingBlocksResolved}
/> />
{signatureCollected && ( {signatureCollected && (
@ -124,10 +136,12 @@ export const ConfirmationsContainer = ({
message={message} message={message}
executionData={executionData} executionData={executionData}
isHome={!fromHome} isHome={!fromHome}
signatureCollected={signatureCollected} confirmations={confirmations}
setExecutionData={setExecutionData} setExecutionData={setExecutionData}
executionEventsFetched={executionEventsFetched} executionEventsFetched={executionEventsFetched}
setPendingExecution={setPendingExecution} setPendingExecution={setPendingExecution}
dstRequiredSignatures={dst.requiredSignatures}
dstValidatorList={dst.validatorList}
/> />
)} )}
</StyledConfirmationContainer> </StyledConfirmationContainer>

@ -4,7 +4,7 @@ import { useWindowWidth } from '@react-hook/window-size'
import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS, ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from '../config/constants' import { SEARCHING_TX, VALIDATOR_CONFIRMATION_STATUS, ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION } from '../config/constants'
import { SimpleLoading } from './commons/Loading' import { SimpleLoading } from './commons/Loading'
import styled from 'styled-components' import styled from 'styled-components'
import { ExecutionData } from '../hooks/useMessageConfirmations' import { ConfirmationParam, ExecutionData } from '../hooks/useMessageConfirmations'
import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels' import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
import { ExplorerTxLink } from './commons/ExplorerTxLink' import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { Thead, AgeTd, StatusTd } from './commons/Table' import { Thead, AgeTd, StatusTd } from './commons/Table'
@ -22,20 +22,24 @@ export interface ExecutionConfirmationParams {
message: MessageObject message: MessageObject
executionData: ExecutionData executionData: ExecutionData
setExecutionData: Function setExecutionData: Function
signatureCollected: boolean | string[] confirmations: ConfirmationParam[]
isHome: boolean isHome: boolean
executionEventsFetched: boolean executionEventsFetched: boolean
setPendingExecution: Function setPendingExecution: Function
dstRequiredSignatures: number
dstValidatorList: string[]
} }
export const ExecutionConfirmation = ({ export const ExecutionConfirmation = ({
message, message,
executionData, executionData,
setExecutionData, setExecutionData,
signatureCollected, confirmations,
isHome, isHome,
executionEventsFetched, executionEventsFetched,
setPendingExecution setPendingExecution,
dstRequiredSignatures,
dstValidatorList
}: ExecutionConfirmationParams) => { }: ExecutionConfirmationParams) => {
const { foreign } = useStateProvider() const { foreign } = useStateProvider()
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false) const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
@ -152,9 +156,11 @@ export const ExecutionConfirmation = ({
safeExecutionAvailable={safeExecutionAvailable} safeExecutionAvailable={safeExecutionAvailable}
messageData={message.data} messageData={message.data}
setExecutionData={setExecutionData} setExecutionData={setExecutionData}
signatureCollected={signatureCollected as string[]} confirmations={confirmations}
setPendingExecution={setPendingExecution} setPendingExecution={setPendingExecution}
setError={setError} setError={setError}
requiredSignatures={dstRequiredSignatures}
validatorList={dstValidatorList}
/> />
</td> </td>
)} )}

@ -14,7 +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' import { ConfirmationParam } from '../hooks/useMessageConfirmations'
const ActionButton = styled.button` const ActionButton = styled.button`
color: var(--button-color); color: var(--button-color);
@ -31,18 +31,22 @@ interface ManualExecutionButtonParams {
safeExecutionAvailable: boolean safeExecutionAvailable: boolean
messageData: string messageData: string
setExecutionData: Function setExecutionData: Function
signatureCollected: string[] confirmations: ConfirmationParam[]
setPendingExecution: Function setPendingExecution: Function
setError: Function setError: Function
requiredSignatures: number
validatorList: string[]
} }
export const ManualExecutionButton = ({ export const ManualExecutionButton = ({
safeExecutionAvailable, safeExecutionAvailable,
messageData, messageData,
setExecutionData, setExecutionData,
signatureCollected, confirmations,
setPendingExecution, setPendingExecution,
setError setError,
requiredSignatures,
validatorList
}: ManualExecutionButtonParams) => { }: ManualExecutionButtonParams) => {
const { foreign } = useStateProvider() const { foreign } = useStateProvider()
const { library, activate, account, active } = useWeb3React() const { library, activate, account, active } = useWeb3React()
@ -52,15 +56,13 @@ export const ManualExecutionButton = ({
const [title, setTitle] = useState('Loading') const [title, setTitle] = useState('Loading')
const [validSignatures, setValidSignatures] = useState<string[]>([]) const [validSignatures, setValidSignatures] = useState<string[]>([])
const { requiredSignatures, validatorList } = useValidatorContract(false, 'latest')
useEffect( useEffect(
() => { () => {
if ( if (
!foreign.bridgeContract || !foreign.bridgeContract ||
!foreign.web3 || !foreign.web3 ||
!signatureCollected || !confirmations ||
!signatureCollected.length || !confirmations.length ||
!requiredSignatures || !requiredSignatures ||
!validatorList || !validatorList ||
!validatorList.length !validatorList.length
@ -68,39 +70,20 @@ export const ManualExecutionButton = ({
return return
const signatures = [] const signatures = []
const remainingValidators = Object.fromEntries(validatorList.map(validator => [validator, true])) for (let i = 0; i < confirmations.length && signatures.length < requiredSignatures; i++) {
for (let i = 0; i < signatureCollected.length && signatures.length < requiredSignatures; i++) { const sig = confirmations[i].signature
const { v, r, s } = signatureToVRS(signatureCollected[i]) if (!sig) {
continue
}
const { v, r, s } = signatureToVRS(sig)
const signer = foreign.web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`) const signer = foreign.web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`)
if (validatorList.includes(signer)) { if (validatorList.includes(signer)) {
delete remainingValidators[signer] signatures.push(sig)
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) { if (signatures.length >= requiredSignatures) {
setValidSignatures(signatures) setValidSignatures(signatures.slice(0, requiredSignatures))
setTitle('Execute') setTitle('Execute')
setReady(true) setReady(true)
} else { } else {
@ -110,11 +93,11 @@ export const ManualExecutionButton = ({
[ [
foreign.bridgeContract, foreign.bridgeContract,
foreign.web3, foreign.web3,
signatureCollected,
validatorList, validatorList,
requiredSignatures, requiredSignatures,
messageData, messageData,
setValidSignatures setValidSignatures,
confirmations
] ]
) )

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

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

@ -29,17 +29,16 @@ export interface useMessageConfirmationsParams {
foreignStartBlock: Maybe<number> foreignStartBlock: Maybe<number>
requiredSignatures: number requiredSignatures: number
validatorList: string[] validatorList: string[]
targetValidatorList: string[]
blockConfirmations: number blockConfirmations: number
} }
export interface BasicConfirmationParam { export interface ConfirmationParam {
validator: string validator: string
status: string status: string
}
export interface ConfirmationParam extends BasicConfirmationParam {
txHash: string txHash: string
timestamp: number timestamp: number
signature?: string
} }
export interface ExecutionData { export interface ExecutionData {
@ -48,6 +47,7 @@ export interface ExecutionData {
txHash: string txHash: string
timestamp: number timestamp: number
executionResult: boolean executionResult: boolean
blockNumber: number
} }
export const useMessageConfirmations = ({ export const useMessageConfirmations = ({
@ -58,6 +58,7 @@ export const useMessageConfirmations = ({
foreignStartBlock, foreignStartBlock,
requiredSignatures, requiredSignatures,
validatorList, validatorList,
targetValidatorList,
blockConfirmations blockConfirmations
}: useMessageConfirmationsParams) => { }: useMessageConfirmationsParams) => {
const { home, foreign } = useStateProvider() const { home, foreign } = useStateProvider()
@ -65,7 +66,7 @@ export const useMessageConfirmations = ({
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED) const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
const [waitingBlocks, setWaitingBlocks] = useState(false) const [waitingBlocks, setWaitingBlocks] = useState(false)
const [waitingBlocksResolved, setWaitingBlocksResolved] = 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 [executionEventsFetched, setExecutionEventsFetched] = useState(false)
const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null) const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null)
const [executionData, setExecutionData] = useState<ExecutionData>({ const [executionData, setExecutionData] = useState<ExecutionData>({
@ -73,7 +74,8 @@ export const useMessageConfirmations = ({
validator: '', validator: '',
txHash: '', txHash: '',
timestamp: 0, timestamp: 0,
executionResult: false executionResult: false,
blockNumber: 0
}) })
const [waitingBlocksForExecution, setWaitingBlocksForExecution] = useState(false) const [waitingBlocksForExecution, setWaitingBlocksForExecution] = useState(false)
const [waitingBlocksForExecutionResolved, setWaitingBlocksForExecutionResolved] = 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 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 // 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 // This is executed if the message is in Home to Foreign direction only
const hasCollectedSignatures = !!signatureCollected // true or string[]
useEffect( useEffect(
() => { () => {
if (!fromHome || !receipt || !home.web3 || !home.bridgeContract || !hasCollectedSignatures) return if (!fromHome || !receipt || !home.web3 || !home.bridgeContract || !signatureCollected) return
let timeoutId: number let timeoutId: number
let isCancelled = false let isCancelled = false
@ -179,7 +180,7 @@ export const useMessageConfirmations = ({
isCancelled = true 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 // 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 timeoutId: number
let isCancelled = false 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( getConfirmationsForTx(
message.data, message.data,
home.web3, home.web3,
@ -284,7 +314,8 @@ export const useMessageConfirmations = ({
home.bridgeContract, home.bridgeContract,
requiredSignatures, requiredSignatures,
waitingBlocksResolved, waitingBlocksResolved,
homeStartBlock homeStartBlock,
targetValidatorList
] ]
) )

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

@ -5,7 +5,7 @@ import Web3 from 'web3'
import { Contract } from 'web3-eth-contract' import { Contract } from 'web3-eth-contract'
import { APIPendingTransaction, APITransaction } from '../explorer' import { APIPendingTransaction, APITransaction } from '../explorer'
import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants' import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants'
import { BasicConfirmationParam } from '../../hooks/useMessageConfirmations' import { ConfirmationParam } from '../../hooks/useMessageConfirmations'
jest.mock('../validatorConfirmationHelpers') jest.mock('../validatorConfirmationHelpers')
@ -18,6 +18,9 @@ const messageData = '0x111111111'
const web3 = { const web3 = {
utils: { utils: {
soliditySha3Raw: (data: string) => `0xaaaa${data.replace('0x', '')}` soliditySha3Raw: (data: string) => `0xaaaa${data.replace('0x', '')}`
},
eth: {
accounts: new Web3().eth.accounts
} }
} as Web3 } as Web3
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908' const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
@ -25,7 +28,7 @@ const validator2 = '0xAe8bFfc8BBc6AAa9E21ED1E4e4957fe798BEA25f'
const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244' const validator3 = '0x285A6eB779be4db94dA65e2F3518B1c5F0f71244'
const validatorList = [validator1, validator2, validator3] const validatorList = [validator1, validator2, validator3]
const signature = const signature =
'0x519d704bceed17423daa79c20531cc34fc27a4be6e53fc5069a8023019188ca4519d704bceed17423daa79c20531cc34fc27a4be6e53fc5069a8023019188ca4' '0x6f5b74905669999f1abdb52e1e215506907e1849aac7b31854da458b33a5954e15b165007c3703cfd16e61ca46a96a56727ed11fa47be359d3834515accd016e1b'
const bridgeContract = { const bridgeContract = {
methods: { methods: {
signature: () => ({ signature: () => ({
@ -61,19 +64,19 @@ describe('getConfirmationsForTx', () => {
validator, validator,
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: '', txHash: '',
timestamp: 0 timestamp: 0
})) }))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
timestamp: 0 timestamp: 0
})) }))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
@ -110,9 +113,8 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(2) expect(setResult).toBeCalledTimes(2)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).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[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1) expect(setFailedConfirmations).toBeCalledTimes(1)
@ -135,7 +137,7 @@ 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.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, validator,
status: validator === validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED status: validator === validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: '', txHash: '',
timestamp: 0 timestamp: 0
})) }))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
timestamp: 0 timestamp: 0
})) }))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
@ -208,19 +210,19 @@ describe('getConfirmationsForTx', () => {
validator, validator,
status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED status: validator !== validator3 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 ? '0x123' : '', txHash: validatorData.validator !== validator3 ? '0x123' : '',
timestamp: validatorData.validator !== validator3 ? 123 : 0 timestamp: validatorData.validator !== validator3 ? 123 : 0
})) }))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
timestamp: 0 timestamp: 0
})) }))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
@ -257,9 +259,8 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(3) expect(setResult).toBeCalledTimes(3)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).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[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1) expect(setFailedConfirmations).toBeCalledTimes(1)
@ -290,7 +291,7 @@ describe('getConfirmationsForTx', () => {
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.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.SUCCESS
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? '0x123' : '', txHash: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? '0x123' : '',
timestamp: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? 123 : 0 timestamp: validatorData.validator !== validator3 && validatorData.validator !== validator4 ? 123 : 0
})) }))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator === validator3 validatorData.validator === validator3
? VALIDATOR_CONFIRMATION_STATUS.FAILED ? VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator3 ? '0x123' : '', txHash: validatorData.validator === validator3 ? '0x123' : '',
timestamp: validatorData.validator === validator3 ? 123 : 0 timestamp: validatorData.validator === validator3 ? 123 : 0
})) }))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
@ -356,9 +357,8 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(4) expect(setResult).toBeCalledTimes(4)
expect(getValidatorConfirmation).toBeCalledTimes(1) expect(getValidatorConfirmation).toBeCalledTimes(1)
expect(getSuccessExecutionTransaction).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[0][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[1][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).toBeCalledTimes(1) expect(setFailedConfirmations).toBeCalledTimes(1)
@ -392,7 +392,7 @@ describe('getConfirmationsForTx', () => {
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.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
]) ])
) )
@ -400,8 +400,8 @@ describe('getConfirmationsForTx', () => {
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.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x123', timestamp: 123 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED, txHash: '', timestamp: 0 }
]) ])
) )
}) })
@ -414,22 +414,22 @@ describe('getConfirmationsForTx', () => {
validator, validator,
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
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 ? '0x123' : '',
timestamp: validatorData.validator === validator1 ? 123 : 0 timestamp: validatorData.validator === validator1 ? 123 : 0
})) }))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator === validator2 validatorData.validator === validator2
? VALIDATOR_CONFIRMATION_STATUS.FAILED ? VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: validatorData.validator === validator2 ? '0x123' : '', txHash: validatorData.validator === validator2 ? '0x123' : '',
timestamp: validatorData.validator === validator2 ? 123 : 0 timestamp: validatorData.validator === validator2 ? 123 : 0
})) }))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator === validator3 validatorData.validator === validator3
@ -507,7 +507,7 @@ describe('getConfirmationsForTx', () => {
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.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 } { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 }
]) ])
) )
@ -521,13 +521,13 @@ describe('getConfirmationsForTx', () => {
validator, validator,
status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED status: validator === validator1 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getSuccessExecutionTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
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 ? '0x123' : '',
timestamp: validatorData.validator === validator1 ? 123 : 0 timestamp: validatorData.validator === validator1 ? 123 : 0
})) }))
getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorFailedTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator !== validator1 validatorData.validator !== validator1
@ -536,7 +536,7 @@ describe('getConfirmationsForTx', () => {
txHash: validatorData.validator !== validator1 ? '0x123' : '', txHash: validatorData.validator !== validator1 ? '0x123' : '',
timestamp: validatorData.validator !== validator1 ? 123 : 0 timestamp: validatorData.validator !== validator1 ? 123 : 0
})) }))
getValidatorPendingTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ getValidatorPendingTransaction.mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
@ -632,13 +632,13 @@ describe('getConfirmationsForTx', () => {
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
})) }))
getSuccessExecutionTransaction getSuccessExecutionTransaction
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ .mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: validatorData.validator === validator1 ? '0x100' : '', txHash: validatorData.validator === validator1 ? '0x100' : '',
timestamp: validatorData.validator === validator1 ? 100 : 0 timestamp: validatorData.validator === validator1 ? 100 : 0
})) }))
.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ .mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
txHash: txHash:
@ -646,7 +646,7 @@ describe('getConfirmationsForTx', () => {
timestamp: validatorData.validator === validator1 ? 100 : validatorData.validator === validator3 ? 300 : '' timestamp: validatorData.validator === validator1 ? 100 : validatorData.validator === validator3 ? 300 : ''
})) }))
getValidatorFailedTransaction getValidatorFailedTransaction
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ .mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator === validator2 validatorData.validator === validator2
@ -655,18 +655,20 @@ describe('getConfirmationsForTx', () => {
txHash: validatorData.validator === validator2 ? '0x200' : '', txHash: validatorData.validator === validator2 ? '0x200' : '',
timestamp: validatorData.validator === validator2 ? 200 : 0 timestamp: validatorData.validator === validator2 ? 200 : 0
})) }))
.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ .mockImplementation(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator === validator2 || validatorData.validator === validator4 validatorData.validator === validator2 || validatorData.validator === validator4
? validatorData.validator === validator2
? VALIDATOR_CONFIRMATION_STATUS.FAILED ? VALIDATOR_CONFIRMATION_STATUS.FAILED
: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: txHash:
validatorData.validator === validator2 ? '0x200' : validatorData.validator === validator4 ? '0x400' : '', validatorData.validator === validator2 ? '0x200' : validatorData.validator === validator4 ? '0x400' : '',
timestamp: validatorData.validator === validator2 ? 200 : validatorData.validator === validator4 ? 400 : '' timestamp: validatorData.validator === validator2 ? 200 : validatorData.validator === validator4 ? 400 : ''
})) }))
getValidatorPendingTransaction getValidatorPendingTransaction
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ .mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: status:
validatorData.validator === validator3 validatorData.validator === validator3
@ -675,7 +677,7 @@ describe('getConfirmationsForTx', () => {
txHash: validatorData.validator === validator3 ? '0x300' : '', txHash: validatorData.validator === validator3 ? '0x300' : '',
timestamp: validatorData.validator === validator3 ? 300 : 0 timestamp: validatorData.validator === validator3 ? 300 : 0
})) }))
.mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ .mockImplementationOnce(() => async (validatorData: ConfirmationParam) => ({
validator: validatorData.validator, validator: validatorData.validator,
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
txHash: '', txHash: '',
@ -716,7 +718,7 @@ describe('getConfirmationsForTx', () => {
expect(getValidatorFailedTransaction).toBeCalledTimes(1) expect(getValidatorFailedTransaction).toBeCalledTimes(1)
expect(setFailedConfirmations).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(getValidatorPendingTransaction).toBeCalledTimes(1)
expect(setPendingConfirmations).toBeCalledTimes(1) expect(setPendingConfirmations).toBeCalledTimes(1)
@ -783,14 +785,13 @@ describe('getConfirmationsForTx', () => {
expect(setResult).toBeCalledTimes(7) expect(setResult).toBeCalledTimes(7)
expect(getValidatorConfirmation).toBeCalledTimes(2) expect(getValidatorConfirmation).toBeCalledTimes(2)
expect(getSuccessExecutionTransaction).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[0][0]).toEqual(false)
expect(setSignatureCollected.mock.calls[1][0]).toEqual(true) expect(setSignatureCollected.mock.calls[1][0]).toEqual(true)
expect(setSignatureCollected.mock.calls[2][0]).toEqual([signature, signature])
expect(getValidatorFailedTransaction).toBeCalledTimes(2) expect(getValidatorFailedTransaction).toBeCalledTimes(2)
expect(setFailedConfirmations).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(setFailedConfirmations.mock.calls[1][0]).toEqual(false)
expect(getValidatorPendingTransaction).toBeCalledTimes(1) expect(getValidatorPendingTransaction).toBeCalledTimes(1)
@ -805,7 +806,7 @@ describe('getConfirmationsForTx', () => {
expect.arrayContaining([ expect.arrayContaining([
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 }, { 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 } { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }
]) ])
) )
@ -822,7 +823,7 @@ describe('getConfirmationsForTx', () => {
{ validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 },
{ validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 },
{ validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 }, { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 },
{ validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x400', timestamp: 400 } { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID, txHash: '0x400', timestamp: 400 }
]) ])
) )
}) })

@ -87,7 +87,8 @@ describe('getFinalizationEvent', () => {
status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS, status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS,
txHash, txHash,
timestamp, timestamp,
executionResult: true executionResult: true,
blockNumber: 5523145
}) })
expect(getFailedExecution).toBeCalledTimes(0) expect(getFailedExecution).toBeCalledTimes(0)
@ -237,7 +238,8 @@ describe('getFinalizationEvent', () => {
status: VALIDATOR_CONFIRMATION_STATUS.PENDING, status: VALIDATOR_CONFIRMATION_STATUS.PENDING,
txHash, txHash,
timestamp: expect.any(Number), timestamp: expect.any(Number),
executionResult: false executionResult: false,
blockNumber: 0
}) })
expect(getFailedExecution).toBeCalledTimes(0) expect(getFailedExecution).toBeCalledTimes(0)
@ -294,7 +296,8 @@ describe('getFinalizationEvent', () => {
status: VALIDATOR_CONFIRMATION_STATUS.FAILED, status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
txHash, txHash,
timestamp: expect.any(Number), timestamp: expect.any(Number),
executionResult: false executionResult: false,
blockNumber: expect.any(Number)
}) })
expect(getFailedExecution).toBeCalledTimes(1) expect(getFailedExecution).toBeCalledTimes(1)

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

@ -2,26 +2,37 @@ import Web3 from 'web3'
import { Contract } from 'web3-eth-contract' import { Contract } from 'web3-eth-contract'
import { HOME_RPC_POLLING_INTERVAL, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants' import { HOME_RPC_POLLING_INTERVAL, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { GetTransactionParams, APITransaction, APIPendingTransaction, GetPendingTransactionParams } from './explorer' import { GetTransactionParams, APITransaction, APIPendingTransaction, GetPendingTransactionParams } from './explorer'
import { getAffirmationsSigned, getMessagesSigned } from './contract'
import { import {
getValidatorConfirmation, getValidatorConfirmation,
getValidatorFailedTransaction, getValidatorFailedTransaction,
getValidatorPendingTransaction, getValidatorPendingTransaction,
getSuccessExecutionTransaction getSuccessExecutionTransaction
} from './validatorConfirmationHelpers' } 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] const confirmations = [...oldConfirmations]
newConfirmations.forEach(validatorData => { newConfirmations.forEach(validatorData => {
const index = confirmations.findIndex(e => e.validator === validatorData.validator) const index = confirmations.findIndex(e => e.validator === validatorData.validator)
if (index === -1) {
confirmations.push(validatorData)
return
}
const currentStatus = confirmations[index].status const currentStatus = confirmations[index].status
const newStatus = validatorData.status const newStatus = validatorData.status
if ( if (
(validatorData as ConfirmationParam).txHash || validatorData.txHash ||
!!validatorData.signature ||
(newStatus !== currentStatus && newStatus !== VALIDATOR_CONFIRMATION_STATUS.UNDEFINED) (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 return confirmations
@ -45,19 +56,17 @@ export const getConfirmationsForTx = async (
setPendingConfirmations: Function, setPendingConfirmations: Function,
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]> getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
) => { ) => {
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
const hashMsg = web3.utils.soliditySha3Raw(messageData) const hashMsg = web3.utils.soliditySha3Raw(messageData)
let validatorConfirmations = await Promise.all( 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) { if (confirmations.length === 0) {
return return
} }
validatorConfirmations = mergeConfirmations(validatorConfirmations, confirmations) validatorConfirmations = mergeConfirmations(validatorConfirmations, confirmations)
setResult((currentConfirmations: BasicConfirmationParam[]) => { setResult((currentConfirmations: ConfirmationParam[]) => {
if (currentConfirmations && currentConfirmations.length) { if (currentConfirmations && currentConfirmations.length) {
return mergeConfirmations(currentConfirmations, confirmations) return mergeConfirmations(currentConfirmations, confirmations)
} }
@ -67,7 +76,7 @@ export const getConfirmationsForTx = async (
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
const notSuccessConfirmations = 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) updateConfirmations(validatorConfirmations)
setSignatureCollected(hasEnoughSignatures) setSignatureCollected(hasEnoughSignatures)
@ -76,11 +85,15 @@ export const getConfirmationsForTx = async (
setPendingConfirmations(false) setPendingConfirmations(false)
if (fromHome) { if (fromHome) {
// fetch collected signatures for possible manual processing // fetch collected signatures for possible manual processing
setSignatureCollected( const signatures = await Promise.all(
await Promise.all(
Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call()) 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)
} }
} }
@ -115,13 +128,13 @@ export const getConfirmationsForTx = async (
// Check if confirmation failed // Check if confirmation failed
const validatorFailedConfirmationsChecks = await Promise.all( const validatorFailedConfirmationsChecks = await Promise.all(
undefinedConfirmations.map( undefinedConfirmations.map(
getValidatorFailedTransaction(bridgeContract, messageData, startBlock, getFailedTransactions) getValidatorFailedTransaction(web3, bridgeContract, messageData, startBlock, getFailedTransactions)
) )
) )
let validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter( let validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED || c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
) )
if (hasEnoughSignatures) { if (hasEnoughSignatures && !fromHome) {
const lastTS = Math.max(...successConfirmationWithTxFound.map(c => c.timestamp || 0)) const lastTS = Math.max(...successConfirmationWithTxFound.map(c => c.timestamp || 0))
validatorFailedConfirmations = validatorFailedConfirmations.map( validatorFailedConfirmations = validatorFailedConfirmations.map(
c => c =>
@ -129,11 +142,13 @@ export const getConfirmationsForTx = async (
? c ? c
: { : {
...c, ...c,
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS status: VALIDATOR_CONFIRMATION_STATUS.FAILED_VALID
} }
) )
} }
setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures) setFailedConfirmations(
!hasEnoughSignatures && validatorFailedConfirmations.some(c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED)
)
updateConfirmations(validatorFailedConfirmations) updateConfirmations(validatorFailedConfirmations)
const missingConfirmations = validatorConfirmations.filter( const missingConfirmations = validatorConfirmations.filter(
@ -144,7 +159,9 @@ export const getConfirmationsForTx = async (
// If signatures collected, it should set other signatures not found as not required // If signatures collected, it should set other signatures not found as not required
const notRequiredConfirmations = missingConfirmations.map(c => ({ const notRequiredConfirmations = missingConfirmations.map(c => ({
validator: c.validator, validator: c.validator,
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED,
timestamp: 0,
txHash: ''
})) }))
updateConfirmations(notRequiredConfirmations) updateConfirmations(notRequiredConfirmations)
} }

@ -63,7 +63,8 @@ export const getSuccessExecutionData = async (
validator: validatorAddress, validator: validatorAddress,
txHash: event.transactionHash, txHash: event.transactionHash,
timestamp: blockTimestamp, timestamp: blockTimestamp,
executionResult: event.returnValues.status executionResult: event.returnValues.status,
blockNumber: event.blockNumber
} }
} }
return null return null
@ -115,7 +116,8 @@ export const getFinalizationEvent = async (
validator: validator, validator: validator,
txHash: pendingTx.hash, txHash: pendingTx.hash,
timestamp: nowTimestamp, timestamp: nowTimestamp,
executionResult: false executionResult: false,
blockNumber: 0
}) })
setPendingExecution(true) setPendingExecution(true)
} else { } else {
@ -144,7 +146,8 @@ export const getFinalizationEvent = async (
validator: validator, validator: validator,
txHash: failedTx.hash, txHash: failedTx.hash,
timestamp, timestamp,
executionResult: false executionResult: false,
blockNumber: parseInt(failedTx.blockNumber)
}) })
setFailedExecution(true) setFailedExecution(true)
} }

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