Improve performance of BS requests in ALM (#516)
This commit is contained in:
parent
626f9376b2
commit
9fd3f6ab82
@ -39,10 +39,17 @@ export interface ConfirmationsContainerParams {
|
||||
message: MessageObject
|
||||
receipt: Maybe<TransactionReceipt>
|
||||
fromHome: boolean
|
||||
timestamp: number
|
||||
homeStartBlock: Maybe<number>
|
||||
foreignStartBlock: Maybe<number>
|
||||
}
|
||||
|
||||
export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }: ConfirmationsContainerParams) => {
|
||||
export const ConfirmationsContainer = ({
|
||||
message,
|
||||
receipt,
|
||||
fromHome,
|
||||
homeStartBlock,
|
||||
foreignStartBlock
|
||||
}: ConfirmationsContainerParams) => {
|
||||
const {
|
||||
home: { name: homeName },
|
||||
foreign: { name: foreignName }
|
||||
@ -62,7 +69,8 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }
|
||||
message,
|
||||
receipt,
|
||||
fromHome,
|
||||
timestamp,
|
||||
homeStartBlock,
|
||||
foreignStartBlock,
|
||||
requiredSignatures,
|
||||
validatorList,
|
||||
blockConfirmations
|
||||
|
@ -10,6 +10,7 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||
import { ConfirmationsContainer } from './ConfirmationsContainer'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { BackButton } from './commons/BackButton'
|
||||
import { useClosestBlock } from '../hooks/useClosestBlock'
|
||||
|
||||
export interface StatusContainerParam {
|
||||
onBackToMain: () => void
|
||||
@ -23,12 +24,15 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
|
||||
const { chainId, txHash, messageIdParam } = useParams()
|
||||
const validChainId = chainId === home.chainId.toString() || chainId === foreign.chainId.toString()
|
||||
const validParameters = validChainId && validTxHash(txHash)
|
||||
const isHome = chainId === home.chainId.toString()
|
||||
|
||||
const { messages, receipt, status, description, timestamp, loading } = useTransactionStatus({
|
||||
txHash: validParameters ? txHash : '',
|
||||
chainId: validParameters ? parseInt(chainId) : 0,
|
||||
receiptParam
|
||||
})
|
||||
const homeStartBlock = useClosestBlock(true, isHome, receipt, timestamp)
|
||||
const foreignStartBlock = useClosestBlock(false, isHome, receipt, timestamp)
|
||||
|
||||
const selectedMessageId = messageIdParam === undefined || messages[messageIdParam] === undefined ? -1 : messageIdParam
|
||||
|
||||
@ -64,7 +68,6 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
|
||||
const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash
|
||||
const formattedMessageId = formatTxHash(displayReference)
|
||||
|
||||
const isHome = chainId === home.chainId.toString()
|
||||
const txExplorerLink = getExplorerTxUrl(txHash, isHome)
|
||||
const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND
|
||||
|
||||
@ -101,7 +104,13 @@ export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptPar
|
||||
)}
|
||||
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}
|
||||
{displayConfirmations && (
|
||||
<ConfirmationsContainer message={messageToConfirm} receipt={receipt} fromHome={isHome} timestamp={timestamp} />
|
||||
<ConfirmationsContainer
|
||||
message={messageToConfirm}
|
||||
receipt={receipt}
|
||||
fromHome={isHome}
|
||||
homeStartBlock={homeStartBlock}
|
||||
foreignStartBlock={foreignStartBlock}
|
||||
/>
|
||||
)}
|
||||
<BackButton onBackToMain={onBackToMain} />
|
||||
</div>
|
||||
|
@ -18,9 +18,8 @@ export const ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION: boolean =
|
||||
|
||||
export const HOME_RPC_POLLING_INTERVAL: number = 5000
|
||||
export const FOREIGN_RPC_POLLING_INTERVAL: number = 5000
|
||||
export const BLOCK_RANGE: number = 50
|
||||
export const ONE_DAY_TIMESTAMP: number = 86400
|
||||
export const THREE_DAYS_TIMESTAMP: number = 259200
|
||||
export const BLOCK_RANGE: number = 500
|
||||
export const MAX_TX_SEARCH_BLOCK_RANGE: number = 10000
|
||||
|
||||
export const EXECUTE_AFFIRMATION_HASH = 'e7a2c01f'
|
||||
export const SUBMIT_SIGNATURE_HASH = '630cea8e'
|
||||
|
68
alm/src/hooks/useClosestBlock.ts
Normal file
68
alm/src/hooks/useClosestBlock.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants'
|
||||
import { getClosestBlockByTimestamp } from '../utils/explorer'
|
||||
|
||||
export function useClosestBlock(
|
||||
searchHome: boolean,
|
||||
fromHome: boolean,
|
||||
receipt: Maybe<TransactionReceipt>,
|
||||
timestamp: number
|
||||
) {
|
||||
const { home, foreign } = useStateProvider()
|
||||
const [blockNumber, setBlockNumber] = useState<number | null>(null)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!receipt || blockNumber || !timestamp) return
|
||||
|
||||
if (fromHome === searchHome) {
|
||||
setBlockNumber(receipt.blockNumber)
|
||||
return
|
||||
}
|
||||
|
||||
const web3 = searchHome ? home.web3 : foreign.web3
|
||||
if (!web3) return
|
||||
|
||||
const getBlock = async () => {
|
||||
// try to fast-fetch closest block number from the chain explorer
|
||||
try {
|
||||
const api = searchHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API
|
||||
setBlockNumber(await getClosestBlockByTimestamp(api, timestamp))
|
||||
return
|
||||
} catch {}
|
||||
|
||||
const lastBlock = await web3.eth.getBlock('latest')
|
||||
if (lastBlock.timestamp <= timestamp) {
|
||||
setBlockNumber(lastBlock.number)
|
||||
return
|
||||
}
|
||||
|
||||
const oldBlock = await web3.eth.getBlock(Math.max(lastBlock.number - 10000, 1))
|
||||
const blockDiff = lastBlock.number - oldBlock.number
|
||||
const timeDiff = (lastBlock.timestamp as number) - (oldBlock.timestamp as number)
|
||||
const averageBlockTime = timeDiff / blockDiff
|
||||
let currentBlock = lastBlock
|
||||
|
||||
let prevBlockDiff = Infinity
|
||||
while (true) {
|
||||
const timeDiff = (currentBlock.timestamp as number) - timestamp
|
||||
const blockDiff = Math.ceil(timeDiff / averageBlockTime)
|
||||
if (Math.abs(blockDiff) < 5 || Math.abs(blockDiff) >= Math.abs(prevBlockDiff)) {
|
||||
setBlockNumber(currentBlock.number - blockDiff - 5)
|
||||
break
|
||||
}
|
||||
|
||||
prevBlockDiff = blockDiff
|
||||
currentBlock = await web3.eth.getBlock(currentBlock.number - blockDiff)
|
||||
}
|
||||
}
|
||||
|
||||
getBlock()
|
||||
},
|
||||
[blockNumber, foreign.web3, fromHome, home.web3, receipt, searchHome, timestamp]
|
||||
)
|
||||
|
||||
return blockNumber
|
||||
}
|
@ -28,7 +28,8 @@ export interface useMessageConfirmationsParams {
|
||||
message: MessageObject
|
||||
receipt: Maybe<TransactionReceipt>
|
||||
fromHome: boolean
|
||||
timestamp: number
|
||||
homeStartBlock: Maybe<number>
|
||||
foreignStartBlock: Maybe<number>
|
||||
requiredSignatures: number
|
||||
validatorList: string[]
|
||||
blockConfirmations: number
|
||||
@ -56,7 +57,8 @@ export const useMessageConfirmations = ({
|
||||
message,
|
||||
receipt,
|
||||
fromHome,
|
||||
timestamp,
|
||||
homeStartBlock,
|
||||
foreignStartBlock,
|
||||
requiredSignatures,
|
||||
validatorList,
|
||||
blockConfirmations
|
||||
@ -90,6 +92,17 @@ export const useMessageConfirmations = ({
|
||||
return filteredList.length > 0
|
||||
}
|
||||
|
||||
// start watching blocks at the start
|
||||
useEffect(
|
||||
() => {
|
||||
if (!home.web3 || !foreign.web3) return
|
||||
|
||||
homeBlockNumberProvider.start(home.web3)
|
||||
foreignBlockNumberProvider.start(foreign.web3)
|
||||
},
|
||||
[foreign.web3, home.web3]
|
||||
)
|
||||
|
||||
// Check if the validators are waiting for block confirmations to verify the message
|
||||
useEffect(
|
||||
() => {
|
||||
@ -105,9 +118,6 @@ export const useMessageConfirmations = ({
|
||||
|
||||
const blockProvider = fromHome ? homeBlockNumberProvider : foreignBlockNumberProvider
|
||||
const interval = fromHome ? HOME_RPC_POLLING_INTERVAL : FOREIGN_RPC_POLLING_INTERVAL
|
||||
const web3 = fromHome ? home.web3 : foreign.web3
|
||||
blockProvider.start(web3)
|
||||
|
||||
const targetBlock = receipt.blockNumber + blockConfirmations
|
||||
|
||||
checkSignaturesWaitingForBLocks(
|
||||
@ -123,7 +133,6 @@ export const useMessageConfirmations = ({
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
blockProvider.stop()
|
||||
}
|
||||
},
|
||||
[
|
||||
@ -153,8 +162,6 @@ export const useMessageConfirmations = ({
|
||||
})
|
||||
}
|
||||
|
||||
homeBlockNumberProvider.start(home.web3)
|
||||
|
||||
const fromBlock = receipt.blockNumber
|
||||
const toBlock = fromBlock + BLOCK_RANGE
|
||||
const messageHash = home.web3.utils.soliditySha3Raw(message.data)
|
||||
@ -171,7 +178,6 @@ export const useMessageConfirmations = ({
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
homeBlockNumberProvider.stop()
|
||||
}
|
||||
},
|
||||
[fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected]
|
||||
@ -192,7 +198,6 @@ export const useMessageConfirmations = ({
|
||||
})
|
||||
}
|
||||
|
||||
homeBlockNumberProvider.start(home.web3)
|
||||
const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations
|
||||
|
||||
checkWaitingBlocksForExecution(
|
||||
@ -208,7 +213,6 @@ export const useMessageConfirmations = ({
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
homeBlockNumberProvider.stop()
|
||||
}
|
||||
},
|
||||
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt, waitingBlocksForExecutionResolved]
|
||||
@ -218,7 +222,7 @@ export const useMessageConfirmations = ({
|
||||
// To avoid making extra requests, this is only executed when validators finished waiting for blocks confirmations
|
||||
useEffect(
|
||||
() => {
|
||||
if (!waitingBlocksResolved || !timestamp || !requiredSignatures) return
|
||||
if (!waitingBlocksResolved || !homeStartBlock || !requiredSignatures) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
@ -239,7 +243,7 @@ export const useMessageConfirmations = ({
|
||||
setSignatureCollected,
|
||||
waitingBlocksResolved,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
homeStartBlock,
|
||||
getValidatorFailedTransactionsForMessage,
|
||||
setFailedConfirmations,
|
||||
getValidatorPendingTransactionsForMessage,
|
||||
@ -259,7 +263,7 @@ export const useMessageConfirmations = ({
|
||||
home.bridgeContract,
|
||||
requiredSignatures,
|
||||
waitingBlocksResolved,
|
||||
timestamp,
|
||||
homeStartBlock,
|
||||
setConfirmations
|
||||
]
|
||||
)
|
||||
@ -270,6 +274,8 @@ export const useMessageConfirmations = ({
|
||||
useEffect(
|
||||
() => {
|
||||
if ((fromHome && !waitingBlocksForExecutionResolved) || (!fromHome && !waitingBlocksResolved)) return
|
||||
const startBlock = fromHome ? foreignStartBlock : homeStartBlock
|
||||
if (!startBlock) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
@ -285,6 +291,7 @@ export const useMessageConfirmations = ({
|
||||
const interval = fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL
|
||||
|
||||
getFinalizationEvent(
|
||||
fromHome,
|
||||
bridgeContract,
|
||||
contractEvent,
|
||||
providedWeb3,
|
||||
@ -293,7 +300,7 @@ export const useMessageConfirmations = ({
|
||||
message,
|
||||
interval,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
startBlock,
|
||||
collectedSignaturesEvent,
|
||||
getExecutionFailedTransactionForMessage,
|
||||
setFailedExecution,
|
||||
@ -315,8 +322,9 @@ export const useMessageConfirmations = ({
|
||||
home.web3,
|
||||
waitingBlocksResolved,
|
||||
waitingBlocksForExecutionResolved,
|
||||
timestamp,
|
||||
collectedSignaturesEvent
|
||||
collectedSignaturesEvent,
|
||||
foreignStartBlock,
|
||||
homeStartBlock
|
||||
]
|
||||
)
|
||||
|
||||
@ -328,6 +336,9 @@ export const useMessageConfirmations = ({
|
||||
? CONFIRMATIONS_STATUS.SUCCESS
|
||||
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
|
||||
setStatus(newStatus)
|
||||
|
||||
foreignBlockNumberProvider.stop()
|
||||
homeBlockNumberProvider.stop()
|
||||
} else if (signatureCollected) {
|
||||
if (fromHome) {
|
||||
if (waitingBlocksForExecution) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Web3 from 'web3'
|
||||
import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'
|
||||
import { HOME_RPC_POLLING_INTERVAL } from '../config/constants'
|
||||
import { FOREIGN_RPC_POLLING_INTERVAL, HOME_RPC_POLLING_INTERVAL } from '../config/constants'
|
||||
|
||||
export class BlockNumberProvider {
|
||||
private running: number
|
||||
@ -61,4 +61,4 @@ export class BlockNumberProvider {
|
||||
}
|
||||
|
||||
export const homeBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL)
|
||||
export const foreignBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL)
|
||||
export const foreignBlockNumberProvider = new BlockNumberProvider(FOREIGN_RPC_POLLING_INTERVAL)
|
||||
|
@ -17,19 +17,33 @@ const otherAddress = '0xD4075FB57fCf038bFc702c915Ef9592534bED5c1'
|
||||
|
||||
describe('getFailedTransactions', () => {
|
||||
test('should only return failed transactions', async () => {
|
||||
const transactions = [{ isError: '0' }, { isError: '1' }, { isError: '0' }, { isError: '1' }, { isError: '1' }]
|
||||
const to = otherAddress
|
||||
const transactions = [
|
||||
{ isError: '0', to },
|
||||
{ isError: '1', to },
|
||||
{ isError: '0', to },
|
||||
{ isError: '1', to },
|
||||
{ isError: '1', to }
|
||||
]
|
||||
|
||||
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||
const result = await getFailedTransactions('', '', 0, 1, '', fetchAccountTransactions)
|
||||
const result = await getFailedTransactions('', to, 0, 1, '', fetchAccountTransactions)
|
||||
expect(result.length).toEqual(3)
|
||||
})
|
||||
})
|
||||
describe('getSuccessTransactions', () => {
|
||||
test('should only return success transactions', async () => {
|
||||
const transactions = [{ isError: '0' }, { isError: '1' }, { isError: '0' }, { isError: '1' }, { isError: '1' }]
|
||||
const to = otherAddress
|
||||
const transactions = [
|
||||
{ isError: '0', to },
|
||||
{ isError: '1', to },
|
||||
{ isError: '0', to },
|
||||
{ isError: '1', to },
|
||||
{ isError: '1', to }
|
||||
]
|
||||
|
||||
const fetchAccountTransactions = jest.fn().mockImplementation(() => transactions)
|
||||
const result = await getSuccessTransactions('', '', 0, 1, '', fetchAccountTransactions)
|
||||
const result = await getSuccessTransactions('', to, 0, 1, '', fetchAccountTransactions)
|
||||
expect(result.length).toEqual(2)
|
||||
})
|
||||
})
|
||||
@ -74,8 +88,8 @@ describe('getExecutionFailedTransactionForMessage', () => {
|
||||
account: '',
|
||||
to: '',
|
||||
messageData,
|
||||
startTimestamp: 0,
|
||||
endTimestamp: 1
|
||||
startBlock: 0,
|
||||
endBlock: 1
|
||||
},
|
||||
fetchAccountTransactions
|
||||
)
|
||||
|
@ -64,6 +64,7 @@ describe('getFinalizationEvent', () => {
|
||||
const setExecutionEventsFetched = jest.fn()
|
||||
|
||||
await getFinalizationEvent(
|
||||
true,
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
@ -115,6 +116,7 @@ describe('getFinalizationEvent', () => {
|
||||
const setExecutionEventsFetched = jest.fn()
|
||||
|
||||
await getFinalizationEvent(
|
||||
true,
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
@ -166,6 +168,7 @@ describe('getFinalizationEvent', () => {
|
||||
const setExecutionEventsFetched = jest.fn()
|
||||
|
||||
await getFinalizationEvent(
|
||||
true,
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
@ -217,6 +220,7 @@ describe('getFinalizationEvent', () => {
|
||||
const setExecutionEventsFetched = jest.fn()
|
||||
|
||||
await getFinalizationEvent(
|
||||
true,
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
@ -275,6 +279,7 @@ describe('getFinalizationEvent', () => {
|
||||
const setExecutionEventsFetched = jest.fn()
|
||||
|
||||
await getFinalizationEvent(
|
||||
true,
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
|
@ -30,7 +30,6 @@ export const checkWaitingBlocksForExecution = async (
|
||||
)
|
||||
setWaitingBlocksForExecutionResolved(true)
|
||||
setWaitingBlocksForExecution(false)
|
||||
blockProvider.stop()
|
||||
} else {
|
||||
let nextInterval = interval
|
||||
if (!currentBlock) {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import {
|
||||
BLOCK_RANGE,
|
||||
EXECUTE_AFFIRMATION_HASH,
|
||||
EXECUTE_SIGNATURES_HASH,
|
||||
FOREIGN_EXPLORER_API,
|
||||
HOME_EXPLORER_API,
|
||||
MAX_TX_SEARCH_BLOCK_RANGE,
|
||||
SUBMIT_SIGNATURE_HASH
|
||||
} from '../config/constants'
|
||||
|
||||
@ -12,6 +14,7 @@ export interface APITransaction {
|
||||
input: string
|
||||
to: string
|
||||
hash: string
|
||||
blockNumber: string
|
||||
}
|
||||
|
||||
export interface APIPendingTransaction {
|
||||
@ -27,106 +30,42 @@ export interface PendingTransactionsParams {
|
||||
|
||||
export interface AccountTransactionsParams {
|
||||
account: string
|
||||
to: string
|
||||
startTimestamp: number
|
||||
endTimestamp: number
|
||||
startBlock: number
|
||||
endBlock: number
|
||||
api: string
|
||||
}
|
||||
|
||||
export interface GetFailedTransactionParams {
|
||||
account: string
|
||||
to: string
|
||||
messageData: string
|
||||
startTimestamp: number
|
||||
endTimestamp: number
|
||||
}
|
||||
|
||||
export interface GetPendingTransactionParams {
|
||||
account: string
|
||||
to: string
|
||||
messageData: string
|
||||
}
|
||||
|
||||
export const fetchAccountTransactionsFromBlockscout = async ({
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
api
|
||||
}: AccountTransactionsParams): Promise<APITransaction[]> => {
|
||||
const url = `${api}?module=account&action=txlist&address=${account}&filterby=from=${account}&to=${to}&starttimestamp=${startTimestamp}&endtimestamp=${endTimestamp}`
|
||||
export interface GetTransactionParams extends GetPendingTransactionParams {
|
||||
startBlock: number
|
||||
endBlock: number
|
||||
}
|
||||
|
||||
export const fetchAccountTransactions = async ({ account, startBlock, endBlock, api }: AccountTransactionsParams) => {
|
||||
const params = `module=account&action=txlist&address=${account}&filterby=from&startblock=${startBlock}&endblock=${endBlock}`
|
||||
const url = api.includes('blockscout') ? `${api}?${params}` : `${api}&${params}`
|
||||
|
||||
try {
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
if (result.status === '0') {
|
||||
|
||||
if (result.message === 'No transactions found') {
|
||||
return []
|
||||
}
|
||||
|
||||
return result.result
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const getBlockByTimestampUrl = (api: string, timestamp: number) =>
|
||||
`${api}&module=block&action=getblocknobytime×tamp=${timestamp}&closest=before`
|
||||
|
||||
export const fetchAccountTransactionsFromEtherscan = async ({
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
api
|
||||
}: AccountTransactionsParams): Promise<APITransaction[]> => {
|
||||
const startBlockUrl = getBlockByTimestampUrl(api, startTimestamp)
|
||||
const endBlockUrl = getBlockByTimestampUrl(api, endTimestamp)
|
||||
let fromBlock = 0
|
||||
let toBlock = 9999999999999
|
||||
try {
|
||||
const [fromBlockResult, toBlockResult] = await Promise.all([
|
||||
fetch(startBlockUrl).then(res => res.json()),
|
||||
fetch(endBlockUrl).then(res => res.json())
|
||||
])
|
||||
|
||||
if (fromBlockResult.status !== '0') {
|
||||
fromBlock = parseInt(fromBlockResult.result)
|
||||
}
|
||||
|
||||
if (toBlockResult.status !== '0') {
|
||||
toBlock = parseInt(toBlockResult.result)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
|
||||
const url = `${api}&module=account&action=txlist&address=${account}&startblock=${fromBlock}&endblock=${toBlock}`
|
||||
|
||||
try {
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
|
||||
if (result.status === '0') {
|
||||
return []
|
||||
}
|
||||
|
||||
const toAddressLowerCase = to.toLowerCase()
|
||||
const transactions: APITransaction[] = result.result
|
||||
return transactions.filter(t => t.to.toLowerCase() === toAddressLowerCase)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchAccountTransactions = (api: string) => {
|
||||
return api.includes('blockscout') ? fetchAccountTransactionsFromBlockscout : fetchAccountTransactionsFromEtherscan
|
||||
}
|
||||
|
||||
export const fetchPendingTransactions = async ({
|
||||
account,
|
||||
api
|
||||
}: PendingTransactionsParams): Promise<APIPendingTransaction[]> => {
|
||||
if (!api.includes('blockscout')) {
|
||||
return []
|
||||
}
|
||||
const url = `${api}?module=account&action=pendingtxlist&address=${account}`
|
||||
|
||||
try {
|
||||
@ -141,30 +80,96 @@ export const fetchPendingTransactions = async ({
|
||||
}
|
||||
}
|
||||
|
||||
export const getClosestBlockByTimestamp = async (api: string, timestamp: number): Promise<number> => {
|
||||
if (api.includes('blockscout')) {
|
||||
throw new Error('Blockscout does not support getblocknobytime')
|
||||
}
|
||||
|
||||
const url = `${api}&module=block&action=getblocknobytime×tamp=${timestamp}&closest=before`
|
||||
|
||||
const blockNumber = await fetch(url).then(res => res.json())
|
||||
|
||||
return parseInt(blockNumber.result)
|
||||
}
|
||||
|
||||
// fast version of fetchAccountTransactions
|
||||
// sequentially fetches transactions in small batches
|
||||
// caches the result
|
||||
const transactionsCache: { [key: string]: { lastBlock: number; transactions: APITransaction[] } } = {}
|
||||
export const getAccountTransactions = async ({
|
||||
account,
|
||||
startBlock,
|
||||
endBlock,
|
||||
api
|
||||
}: AccountTransactionsParams): Promise<APITransaction[]> => {
|
||||
const key = `${account}-${startBlock}-${api}`
|
||||
|
||||
// initialize empty cache if it doesn't exist yet
|
||||
if (!transactionsCache[key]) {
|
||||
transactionsCache[key] = { lastBlock: startBlock - 1, transactions: [] }
|
||||
}
|
||||
|
||||
// if cache contains events up to block X,
|
||||
// new batch is fetched for range [X + 1, X + 1 + BLOCK_RANGE]
|
||||
const newStartBlock = transactionsCache[key].lastBlock + 1
|
||||
const newEndBlock = newStartBlock + BLOCK_RANGE
|
||||
|
||||
// search for new transactions only if max allowed block range is not yet exceeded
|
||||
if (newEndBlock <= startBlock + MAX_TX_SEARCH_BLOCK_RANGE) {
|
||||
const newTransactions = await fetchAccountTransactions({
|
||||
account,
|
||||
startBlock: newStartBlock,
|
||||
endBlock: newEndBlock,
|
||||
api
|
||||
})
|
||||
|
||||
const transactions = transactionsCache[key].transactions.concat(...newTransactions)
|
||||
|
||||
// cache updated transactions list
|
||||
transactionsCache[key].transactions = transactions
|
||||
|
||||
// enbBlock is assumed to be the current block number of the chain
|
||||
// if the whole range is finalized, last block can be safely updated to the end of the range
|
||||
// this works even if there are no transactions in the list
|
||||
if (newEndBlock < endBlock) {
|
||||
transactionsCache[key].lastBlock = newEndBlock
|
||||
} else if (transactions.length > 0) {
|
||||
transactionsCache[key].lastBlock = parseInt(transactions[transactions.length - 1].blockNumber, 10)
|
||||
}
|
||||
|
||||
return transactions
|
||||
}
|
||||
|
||||
console.warn(`Reached max transaction searching range, returning previously cached transactions for ${account}`)
|
||||
return transactionsCache[key].transactions
|
||||
}
|
||||
|
||||
const filterReceiver = (to: string) => (tx: APITransaction) => tx.to.toLowerCase() === to.toLowerCase()
|
||||
|
||||
export const getFailedTransactions = async (
|
||||
account: string,
|
||||
to: string,
|
||||
startTimestamp: number,
|
||||
endTimestamp: number,
|
||||
startBlock: number,
|
||||
endBlock: number,
|
||||
api: string,
|
||||
fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
|
||||
getAccountTransactionsMethod = getAccountTransactions
|
||||
): Promise<APITransaction[]> => {
|
||||
const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
|
||||
const transactions = await getAccountTransactionsMethod({ account, startBlock, endBlock, api })
|
||||
|
||||
return transactions.filter(t => t.isError !== '0')
|
||||
return transactions.filter(t => t.isError !== '0').filter(filterReceiver(to))
|
||||
}
|
||||
|
||||
export const getSuccessTransactions = async (
|
||||
account: string,
|
||||
to: string,
|
||||
startTimestamp: number,
|
||||
endTimestamp: number,
|
||||
startBlock: number,
|
||||
endBlock: number,
|
||||
api: string,
|
||||
fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
|
||||
getAccountTransactionsMethod = getAccountTransactions
|
||||
): Promise<APITransaction[]> => {
|
||||
const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
|
||||
const transactions = await getAccountTransactionsMethod({ account, startBlock, endBlock, api })
|
||||
|
||||
return transactions.filter(t => t.isError === '0')
|
||||
return transactions.filter(t => t.isError === '0').filter(filterReceiver(to))
|
||||
}
|
||||
|
||||
export const filterValidatorSignatureTransaction = (
|
||||
@ -183,17 +188,10 @@ export const getValidatorFailedTransactionsForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData,
|
||||
startTimestamp,
|
||||
endTimestamp
|
||||
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||
const failedTransactions = await getFailedTransactions(
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
HOME_EXPLORER_API,
|
||||
fetchAccountTransactionsFromBlockscout
|
||||
)
|
||||
startBlock,
|
||||
endBlock
|
||||
}: GetTransactionParams): Promise<APITransaction[]> => {
|
||||
const failedTransactions = await getFailedTransactions(account, to, startBlock, endBlock, HOME_EXPLORER_API)
|
||||
|
||||
return filterValidatorSignatureTransaction(failedTransactions, messageData)
|
||||
}
|
||||
@ -202,33 +200,19 @@ export const getValidatorSuccessTransactionsForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData,
|
||||
startTimestamp,
|
||||
endTimestamp
|
||||
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||
const transactions = await getSuccessTransactions(
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
HOME_EXPLORER_API,
|
||||
fetchAccountTransactionsFromBlockscout
|
||||
)
|
||||
startBlock,
|
||||
endBlock
|
||||
}: GetTransactionParams): Promise<APITransaction[]> => {
|
||||
const transactions = await getSuccessTransactions(account, to, startBlock, endBlock, HOME_EXPLORER_API)
|
||||
|
||||
return filterValidatorSignatureTransaction(transactions, messageData)
|
||||
}
|
||||
|
||||
export const getExecutionFailedTransactionForMessage = async (
|
||||
{ account, to, messageData, startTimestamp, endTimestamp }: GetFailedTransactionParams,
|
||||
{ account, to, messageData, startBlock, endBlock }: GetTransactionParams,
|
||||
getFailedTransactionsMethod = getFailedTransactions
|
||||
): Promise<APITransaction[]> => {
|
||||
const failedTransactions = await getFailedTransactionsMethod(
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
FOREIGN_EXPLORER_API,
|
||||
fetchAccountTransactions(FOREIGN_EXPLORER_API)
|
||||
)
|
||||
const failedTransactions = await getFailedTransactionsMethod(account, to, startBlock, endBlock, FOREIGN_EXPLORER_API)
|
||||
|
||||
const messageDataValue = messageData.replace('0x', '')
|
||||
return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue))
|
||||
|
@ -31,7 +31,6 @@ export const getCollectedSignaturesEvent = async (
|
||||
if (filteredEvents.length) {
|
||||
const event = filteredEvents[0]
|
||||
setCollectedSignaturesEvent(event)
|
||||
homeBlockNumberProvider.stop()
|
||||
} else {
|
||||
const newFromBlock = currentBlock ? securedToBlock : fromBlock
|
||||
const newToBlock = currentBlock ? toBlock + BLOCK_RANGE : toBlock
|
||||
|
@ -1,12 +1,7 @@
|
||||
import Web3 from 'web3'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import { HOME_RPC_POLLING_INTERVAL, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import {
|
||||
GetFailedTransactionParams,
|
||||
APITransaction,
|
||||
APIPendingTransaction,
|
||||
GetPendingTransactionParams
|
||||
} from './explorer'
|
||||
import { GetTransactionParams, APITransaction, APIPendingTransaction, GetPendingTransactionParams } from './explorer'
|
||||
import { getAffirmationsSigned, getMessagesSigned } from './contract'
|
||||
import {
|
||||
getValidatorConfirmation,
|
||||
@ -43,12 +38,12 @@ export const getConfirmationsForTx = async (
|
||||
setSignatureCollected: Function,
|
||||
waitingBlocksResolved: boolean,
|
||||
subscriptions: number[],
|
||||
timestamp: number,
|
||||
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
|
||||
startBlock: number,
|
||||
getFailedTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>,
|
||||
setFailedConfirmations: Function,
|
||||
getPendingTransactions: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
|
||||
setPendingConfirmations: Function,
|
||||
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
|
||||
) => {
|
||||
if (!web3 || !validatorList || !validatorList.length || !bridgeContract || !waitingBlocksResolved) return
|
||||
|
||||
@ -102,7 +97,7 @@ export const getConfirmationsForTx = async (
|
||||
// Check if confirmation failed
|
||||
const validatorFailedConfirmationsChecks = await Promise.all(
|
||||
undefinedConfirmations.map(
|
||||
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
|
||||
getValidatorFailedTransaction(bridgeContract, messageData, startBlock, getFailedTransactions)
|
||||
)
|
||||
)
|
||||
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
|
||||
@ -136,7 +131,7 @@ export const getConfirmationsForTx = async (
|
||||
// get transactions from success signatures
|
||||
const successConfirmationWithData = await Promise.all(
|
||||
successConfirmations.map(
|
||||
getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, timestamp, getSuccessTransactions)
|
||||
getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, startBlock, getSuccessTransactions)
|
||||
)
|
||||
)
|
||||
|
||||
@ -162,7 +157,7 @@ export const getConfirmationsForTx = async (
|
||||
setSignatureCollected,
|
||||
waitingBlocksResolved,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
startBlock,
|
||||
getFailedTransactions,
|
||||
setFailedConfirmations,
|
||||
getPendingTransactions,
|
||||
|
@ -1,15 +1,11 @@
|
||||
import { Contract, EventData } from 'web3-eth-contract'
|
||||
import Web3 from 'web3'
|
||||
import { CACHE_KEY_EXECUTION_FAILED, THREE_DAYS_TIMESTAMP, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { CACHE_KEY_EXECUTION_FAILED, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
||||
import {
|
||||
APIPendingTransaction,
|
||||
APITransaction,
|
||||
GetFailedTransactionParams,
|
||||
GetPendingTransactionParams
|
||||
} from './explorer'
|
||||
import { APIPendingTransaction, APITransaction, GetTransactionParams, GetPendingTransactionParams } from './explorer'
|
||||
import { getBlock, MessageObject } from './web3'
|
||||
import validatorsCache from '../services/ValidatorsCache'
|
||||
import { foreignBlockNumberProvider, homeBlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
|
||||
export const getSuccessExecutionData = async (contract: Contract, eventName: string, web3: Web3, messageId: string) => {
|
||||
// Since it filters by the message id, only one event will be fetched
|
||||
@ -43,6 +39,7 @@ export const getSuccessExecutionData = async (contract: Contract, eventName: str
|
||||
}
|
||||
|
||||
export const getFinalizationEvent = async (
|
||||
fromHome: boolean,
|
||||
contract: Maybe<Contract>,
|
||||
eventName: string,
|
||||
web3: Maybe<Web3>,
|
||||
@ -51,9 +48,9 @@ export const getFinalizationEvent = async (
|
||||
message: MessageObject,
|
||||
interval: number,
|
||||
subscriptions: number[],
|
||||
timestamp: number,
|
||||
startBlock: number,
|
||||
collectedSignaturesEvent: Maybe<EventData>,
|
||||
getFailedExecution: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
|
||||
getFailedExecution: (args: GetTransactionParams) => Promise<APITransaction[]>,
|
||||
setFailedExecution: Function,
|
||||
getPendingExecution: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
|
||||
setPendingExecution: Function,
|
||||
@ -92,14 +89,15 @@ export const getFinalizationEvent = async (
|
||||
} else {
|
||||
const validatorExecutionCacheKey = `${CACHE_KEY_EXECUTION_FAILED}${validator}-${message.id}`
|
||||
const failedFromCache = validatorsCache.get(validatorExecutionCacheKey)
|
||||
const blockProvider = fromHome ? foreignBlockNumberProvider : homeBlockNumberProvider
|
||||
|
||||
if (!failedFromCache) {
|
||||
const failedTransactions = await getFailedExecution({
|
||||
account: validator,
|
||||
to: contract.options.address,
|
||||
messageData: message.data,
|
||||
startTimestamp: timestamp,
|
||||
endTimestamp: timestamp + THREE_DAYS_TIMESTAMP
|
||||
startBlock,
|
||||
endBlock: blockProvider.get() || 0
|
||||
})
|
||||
|
||||
if (failedTransactions.length > 0) {
|
||||
@ -125,6 +123,7 @@ export const getFinalizationEvent = async (
|
||||
const timeoutId = setTimeout(
|
||||
() =>
|
||||
getFinalizationEvent(
|
||||
fromHome,
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
@ -133,7 +132,7 @@ export const getFinalizationEvent = async (
|
||||
message,
|
||||
interval,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
startBlock,
|
||||
collectedSignaturesEvent,
|
||||
getFailedExecution,
|
||||
setFailedExecution,
|
||||
|
@ -16,7 +16,6 @@ export const checkSignaturesWaitingForBLocks = async (
|
||||
if (currentBlock && currentBlock >= targetBlock) {
|
||||
setWaitingBlocksResolved(true)
|
||||
setWaitingStatus(false)
|
||||
blockProvider.stop()
|
||||
} else {
|
||||
let nextInterval = interval
|
||||
if (!currentBlock) {
|
||||
|
@ -2,18 +2,9 @@ import Web3 from 'web3'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
|
||||
import validatorsCache from '../services/ValidatorsCache'
|
||||
import {
|
||||
CACHE_KEY_FAILED,
|
||||
CACHE_KEY_SUCCESS,
|
||||
ONE_DAY_TIMESTAMP,
|
||||
VALIDATOR_CONFIRMATION_STATUS
|
||||
} from '../config/constants'
|
||||
import {
|
||||
APIPendingTransaction,
|
||||
APITransaction,
|
||||
GetFailedTransactionParams,
|
||||
GetPendingTransactionParams
|
||||
} from './explorer'
|
||||
import { CACHE_KEY_FAILED, CACHE_KEY_SUCCESS, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { APIPendingTransaction, APITransaction, GetTransactionParams, GetPendingTransactionParams } from './explorer'
|
||||
import { homeBlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
|
||||
export const getValidatorConfirmation = (
|
||||
web3: Web3,
|
||||
@ -50,8 +41,8 @@ export const getSuccessExecutionTransaction = (
|
||||
bridgeContract: Contract,
|
||||
fromHome: boolean,
|
||||
messageData: string,
|
||||
timestamp: number,
|
||||
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||
startBlock: number,
|
||||
getSuccessTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
|
||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||
const { validator } = validatorData
|
||||
const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}`
|
||||
@ -65,8 +56,8 @@ export const getSuccessExecutionTransaction = (
|
||||
account: validatorData.validator,
|
||||
to: bridgeContract.options.address,
|
||||
messageData,
|
||||
startTimestamp: timestamp,
|
||||
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||
startBlock,
|
||||
endBlock: homeBlockNumberProvider.get() || 0
|
||||
})
|
||||
|
||||
let txHashTimestamp = 0
|
||||
@ -98,8 +89,8 @@ export const getSuccessExecutionTransaction = (
|
||||
export const getValidatorFailedTransaction = (
|
||||
bridgeContract: Contract,
|
||||
messageData: string,
|
||||
timestamp: number,
|
||||
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||
startBlock: number,
|
||||
getFailedTransactions: (args: GetTransactionParams) => Promise<APITransaction[]>
|
||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}`
|
||||
const failedFromCache = validatorsCache.getData(validatorCacheKey)
|
||||
@ -112,8 +103,8 @@ export const getValidatorFailedTransaction = (
|
||||
account: validatorData.validator,
|
||||
to: bridgeContract.options.address,
|
||||
messageData,
|
||||
startTimestamp: timestamp,
|
||||
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||
startBlock,
|
||||
endBlock: homeBlockNumberProvider.get() || 0
|
||||
})
|
||||
const newStatus =
|
||||
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||
|
Loading…
Reference in New Issue
Block a user