Skip to content

Commit

Permalink
Improve performance of BS requests in ALM (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
k1rill-fedoseev authored Feb 26, 2021
1 parent 626f937 commit 9fd3f6a
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 193 deletions.
14 changes: 11 additions & 3 deletions alm/src/components/ConfirmationsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -62,7 +69,8 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }
message,
receipt,
fromHome,
timestamp,
homeStartBlock,
foreignStartBlock,
requiredSignatures,
validatorList,
blockConfirmations
Expand Down
13 changes: 11 additions & 2 deletions alm/src/components/StatusContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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>
Expand Down
5 changes: 2 additions & 3 deletions alm/src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
68 changes: 68 additions & 0 deletions alm/src/hooks/useClosestBlock.ts
Original file line number Diff line number Diff line change
@@ -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
}
45 changes: 28 additions & 17 deletions alm/src/hooks/useMessageConfirmations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -56,7 +57,8 @@ export const useMessageConfirmations = ({
message,
receipt,
fromHome,
timestamp,
homeStartBlock,
foreignStartBlock,
requiredSignatures,
validatorList,
blockConfirmations
Expand Down Expand Up @@ -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(
() => {
Expand All @@ -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(
Expand All @@ -123,7 +133,6 @@ export const useMessageConfirmations = ({

return () => {
unsubscribe()
blockProvider.stop()
}
},
[
Expand Down Expand Up @@ -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)
Expand All @@ -171,7 +178,6 @@ export const useMessageConfirmations = ({

return () => {
unsubscribe()
homeBlockNumberProvider.stop()
}
},
[fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected]
Expand All @@ -192,7 +198,6 @@ export const useMessageConfirmations = ({
})
}

homeBlockNumberProvider.start(home.web3)
const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations

checkWaitingBlocksForExecution(
Expand All @@ -208,7 +213,6 @@ export const useMessageConfirmations = ({

return () => {
unsubscribe()
homeBlockNumberProvider.stop()
}
},
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt, waitingBlocksForExecutionResolved]
Expand All @@ -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> = []

Expand All @@ -239,7 +243,7 @@ export const useMessageConfirmations = ({
setSignatureCollected,
waitingBlocksResolved,
subscriptions,
timestamp,
homeStartBlock,
getValidatorFailedTransactionsForMessage,
setFailedConfirmations,
getValidatorPendingTransactionsForMessage,
Expand All @@ -259,7 +263,7 @@ export const useMessageConfirmations = ({
home.bridgeContract,
requiredSignatures,
waitingBlocksResolved,
timestamp,
homeStartBlock,
setConfirmations
]
)
Expand All @@ -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> = []

Expand All @@ -285,6 +291,7 @@ export const useMessageConfirmations = ({
const interval = fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL

getFinalizationEvent(
fromHome,
bridgeContract,
contractEvent,
providedWeb3,
Expand All @@ -293,7 +300,7 @@ export const useMessageConfirmations = ({
message,
interval,
subscriptions,
timestamp,
startBlock,
collectedSignaturesEvent,
getExecutionFailedTransactionForMessage,
setFailedExecution,
Expand All @@ -315,8 +322,9 @@ export const useMessageConfirmations = ({
home.web3,
waitingBlocksResolved,
waitingBlocksForExecutionResolved,
timestamp,
collectedSignaturesEvent
collectedSignaturesEvent,
foreignStartBlock,
homeStartBlock
]
)

Expand All @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions alm/src/services/BlockNumberProvider.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
26 changes: 20 additions & 6 deletions alm/src/utils/__tests__/explorer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})
Expand Down Expand Up @@ -74,8 +88,8 @@ describe('getExecutionFailedTransactionForMessage', () => {
account: '',
to: '',
messageData,
startTimestamp: 0,
endTimestamp: 1
startBlock: 0,
endBlock: 1
},
fetchAccountTransactions
)
Expand Down
Loading

0 comments on commit 9fd3f6a

Please sign in to comment.