Fix ALM react subscriptions leading to duplicated RPC requests (#534)

This commit is contained in:
Kirill Fedoseev 2021-03-18 09:04:33 +03:00 committed by GitHub
parent 4dba9a50e8
commit b6ba0744b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 228 additions and 425 deletions

@ -11,9 +11,6 @@ import {
VALIDATOR_CONFIRMATION_STATUS VALIDATOR_CONFIRMATION_STATUS
} from '../config/constants' } from '../config/constants'
import { homeBlockNumberProvider, foreignBlockNumberProvider } from '../services/BlockNumberProvider' import { homeBlockNumberProvider, foreignBlockNumberProvider } from '../services/BlockNumberProvider'
import { checkSignaturesWaitingForBLocks } from '../utils/signatureWaitingForBlocks'
import { getCollectedSignaturesEvent } from '../utils/getCollectedSignaturesEvent'
import { checkWaitingBlocksForExecution } from '../utils/executionWaitingForBlocks'
import { getConfirmationsForTx } from '../utils/getConfirmationsForTx' import { getConfirmationsForTx } from '../utils/getConfirmationsForTx'
import { getFinalizationEvent } from '../utils/getFinalizationEvent' import { getFinalizationEvent } from '../utils/getFinalizationEvent'
import { import {
@ -64,11 +61,11 @@ export const useMessageConfirmations = ({
blockConfirmations blockConfirmations
}: useMessageConfirmationsParams) => { }: useMessageConfirmationsParams) => {
const { home, foreign } = useStateProvider() const { home, foreign } = useStateProvider()
const [confirmations, setConfirmations] = useState([]) const [confirmations, setConfirmations] = useState<ConfirmationParam[]>([])
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(false) const [signatureCollected, setSignatureCollected] = useState<boolean | string[]>(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>({
@ -85,12 +82,10 @@ export const useMessageConfirmations = ({
const [pendingConfirmations, setPendingConfirmations] = useState(false) const [pendingConfirmations, setPendingConfirmations] = useState(false)
const [pendingExecution, setPendingExecution] = useState(false) const [pendingExecution, setPendingExecution] = useState(false)
const existsConfirmation = (confirmationArray: ConfirmationParam[]) => { const existsConfirmation = (confirmationArray: ConfirmationParam[]) =>
const filteredList = confirmationArray.filter( confirmationArray.some(
c => c.status !== VALIDATOR_CONFIRMATION_STATUS.UNDEFINED && c.status !== VALIDATOR_CONFIRMATION_STATUS.WAITING c => c.status !== VALIDATOR_CONFIRMATION_STATUS.UNDEFINED && c.status !== VALIDATOR_CONFIRMATION_STATUS.WAITING
) )
return filteredList.length > 0
}
// start watching blocks at the start // start watching blocks at the start
useEffect( useEffect(
@ -108,129 +103,154 @@ export const useMessageConfirmations = ({
() => { () => {
if (!receipt || !blockConfirmations || waitingBlocksResolved) return if (!receipt || !blockConfirmations || waitingBlocksResolved) return
const subscriptions: Array<number> = [] let timeoutId: number
const unsubscribe = () => {
subscriptions.forEach(s => {
clearTimeout(s)
})
}
const blockProvider = fromHome ? homeBlockNumberProvider : foreignBlockNumberProvider const blockProvider = fromHome ? homeBlockNumberProvider : foreignBlockNumberProvider
const interval = fromHome ? HOME_RPC_POLLING_INTERVAL : FOREIGN_RPC_POLLING_INTERVAL const interval = fromHome ? HOME_RPC_POLLING_INTERVAL : FOREIGN_RPC_POLLING_INTERVAL
const targetBlock = receipt.blockNumber + blockConfirmations const targetBlock = receipt.blockNumber + blockConfirmations
const validatorsWaiting = validatorList.map(validator => ({
validator,
status: VALIDATOR_CONFIRMATION_STATUS.WAITING,
txHash: '',
timestamp: 0
}))
checkSignaturesWaitingForBLocks( const checkSignaturesWaitingForBLocks = () => {
targetBlock, const currentBlock = blockProvider.get()
setWaitingBlocks,
setWaitingBlocksResolved,
validatorList,
setConfirmations,
blockProvider,
interval,
subscriptions
)
return () => { if (currentBlock && currentBlock >= targetBlock) {
unsubscribe() setWaitingBlocksResolved(true)
setWaitingBlocks(false)
} else if (currentBlock) {
setWaitingBlocks(true)
setConfirmations(validatorsWaiting)
timeoutId = setTimeout(checkSignaturesWaitingForBLocks, interval)
} else {
timeoutId = setTimeout(checkSignaturesWaitingForBLocks, 500)
}
} }
checkSignaturesWaitingForBLocks()
return () => clearTimeout(timeoutId)
}, },
[ [blockConfirmations, fromHome, receipt, validatorList, waitingBlocksResolved]
blockConfirmations,
foreign.web3,
fromHome,
validatorList,
home.web3,
receipt,
setConfirmations,
waitingBlocksResolved
]
) )
// 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 || !signatureCollected) return if (!fromHome || !receipt || !home.web3 || !home.bridgeContract || !hasCollectedSignatures) return
const subscriptions: Array<number> = [] let timeoutId: number
let isCancelled = false
const unsubscribe = () => { const messageHash = home.web3.utils.soliditySha3Raw(message.data)
subscriptions.forEach(s => { const contract = home.bridgeContract
clearTimeout(s)
}) const getCollectedSignaturesEvent = async (fromBlock: number, toBlock: number) => {
const currentBlock = homeBlockNumberProvider.get()
if (currentBlock) {
// prevent errors if the toBlock parameter is bigger than the latest
const securedToBlock = toBlock >= currentBlock ? currentBlock : toBlock
const events = await contract.getPastEvents('CollectedSignatures', {
fromBlock,
toBlock: securedToBlock
})
const event = events.find(e => e.returnValues.messageHash === messageHash)
if (event) {
setCollectedSignaturesEvent(event)
} else if (!isCancelled) {
timeoutId = setTimeout(() => getCollectedSignaturesEvent(securedToBlock, securedToBlock + BLOCK_RANGE), 500)
}
} else if (!isCancelled) {
timeoutId = setTimeout(() => getCollectedSignaturesEvent(fromBlock, toBlock), 500)
}
} }
const fromBlock = receipt.blockNumber getCollectedSignaturesEvent(receipt.blockNumber, receipt.blockNumber + BLOCK_RANGE)
const toBlock = fromBlock + BLOCK_RANGE
const messageHash = home.web3.utils.soliditySha3Raw(message.data)
getCollectedSignaturesEvent(
home.web3,
home.bridgeContract,
fromBlock,
toBlock,
messageHash,
setCollectedSignaturesEvent,
subscriptions
)
return () => { return () => {
unsubscribe() clearTimeout(timeoutId)
isCancelled = true
} }
}, },
[fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected] [fromHome, home.bridgeContract, home.web3, message.data, receipt, hasCollectedSignatures]
) )
// 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
// 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
useEffect( useEffect(
() => { () => {
if (!fromHome || !home.web3 || !receipt || !collectedSignaturesEvent || !blockConfirmations) return if (!fromHome || !home.web3 || !collectedSignaturesEvent || !blockConfirmations) return
if (waitingBlocksForExecutionResolved) return if (waitingBlocksForExecutionResolved) return
const subscriptions: Array<number> = [] let timeoutId: number
const unsubscribe = () => {
subscriptions.forEach(s => {
clearTimeout(s)
})
}
const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations
checkWaitingBlocksForExecution( const checkWaitingBlocksForExecution = () => {
homeBlockNumberProvider, const currentBlock = homeBlockNumberProvider.get()
HOME_RPC_POLLING_INTERVAL,
targetBlock,
collectedSignaturesEvent,
setWaitingBlocksForExecution,
setWaitingBlocksForExecutionResolved,
setExecutionData,
subscriptions
)
return () => { if (currentBlock && currentBlock >= targetBlock) {
unsubscribe() const undefinedExecutionState = {
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
txHash: '',
timestamp: 0,
executionResult: false
}
setExecutionData(
(data: any) =>
data.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED ||
data.status === VALIDATOR_CONFIRMATION_STATUS.WAITING
? undefinedExecutionState
: data
)
setWaitingBlocksForExecutionResolved(true)
setWaitingBlocksForExecution(false)
} else if (currentBlock) {
setWaitingBlocksForExecution(true)
const waitingExecutionState = {
status: VALIDATOR_CONFIRMATION_STATUS.WAITING,
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
txHash: '',
timestamp: 0,
executionResult: false
}
setExecutionData(
(data: any) =>
data.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED ||
data.status === VALIDATOR_CONFIRMATION_STATUS.WAITING
? waitingExecutionState
: data
)
timeoutId = setTimeout(() => checkWaitingBlocksForExecution(), HOME_RPC_POLLING_INTERVAL)
} else {
timeoutId = setTimeout(() => checkWaitingBlocksForExecution(), 500)
}
} }
checkWaitingBlocksForExecution()
return () => clearTimeout(timeoutId)
}, },
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt, waitingBlocksForExecutionResolved] [collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, waitingBlocksForExecutionResolved]
) )
// Checks if validators verified the message // Checks if validators verified the message
// To avoid making extra requests, this is only executed when validators finished waiting for blocks confirmations // To avoid making extra requests, this is only executed when validators finished waiting for blocks confirmations
useEffect( useEffect(
() => { () => {
if (!waitingBlocksResolved || !homeStartBlock || !requiredSignatures) return if (!waitingBlocksResolved || !homeStartBlock || !requiredSignatures || !home.web3 || !home.bridgeContract) return
if (!validatorList || !validatorList.length) return
const subscriptions: Array<number> = [] let timeoutId: number
let isCancelled = false
const unsubscribe = () => {
subscriptions.forEach(s => {
clearTimeout(s)
})
}
getConfirmationsForTx( getConfirmationsForTx(
message.data, message.data,
@ -241,8 +261,8 @@ export const useMessageConfirmations = ({
setConfirmations, setConfirmations,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, id => (timeoutId = id),
subscriptions, () => isCancelled,
homeStartBlock, homeStartBlock,
getValidatorFailedTransactionsForMessage, getValidatorFailedTransactionsForMessage,
setFailedConfirmations, setFailedConfirmations,
@ -252,7 +272,8 @@ export const useMessageConfirmations = ({
) )
return () => { return () => {
unsubscribe() clearTimeout(timeoutId)
isCancelled = true
} }
}, },
[ [
@ -263,8 +284,7 @@ export const useMessageConfirmations = ({
home.bridgeContract, home.bridgeContract,
requiredSignatures, requiredSignatures,
waitingBlocksResolved, waitingBlocksResolved,
homeStartBlock, homeStartBlock
setConfirmations
] ]
) )
@ -274,32 +294,23 @@ export const useMessageConfirmations = ({
useEffect( useEffect(
() => { () => {
if ((fromHome && !waitingBlocksForExecutionResolved) || (!fromHome && !waitingBlocksResolved)) return if ((fromHome && !waitingBlocksForExecutionResolved) || (!fromHome && !waitingBlocksResolved)) return
const startBlock = fromHome ? foreignStartBlock : homeStartBlock
if (!startBlock) return
const subscriptions: Array<number> = []
const unsubscribe = () => {
subscriptions.forEach(s => {
clearTimeout(s)
})
}
const contractEvent = fromHome ? 'RelayedMessage' : 'AffirmationCompleted'
const bridgeContract = fromHome ? foreign.bridgeContract : home.bridgeContract const bridgeContract = fromHome ? foreign.bridgeContract : home.bridgeContract
const providedWeb3 = fromHome ? foreign.web3 : home.web3 const web3 = fromHome ? foreign.web3 : home.web3
const interval = fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL const startBlock = fromHome ? foreignStartBlock : homeStartBlock
if (!startBlock || !bridgeContract || !web3) return
let timeoutId: number
let isCancelled = false
getFinalizationEvent( getFinalizationEvent(
fromHome, fromHome,
bridgeContract, bridgeContract,
contractEvent, web3,
providedWeb3,
setExecutionData, setExecutionData,
waitingBlocksResolved,
message, message,
interval, id => (timeoutId = id),
subscriptions, () => isCancelled,
startBlock, startBlock,
collectedSignaturesEvent, collectedSignaturesEvent,
getExecutionFailedTransactionForMessage, getExecutionFailedTransactionForMessage,
@ -310,7 +321,8 @@ export const useMessageConfirmations = ({
) )
return () => { return () => {
unsubscribe() clearTimeout(timeoutId)
isCancelled = true
} }
}, },
[ [

@ -11,40 +11,23 @@ export const useTransactionFinder = ({ txHash, web3 }: { txHash: string; web3: M
() => { () => {
if (!txHash || !web3) return if (!txHash || !web3) return
const subscriptions: number[] = [] let timeoutId: number
const unsubscribe = () => { const getReceipt = async () => {
subscriptions.forEach(s => {
clearTimeout(s)
})
}
const getReceipt = async (
web3: Web3,
txHash: string,
setReceipt: Function,
setStatus: Function,
subscriptions: number[]
) => {
const txReceipt = await web3.eth.getTransactionReceipt(txHash) const txReceipt = await web3.eth.getTransactionReceipt(txHash)
setReceipt(txReceipt) setReceipt(txReceipt)
if (!txReceipt) { if (!txReceipt) {
setStatus(TRANSACTION_STATUS.NOT_FOUND) setStatus(TRANSACTION_STATUS.NOT_FOUND)
const timeoutId = setTimeout( timeoutId = setTimeout(getReceipt, HOME_RPC_POLLING_INTERVAL)
() => getReceipt(web3, txHash, setReceipt, setStatus, subscriptions),
HOME_RPC_POLLING_INTERVAL
)
subscriptions.push(timeoutId)
} else { } else {
setStatus(TRANSACTION_STATUS.FOUND) setStatus(TRANSACTION_STATUS.FOUND)
} }
} }
getReceipt(web3, txHash, setReceipt, setStatus, subscriptions) getReceipt()
return () => {
unsubscribe() return () => clearTimeout(timeoutId)
}
}, },
[txHash, web3] [txHash, web3]
) )

@ -31,19 +31,14 @@ export const useTransactionStatus = ({
useEffect( useEffect(
() => { () => {
const subscriptions: Array<number> = [] if (!chainId || !txHash || !home.chainId || !foreign.chainId || !home.web3 || !foreign.web3) return
const isHome = chainId === home.chainId
const web3 = isHome ? home.web3 : foreign.web3
const unsubscribe = () => { let timeoutId: number
subscriptions.forEach(s => {
clearTimeout(s)
})
}
const getReceipt = async () => { const getReceipt = async () => {
if (!chainId || !txHash || !home.chainId || !foreign.chainId || !home.web3 || !foreign.web3) return
setLoading(true) setLoading(true)
const isHome = chainId === home.chainId
const web3 = isHome ? home.web3 : foreign.web3
let txReceipt let txReceipt
@ -59,8 +54,7 @@ export const useTransactionStatus = ({
setStatus(TRANSACTION_STATUS.NOT_FOUND) setStatus(TRANSACTION_STATUS.NOT_FOUND)
setDescription(getTransactionStatusDescription(TRANSACTION_STATUS.NOT_FOUND)) setDescription(getTransactionStatusDescription(TRANSACTION_STATUS.NOT_FOUND))
setMessages([{ id: txHash, data: '' }]) setMessages([{ id: txHash, data: '' }])
const timeoutId = setTimeout(() => getReceipt(), HOME_RPC_POLLING_INTERVAL) timeoutId = setTimeout(() => getReceipt(), HOME_RPC_POLLING_INTERVAL)
subscriptions.push(timeoutId)
} else { } else {
const blockNumber = txReceipt.blockNumber const blockNumber = txReceipt.blockNumber
const block = await getBlock(web3, blockNumber) const block = await getBlock(web3, blockNumber)
@ -70,9 +64,9 @@ export const useTransactionStatus = ({
if (txReceipt.status) { if (txReceipt.status) {
let bridgeMessages: Array<MessageObject> let bridgeMessages: Array<MessageObject>
if (isHome) { if (isHome) {
bridgeMessages = getHomeMessagesFromReceipt(txReceipt, home.web3, home.bridgeAddress) bridgeMessages = getHomeMessagesFromReceipt(txReceipt, web3, home.bridgeAddress)
} else { } else {
bridgeMessages = getForeignMessagesFromReceipt(txReceipt, foreign.web3, foreign.bridgeAddress) bridgeMessages = getForeignMessagesFromReceipt(txReceipt, web3, foreign.bridgeAddress)
} }
if (bridgeMessages.length === 0) { if (bridgeMessages.length === 0) {
@ -98,14 +92,9 @@ export const useTransactionStatus = ({
setLoading(false) setLoading(false)
} }
// unsubscribe from previous txHash
unsubscribe()
getReceipt() getReceipt()
return () => {
// unsubscribe when unmount component return () => clearTimeout(timeoutId)
unsubscribe()
}
}, },
[ [
txHash, txHash,

@ -34,7 +34,7 @@ const bridgeContract = {
} }
} as Contract } as Contract
const requiredSignatures = 2 const requiredSignatures = 2
const waitingBlocksResolved = true const isCancelled = () => false
let subscriptions: Array<number> = [] let subscriptions: Array<number> = []
const timestamp = 1594045859 const timestamp = 1594045859
const getFailedTransactions = (): Promise<APITransaction[]> => Promise.resolve([]) const getFailedTransactions = (): Promise<APITransaction[]> => Promise.resolve([])
@ -94,8 +94,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -177,8 +177,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -241,8 +241,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -340,8 +340,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -453,8 +453,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -557,8 +557,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -678,8 +678,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,
@ -746,8 +746,8 @@ describe('getConfirmationsForTx', () => {
setResult, setResult,
requiredSignatures, requiredSignatures,
setSignatureCollected, setSignatureCollected,
waitingBlocksResolved, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
getFailedTransactions, getFailedTransactions,
setFailedConfirmations, setFailedConfirmations,

@ -4,7 +4,6 @@ import Web3 from 'web3'
import { getFinalizationEvent } from '../getFinalizationEvent' import { getFinalizationEvent } from '../getFinalizationEvent'
import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants' import { VALIDATOR_CONFIRMATION_STATUS } from '../../config/constants'
const eventName = 'RelayedMessage'
const timestamp = 1594045859 const timestamp = 1594045859
const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908' const validator1 = '0x45b96809336A8b714BFbdAB3E4B5e0fe5d839908'
const txHash = '0xdab36c9210e7e45fb82af10ffe4960461e41661dce0c9cd36b2843adaa1df156' const txHash = '0xdab36c9210e7e45fb82af10ffe4960461e41661dce0c9cd36b2843adaa1df156'
@ -20,12 +19,11 @@ const web3 = ({
toChecksumAddress: (a: string) => a toChecksumAddress: (a: string) => a
} }
} as unknown) as Web3 } as unknown) as Web3
const waitingBlocksResolved = true
const message = { const message = {
id: '0x123', id: '0x123',
data: '0x123456789' data: '0x123456789'
} }
const interval = 10000 const isCancelled = () => false
let subscriptions: Array<number> = [] let subscriptions: Array<number> = []
const event = { const event = {
@ -66,13 +64,11 @@ describe('getFinalizationEvent', () => {
await getFinalizationEvent( await getFinalizationEvent(
true, true,
contract, contract,
eventName,
web3, web3,
setResult, setResult,
waitingBlocksResolved,
message, message,
interval, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getFailedExecution, getFailedExecution,
@ -118,13 +114,11 @@ describe('getFinalizationEvent', () => {
await getFinalizationEvent( await getFinalizationEvent(
true, true,
contract, contract,
eventName,
web3, web3,
setResult, setResult,
waitingBlocksResolved,
message, message,
interval, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getFailedExecution, getFailedExecution,
@ -170,13 +164,11 @@ describe('getFinalizationEvent', () => {
await getFinalizationEvent( await getFinalizationEvent(
true, true,
contract, contract,
eventName,
web3, web3,
setResult, setResult,
waitingBlocksResolved,
message, message,
interval, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getFailedExecution, getFailedExecution,
@ -222,13 +214,11 @@ describe('getFinalizationEvent', () => {
await getFinalizationEvent( await getFinalizationEvent(
true, true,
contract, contract,
eventName,
web3, web3,
setResult, setResult,
waitingBlocksResolved,
message, message,
interval, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getFailedExecution, getFailedExecution,
@ -281,13 +271,11 @@ describe('getFinalizationEvent', () => {
await getFinalizationEvent( await getFinalizationEvent(
true, true,
contract, contract,
eventName,
web3, web3,
setResult, setResult,
waitingBlocksResolved,
message, message,
interval, subscriptions.push.bind(subscriptions),
subscriptions, isCancelled,
timestamp, timestamp,
collectedSignaturesEvent, collectedSignaturesEvent,
getFailedExecution, getFailedExecution,

@ -1,70 +0,0 @@
import { BlockNumberProvider } from '../services/BlockNumberProvider'
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { EventData } from 'web3-eth-contract'
export const checkWaitingBlocksForExecution = async (
blockProvider: BlockNumberProvider,
interval: number,
targetBlock: number,
collectedSignaturesEvent: EventData,
setWaitingBlocksForExecution: Function,
setWaitingBlocksForExecutionResolved: Function,
setExecutionData: Function,
subscriptions: number[]
) => {
const currentBlock = blockProvider.get()
if (currentBlock && currentBlock >= targetBlock) {
const undefinedExecutionState = {
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
txHash: '',
timestamp: 0,
executionResult: false
}
setExecutionData(
(data: any) =>
data.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || data.status === VALIDATOR_CONFIRMATION_STATUS.WAITING
? undefinedExecutionState
: data
)
setWaitingBlocksForExecutionResolved(true)
setWaitingBlocksForExecution(false)
} else {
let nextInterval = interval
if (!currentBlock) {
nextInterval = 500
} else {
setWaitingBlocksForExecution(true)
const waitingExecutionState = {
status: VALIDATOR_CONFIRMATION_STATUS.WAITING,
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
txHash: '',
timestamp: 0,
executionResult: false
}
setExecutionData(
(data: any) =>
data.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED ||
data.status === VALIDATOR_CONFIRMATION_STATUS.WAITING
? waitingExecutionState
: data
)
}
const timeoutId = setTimeout(
() =>
checkWaitingBlocksForExecution(
blockProvider,
interval,
targetBlock,
collectedSignaturesEvent,
setWaitingBlocksForExecution,
setWaitingBlocksForExecutionResolved,
setExecutionData,
subscriptions
),
nextInterval
)
subscriptions.push(timeoutId)
}
}

@ -1,52 +0,0 @@
import Web3 from 'web3'
import { Contract, EventData } from 'web3-eth-contract'
import { homeBlockNumberProvider } from '../services/BlockNumberProvider'
import { BLOCK_RANGE } from '../config/constants'
export const getCollectedSignaturesEvent = async (
web3: Maybe<Web3>,
contract: Maybe<Contract>,
fromBlock: number,
toBlock: number,
messageHash: string,
setCollectedSignaturesEvent: Function,
subscriptions: number[]
) => {
if (!web3 || !contract) return
const currentBlock = homeBlockNumberProvider.get()
let events: EventData[] = []
let securedToBlock = toBlock
if (currentBlock) {
// prevent errors if the toBlock parameter is bigger than the latest
securedToBlock = toBlock >= currentBlock ? currentBlock : toBlock
events = await contract.getPastEvents('CollectedSignatures', {
fromBlock,
toBlock: securedToBlock
})
}
const filteredEvents = events.filter(e => e.returnValues.messageHash === messageHash)
if (filteredEvents.length) {
const event = filteredEvents[0]
setCollectedSignaturesEvent(event)
} else {
const newFromBlock = currentBlock ? securedToBlock : fromBlock
const newToBlock = currentBlock ? toBlock + BLOCK_RANGE : toBlock
const timeoutId = setTimeout(
() =>
getCollectedSignaturesEvent(
web3,
contract,
newFromBlock,
newToBlock,
messageHash,
setCollectedSignaturesEvent,
subscriptions
),
500
)
subscriptions.push(timeoutId)
}
}

@ -29,15 +29,15 @@ const mergeConfirmations = (oldConfirmations: BasicConfirmationParam[], newConfi
export const getConfirmationsForTx = async ( export const getConfirmationsForTx = async (
messageData: string, messageData: string,
web3: Maybe<Web3>, web3: Web3,
validatorList: string[], validatorList: string[],
bridgeContract: Maybe<Contract>, bridgeContract: Contract,
fromHome: boolean, fromHome: boolean,
setResult: Function, setResult: Function,
requiredSignatures: number, requiredSignatures: number,
setSignatureCollected: Function, setSignatureCollected: Function,
waitingBlocksResolved: boolean, setTimeoutId: (timeoutId: number) => void,
subscriptions: number[], isCancelled: () => boolean,
startBlock: number, startBlock: number,
getFailedTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>, getFailedTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>,
setFailedConfirmations: Function, setFailedConfirmations: Function,
@ -45,8 +45,6 @@ export const getConfirmationsForTx = async (
setPendingConfirmations: Function, setPendingConfirmations: Function,
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]> getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
) => { ) => {
if (!web3 || !validatorList || !validatorList.length || !bridgeContract || !waitingBlocksResolved) return
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
const hashMsg = web3.utils.soliditySha3Raw(messageData) const hashMsg = web3.utils.soliditySha3Raw(messageData)
@ -144,28 +142,30 @@ export const getConfirmationsForTx = async (
(!hasEnoughSignatures && missingConfirmations.length > 0) || (!hasEnoughSignatures && missingConfirmations.length > 0) ||
successConfirmationWithTxFound.length < successConfirmationWithData.length successConfirmationWithTxFound.length < successConfirmationWithData.length
) { ) {
const timeoutId = setTimeout( if (!isCancelled()) {
() => const timeoutId = setTimeout(
getConfirmationsForTx( () =>
messageData, getConfirmationsForTx(
web3, messageData,
validatorList, web3,
bridgeContract, validatorList,
fromHome, bridgeContract,
setResult, fromHome,
requiredSignatures, setResult,
setSignatureCollected, requiredSignatures,
waitingBlocksResolved, setSignatureCollected,
subscriptions, setTimeoutId,
startBlock, isCancelled,
getFailedTransactions, startBlock,
setFailedConfirmations, getFailedTransactions,
getPendingTransactions, setFailedConfirmations,
setPendingConfirmations, getPendingTransactions,
getSuccessTransactions setPendingConfirmations,
), getSuccessTransactions
HOME_RPC_POLLING_INTERVAL ),
) HOME_RPC_POLLING_INTERVAL
subscriptions.push(timeoutId) )
setTimeoutId(timeoutId)
}
} }
} }

@ -3,7 +3,9 @@ import Web3 from 'web3'
import { import {
CACHE_KEY_EXECUTION_FAILED, CACHE_KEY_EXECUTION_FAILED,
FOREIGN_EXPLORER_API, FOREIGN_EXPLORER_API,
FOREIGN_RPC_POLLING_INTERVAL,
HOME_EXPLORER_API, HOME_EXPLORER_API,
HOME_RPC_POLLING_INTERVAL,
VALIDATOR_CONFIRMATION_STATUS VALIDATOR_CONFIRMATION_STATUS
} from '../config/constants' } from '../config/constants'
import { ExecutionData } from '../hooks/useMessageConfirmations' import { ExecutionData } from '../hooks/useMessageConfirmations'
@ -69,14 +71,12 @@ export const getSuccessExecutionData = async (
export const getFinalizationEvent = async ( export const getFinalizationEvent = async (
fromHome: boolean, fromHome: boolean,
contract: Maybe<Contract>, contract: Contract,
eventName: string, web3: Web3,
web3: Maybe<Web3>,
setResult: React.Dispatch<React.SetStateAction<ExecutionData>>, setResult: React.Dispatch<React.SetStateAction<ExecutionData>>,
waitingBlocksResolved: boolean,
message: MessageObject, message: MessageObject,
interval: number, setTimeoutId: (timeoutId: number) => void,
subscriptions: number[], isCancelled: () => boolean,
startBlock: number, startBlock: number,
collectedSignaturesEvent: Maybe<EventData>, collectedSignaturesEvent: Maybe<EventData>,
getFailedExecution: (args: GetTransactionParams) => Promise<APITransaction[]>, getFailedExecution: (args: GetTransactionParams) => Promise<APITransaction[]>,
@ -85,9 +85,11 @@ export const getFinalizationEvent = async (
setPendingExecution: Function, setPendingExecution: Function,
setExecutionEventsFetched: Function setExecutionEventsFetched: Function
) => { ) => {
if (!contract || !web3 || !waitingBlocksResolved) return const eventName = fromHome ? 'RelayedMessage' : 'AffirmationCompleted'
const api = fromHome ? FOREIGN_EXPLORER_API : HOME_EXPLORER_API const api = fromHome ? FOREIGN_EXPLORER_API : HOME_EXPLORER_API
const successExecutionData = await getSuccessExecutionData(contract, eventName, web3, message.id, api) const successExecutionData = await getSuccessExecutionData(contract, eventName, web3, message.id, api)
if (successExecutionData) { if (successExecutionData) {
setResult(successExecutionData) setResult(successExecutionData)
} else { } else {
@ -150,28 +152,28 @@ export const getFinalizationEvent = async (
} }
} }
const timeoutId = setTimeout( if (!isCancelled()) {
() => const timeoutId = setTimeout(
getFinalizationEvent( () =>
fromHome, getFinalizationEvent(
contract, fromHome,
eventName, contract,
web3, web3,
setResult, setResult,
waitingBlocksResolved, message,
message, setTimeoutId,
interval, isCancelled,
subscriptions, startBlock,
startBlock, collectedSignaturesEvent,
collectedSignaturesEvent, getFailedExecution,
getFailedExecution, setFailedExecution,
setFailedExecution, getPendingExecution,
getPendingExecution, setPendingExecution,
setPendingExecution, setExecutionEventsFetched
setExecutionEventsFetched ),
), fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL
interval )
) setTimeoutId(timeoutId)
subscriptions.push(timeoutId) }
} }
} }

@ -1,49 +0,0 @@
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
import { BlockNumberProvider } from '../services/BlockNumberProvider'
export const checkSignaturesWaitingForBLocks = async (
targetBlock: number,
setWaitingStatus: Function,
setWaitingBlocksResolved: Function,
validatorList: string[],
setConfirmations: Function,
blockProvider: BlockNumberProvider,
interval: number,
subscriptions: number[]
) => {
const currentBlock = blockProvider.get()
if (currentBlock && currentBlock >= targetBlock) {
setWaitingBlocksResolved(true)
setWaitingStatus(false)
} else {
let nextInterval = interval
if (!currentBlock) {
nextInterval = 500
} else {
const validatorsWaiting = validatorList.map(validator => {
return {
validator,
status: VALIDATOR_CONFIRMATION_STATUS.WAITING
}
})
setWaitingStatus(true)
setConfirmations(validatorsWaiting)
}
const timeoutId = setTimeout(
() =>
checkSignaturesWaitingForBLocks(
targetBlock,
setWaitingStatus,
setWaitingBlocksResolved,
validatorList,
setConfirmations,
blockProvider,
interval,
subscriptions
),
nextInterval
)
subscriptions.push(timeoutId)
}
}