Add ALM failed validator transactions detection (#357)
This commit is contained in:
parent
3c956ab9ec
commit
d2606997a3
@ -9,3 +9,6 @@ ALM_FOREIGN_NETWORK_NAME=Kovan Testnet
|
|||||||
|
|
||||||
ALM_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s
|
ALM_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s
|
||||||
ALM_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
ALM_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||||
|
|
||||||
|
ALM_HOME_EXPLORER_API=https://blockscout.com/poa/sokol/api
|
||||||
|
ALM_FOREIGN_EXPLORER_API=https://kovan.etherscan.io/api
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
"@types/jest": "^24.0.0",
|
"@types/jest": "^24.0.0",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^12.0.0",
|
||||||
|
"@types/promise-retry": "^1.1.3",
|
||||||
"@types/react": "^16.9.0",
|
"@types/react": "^16.9.0",
|
||||||
"@types/react-dom": "^16.9.0",
|
"@types/react-dom": "^16.9.0",
|
||||||
"@types/react-router-dom": "^5.1.5",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
@ -16,6 +17,7 @@
|
|||||||
"customize-cra": "^1.0.0",
|
"customize-cra": "^1.0.0",
|
||||||
"date-fns": "^2.14.0",
|
"date-fns": "^2.14.0",
|
||||||
"fast-memoize": "^2.5.2",
|
"fast-memoize": "^2.5.2",
|
||||||
|
"promise-retry": "^2.0.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-app-rewired": "^2.1.6",
|
"react-app-rewired": "^2.1.6",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
|
@ -35,9 +35,10 @@ export interface ConfirmationsContainerParams {
|
|||||||
message: MessageObject
|
message: MessageObject
|
||||||
receipt: Maybe<TransactionReceipt>
|
receipt: Maybe<TransactionReceipt>
|
||||||
fromHome: boolean
|
fromHome: boolean
|
||||||
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConfirmationsContainer = ({ message, receipt, fromHome }: ConfirmationsContainerParams) => {
|
export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }: ConfirmationsContainerParams) => {
|
||||||
const {
|
const {
|
||||||
home: { name: homeName },
|
home: { name: homeName },
|
||||||
foreign: { name: foreignName }
|
foreign: { name: foreignName }
|
||||||
@ -45,7 +46,8 @@ export const ConfirmationsContainer = ({ message, receipt, fromHome }: Confirmat
|
|||||||
const { confirmations, status, executionData, signatureCollected } = useMessageConfirmations({
|
const { confirmations, status, executionData, signatureCollected } = useMessageConfirmations({
|
||||||
message,
|
message,
|
||||||
receipt,
|
receipt,
|
||||||
fromHome
|
fromHome,
|
||||||
|
timestamp
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -5,7 +5,7 @@ import { 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 { ExecutionData } from '../hooks/useMessageConfirmations'
|
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
||||||
import { GreyLabel, SuccessLabel } from './commons/Labels'
|
import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
|
||||||
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||||
|
|
||||||
const Thead = styled.thead`
|
const Thead = styled.thead`
|
||||||
@ -32,6 +32,8 @@ export const ExecutionConfirmation = ({ executionData, isHome }: ExecutionConfir
|
|||||||
switch (validatorStatus) {
|
switch (validatorStatus) {
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
||||||
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
||||||
|
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
|
||||||
|
return <RedLabel>{validatorStatus}</RedLabel>
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
|
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
|
||||||
return <GreyLabel>{validatorStatus}</GreyLabel>
|
return <GreyLabel>{validatorStatus}</GreyLabel>
|
||||||
default:
|
default:
|
||||||
|
@ -75,7 +75,7 @@ export const StatusContainer = () => {
|
|||||||
)}
|
)}
|
||||||
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}
|
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}
|
||||||
{displayConfirmations && (
|
{displayConfirmations && (
|
||||||
<ConfirmationsContainer message={messageToConfirm} receipt={receipt} fromHome={isHome} />
|
<ConfirmationsContainer message={messageToConfirm} receipt={receipt} fromHome={isHome} timestamp={timestamp} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ import { 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'
|
||||||
import { GreyLabel, SuccessLabel } from './commons/Labels'
|
import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
|
||||||
|
|
||||||
const Thead = styled.thead`
|
const Thead = styled.thead`
|
||||||
border-bottom: 2px solid #9e9e9e;
|
border-bottom: 2px solid #9e9e9e;
|
||||||
@ -30,6 +30,8 @@ export const ValidatorsConfirmations = ({ confirmations }: ValidatorsConfirmatio
|
|||||||
switch (validatorStatus) {
|
switch (validatorStatus) {
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
||||||
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
||||||
|
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
|
||||||
|
return <RedLabel>{validatorStatus}</RedLabel>
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
|
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED:
|
case VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED:
|
||||||
return <GreyLabel>{validatorStatus}</GreyLabel>
|
return <GreyLabel>{validatorStatus}</GreyLabel>
|
||||||
|
@ -13,3 +13,10 @@ export const GreyLabel = styled.label`
|
|||||||
padding: 0.4rem 0.7rem;
|
padding: 0.4rem 0.7rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const RedLabel = styled.label`
|
||||||
|
color: var(--failed-color);
|
||||||
|
background-color: var(--failed-bg-color);
|
||||||
|
padding: 0.4rem 0.7rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
`
|
||||||
|
@ -10,9 +10,21 @@ export const FOREIGN_NETWORK_NAME: string = process.env.REACT_APP_ALM_FOREIGN_NE
|
|||||||
export const HOME_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_HOME_EXPLORER_TX_TEMPLATE || ''
|
export const HOME_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_HOME_EXPLORER_TX_TEMPLATE || ''
|
||||||
export const FOREIGN_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_TX_TEMPLATE || ''
|
export const FOREIGN_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_TX_TEMPLATE || ''
|
||||||
|
|
||||||
|
export const HOME_EXPLORER_API: string = process.env.REACT_APP_ALM_HOME_EXPLORER_API || ''
|
||||||
|
export const FOREIGN_EXPLORER_API: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_API || ''
|
||||||
|
|
||||||
export const HOME_RPC_POLLING_INTERVAL: number = 5000
|
export const HOME_RPC_POLLING_INTERVAL: number = 5000
|
||||||
export const FOREIGN_RPC_POLLING_INTERVAL: number = 15000
|
export const FOREIGN_RPC_POLLING_INTERVAL: number = 15000
|
||||||
export const BLOCK_RANGE: number = 50
|
export const BLOCK_RANGE: number = 50
|
||||||
|
export const ONE_DAY_TIMESTAMP: number = 86400
|
||||||
|
export const THREE_DAYS_TIMESTAMP: number = 259200
|
||||||
|
|
||||||
|
export const EXECUTE_AFFIRMATION_HASH = 'e7a2c01f'
|
||||||
|
export const SUBMIT_SIGNATURE_HASH = '630cea8e'
|
||||||
|
export const EXECUTE_SIGNATURES_HASH = '3f7658fd'
|
||||||
|
|
||||||
|
export const CACHE_KEY_FAILED = 'failed-confirmation-validator-'
|
||||||
|
export const CACHE_KEY_EXECUTION_FAILED = 'failed-execution-validator-'
|
||||||
|
|
||||||
export const TRANSACTION_STATUS = {
|
export const TRANSACTION_STATUS = {
|
||||||
SUCCESS_MULTIPLE_MESSAGES: 'SUCCESS_MULTIPLE_MESSAGES',
|
SUCCESS_MULTIPLE_MESSAGES: 'SUCCESS_MULTIPLE_MESSAGES',
|
||||||
|
@ -17,11 +17,13 @@ import { getCollectedSignaturesEvent } from '../utils/getCollectedSignaturesEven
|
|||||||
import { checkWaitingBlocksForExecution } from '../utils/executionWaitingForBlocks'
|
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 { getValidatorFailedTransactionsForMessage, getExecutionFailedTransactionForMessage } from '../utils/explorer'
|
||||||
|
|
||||||
export interface useMessageConfirmationsParams {
|
export interface useMessageConfirmationsParams {
|
||||||
message: MessageObject
|
message: MessageObject
|
||||||
receipt: Maybe<TransactionReceipt>
|
receipt: Maybe<TransactionReceipt>
|
||||||
fromHome: boolean
|
fromHome: boolean
|
||||||
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfirmationParam {
|
export interface ConfirmationParam {
|
||||||
@ -37,7 +39,7 @@ export interface ExecutionData {
|
|||||||
executionResult: boolean
|
executionResult: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessageConfirmationsParams) => {
|
export const useMessageConfirmations = ({ message, receipt, fromHome, timestamp }: useMessageConfirmationsParams) => {
|
||||||
const { home, foreign } = useStateProvider()
|
const { home, foreign } = useStateProvider()
|
||||||
const [confirmations, setConfirmations] = useState<Array<ConfirmationParam>>([])
|
const [confirmations, setConfirmations] = useState<Array<ConfirmationParam>>([])
|
||||||
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
|
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||||
@ -54,6 +56,8 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
})
|
})
|
||||||
const [waitingBlocksForExecution, setWaitingBlocksForExecution] = useState(false)
|
const [waitingBlocksForExecution, setWaitingBlocksForExecution] = useState(false)
|
||||||
const [waitingBlocksForExecutionResolved, setWaitingBlocksForExecutionResolved] = useState(false)
|
const [waitingBlocksForExecutionResolved, setWaitingBlocksForExecutionResolved] = useState(false)
|
||||||
|
const [failedConfirmations, setFailedConfirmations] = useState(false)
|
||||||
|
const [failedExecution, setFailedExecution] = useState(false)
|
||||||
|
|
||||||
// Check if the validators are waiting for block confirmations to verify the message
|
// Check if the validators are waiting for block confirmations to verify the message
|
||||||
useEffect(
|
useEffect(
|
||||||
@ -182,7 +186,7 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
// 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) return
|
if (!waitingBlocksResolved || !timestamp) return
|
||||||
|
|
||||||
const subscriptions: Array<number> = []
|
const subscriptions: Array<number> = []
|
||||||
|
|
||||||
@ -204,7 +208,10 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
home.requiredSignatures,
|
home.requiredSignatures,
|
||||||
setSignatureCollected,
|
setSignatureCollected,
|
||||||
waitingBlocksResolved,
|
waitingBlocksResolved,
|
||||||
subscriptions
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
getValidatorFailedTransactionsForMessage,
|
||||||
|
setFailedConfirmations
|
||||||
)
|
)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -218,7 +225,8 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
home.validatorList,
|
home.validatorList,
|
||||||
home.bridgeContract,
|
home.bridgeContract,
|
||||||
home.requiredSignatures,
|
home.requiredSignatures,
|
||||||
waitingBlocksResolved
|
waitingBlocksResolved,
|
||||||
|
timestamp
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -248,9 +256,13 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
providedWeb3,
|
providedWeb3,
|
||||||
setExecutionData,
|
setExecutionData,
|
||||||
waitingBlocksResolved,
|
waitingBlocksResolved,
|
||||||
message.id,
|
message,
|
||||||
interval,
|
interval,
|
||||||
subscriptions
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getExecutionFailedTransactionForMessage,
|
||||||
|
setFailedExecution
|
||||||
)
|
)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -261,18 +273,20 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
fromHome,
|
fromHome,
|
||||||
foreign.bridgeContract,
|
foreign.bridgeContract,
|
||||||
home.bridgeContract,
|
home.bridgeContract,
|
||||||
message.id,
|
message,
|
||||||
foreign.web3,
|
foreign.web3,
|
||||||
home.web3,
|
home.web3,
|
||||||
waitingBlocksResolved,
|
waitingBlocksResolved,
|
||||||
waitingBlocksForExecutionResolved
|
waitingBlocksForExecutionResolved,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sets the message status based in the collected information
|
// Sets the message status based in the collected information
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
if (executionData.txHash) {
|
if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) {
|
||||||
const newStatus = executionData.executionResult
|
const newStatus = executionData.executionResult
|
||||||
? CONFIRMATIONS_STATUS.SUCCESS
|
? CONFIRMATIONS_STATUS.SUCCESS
|
||||||
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
|
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
|
||||||
@ -281,6 +295,8 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
if (fromHome) {
|
if (fromHome) {
|
||||||
if (waitingBlocksForExecution) {
|
if (waitingBlocksForExecution) {
|
||||||
setStatus(CONFIRMATIONS_STATUS.EXECUTION_WAITING)
|
setStatus(CONFIRMATIONS_STATUS.EXECUTION_WAITING)
|
||||||
|
} else if (failedExecution) {
|
||||||
|
setStatus(CONFIRMATIONS_STATUS.EXECUTION_FAILED)
|
||||||
} else {
|
} else {
|
||||||
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||||
}
|
}
|
||||||
@ -289,11 +305,21 @@ export const useMessageConfirmations = ({ message, receipt, fromHome }: useMessa
|
|||||||
}
|
}
|
||||||
} else if (waitingBlocks) {
|
} else if (waitingBlocks) {
|
||||||
setStatus(CONFIRMATIONS_STATUS.WAITING)
|
setStatus(CONFIRMATIONS_STATUS.WAITING)
|
||||||
|
} else if (failedConfirmations) {
|
||||||
|
setStatus(CONFIRMATIONS_STATUS.FAILED)
|
||||||
} else {
|
} else {
|
||||||
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[executionData, fromHome, signatureCollected, waitingBlocks, waitingBlocksForExecution]
|
[
|
||||||
|
executionData,
|
||||||
|
fromHome,
|
||||||
|
signatureCollected,
|
||||||
|
waitingBlocks,
|
||||||
|
waitingBlocksForExecution,
|
||||||
|
failedConfirmations,
|
||||||
|
failedExecution
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -3,7 +3,7 @@ import { TransactionReceipt } from 'web3-eth'
|
|||||||
import { HOME_RPC_POLLING_INTERVAL, TRANSACTION_STATUS } from '../config/constants'
|
import { HOME_RPC_POLLING_INTERVAL, TRANSACTION_STATUS } from '../config/constants'
|
||||||
import { getTransactionStatusDescription } from '../utils/networks'
|
import { getTransactionStatusDescription } from '../utils/networks'
|
||||||
import { useStateProvider } from '../state/StateProvider'
|
import { useStateProvider } from '../state/StateProvider'
|
||||||
import { getHomeMessagesFromReceipt, getForeignMessagesFromReceipt, MessageObject } from '../utils/web3'
|
import { getHomeMessagesFromReceipt, getForeignMessagesFromReceipt, MessageObject, getBlock } from '../utils/web3'
|
||||||
|
|
||||||
export const useTransactionStatus = ({ txHash, chainId }: { txHash: string; chainId: number }) => {
|
export const useTransactionStatus = ({ txHash, chainId }: { txHash: string; chainId: number }) => {
|
||||||
const { home, foreign } = useStateProvider()
|
const { home, foreign } = useStateProvider()
|
||||||
@ -41,7 +41,7 @@ export const useTransactionStatus = ({ txHash, chainId }: { txHash: string; chai
|
|||||||
subscriptions.push(timeoutId)
|
subscriptions.push(timeoutId)
|
||||||
} else {
|
} else {
|
||||||
const blockNumber = txReceipt.blockNumber
|
const blockNumber = txReceipt.blockNumber
|
||||||
const block = await web3.eth.getBlock(blockNumber)
|
const block = await getBlock(web3, blockNumber)
|
||||||
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
|
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
|
||||||
setTimestamp(blockTimestamp)
|
setTimestamp(blockTimestamp)
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@ const theme = {
|
|||||||
notRequired: {
|
notRequired: {
|
||||||
textColor: '#bdbdbd',
|
textColor: '#bdbdbd',
|
||||||
backgroundColor: '#424242'
|
backgroundColor: '#424242'
|
||||||
|
},
|
||||||
|
failed: {
|
||||||
|
textColor: '#EF5350',
|
||||||
|
backgroundColor: '#4E342E'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default theme
|
export default theme
|
||||||
|
@ -25,5 +25,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
|
|||||||
--success-bg-color: ${props => props.theme.success.backgroundColor};
|
--success-bg-color: ${props => props.theme.success.backgroundColor};
|
||||||
--not-required-color: ${props => props.theme.notRequired.textColor};
|
--not-required-color: ${props => props.theme.notRequired.textColor};
|
||||||
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
||||||
|
--failed-color: ${props => props.theme.failed.textColor};
|
||||||
|
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -15,6 +15,13 @@ export const checkWaitingBlocksForExecution = async (
|
|||||||
const currentBlock = blockProvider.get()
|
const currentBlock = blockProvider.get()
|
||||||
|
|
||||||
if (currentBlock && currentBlock >= targetBlock) {
|
if (currentBlock && currentBlock >= targetBlock) {
|
||||||
|
setExecutionData({
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||||
|
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
|
||||||
|
txHash: '',
|
||||||
|
timestamp: 0,
|
||||||
|
executionResult: false
|
||||||
|
})
|
||||||
setWaitingBlocksForExecution(false)
|
setWaitingBlocksForExecution(false)
|
||||||
setWaitingBlocksForExecutionResolved(true)
|
setWaitingBlocksForExecutionResolved(true)
|
||||||
blockProvider.stop()
|
blockProvider.stop()
|
||||||
|
164
alm/src/utils/explorer.ts
Normal file
164
alm/src/utils/explorer.ts
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import {
|
||||||
|
EXECUTE_AFFIRMATION_HASH,
|
||||||
|
EXECUTE_SIGNATURES_HASH,
|
||||||
|
FOREIGN_EXPLORER_API,
|
||||||
|
HOME_EXPLORER_API,
|
||||||
|
SUBMIT_SIGNATURE_HASH
|
||||||
|
} from '../config/constants'
|
||||||
|
|
||||||
|
export interface APITransaction {
|
||||||
|
timeStamp: string
|
||||||
|
isError: string
|
||||||
|
input: string
|
||||||
|
to: string
|
||||||
|
hash: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AccountTransactionsParams {
|
||||||
|
account: string
|
||||||
|
to: string
|
||||||
|
startTimestamp: number
|
||||||
|
endTimestamp: number
|
||||||
|
api: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetFailedTransactionParams {
|
||||||
|
account: string
|
||||||
|
to: string
|
||||||
|
messageData: string
|
||||||
|
startTimestamp: number
|
||||||
|
endTimestamp: number
|
||||||
|
}
|
||||||
|
|
||||||
|
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}`
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await fetch(url).then(res => res.json())
|
||||||
|
if (result.status === '0') {
|
||||||
|
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 getFailedTransactions = async (
|
||||||
|
account: string,
|
||||||
|
to: string,
|
||||||
|
startTimestamp: number,
|
||||||
|
endTimestamp: number,
|
||||||
|
api: string,
|
||||||
|
fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
|
||||||
|
): Promise<APITransaction[]> => {
|
||||||
|
const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
|
||||||
|
|
||||||
|
return transactions.filter(t => t.isError !== '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidatorFailedTransactionsForMessage = async ({
|
||||||
|
account,
|
||||||
|
to,
|
||||||
|
messageData,
|
||||||
|
startTimestamp,
|
||||||
|
endTimestamp
|
||||||
|
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||||
|
const failedTransactions = await getFailedTransactions(
|
||||||
|
account,
|
||||||
|
to,
|
||||||
|
startTimestamp,
|
||||||
|
endTimestamp,
|
||||||
|
HOME_EXPLORER_API,
|
||||||
|
fetchAccountTransactionsFromBlockscout
|
||||||
|
)
|
||||||
|
|
||||||
|
const messageDataValue = messageData.replace('0x', '')
|
||||||
|
return failedTransactions.filter(
|
||||||
|
t =>
|
||||||
|
(t.input.includes(SUBMIT_SIGNATURE_HASH) || t.input.includes(EXECUTE_AFFIRMATION_HASH)) &&
|
||||||
|
t.input.includes(messageDataValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExecutionFailedTransactionForMessage = async ({
|
||||||
|
account,
|
||||||
|
to,
|
||||||
|
messageData,
|
||||||
|
startTimestamp,
|
||||||
|
endTimestamp
|
||||||
|
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||||
|
const failedTransactions = await getFailedTransactions(
|
||||||
|
account,
|
||||||
|
to,
|
||||||
|
startTimestamp,
|
||||||
|
endTimestamp,
|
||||||
|
FOREIGN_EXPLORER_API,
|
||||||
|
fetchAccountTransactions(FOREIGN_EXPLORER_API)
|
||||||
|
)
|
||||||
|
|
||||||
|
const messageDataValue = messageData.replace('0x', '')
|
||||||
|
return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue))
|
||||||
|
}
|
@ -1,7 +1,81 @@
|
|||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { Contract } from 'web3-eth-contract'
|
import { Contract } from 'web3-eth-contract'
|
||||||
import validatorsCache from '../services/ValidatorsCache'
|
import validatorsCache from '../services/ValidatorsCache'
|
||||||
import { HOME_RPC_POLLING_INTERVAL, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
import {
|
||||||
|
CACHE_KEY_FAILED,
|
||||||
|
HOME_RPC_POLLING_INTERVAL,
|
||||||
|
ONE_DAY_TIMESTAMP,
|
||||||
|
VALIDATOR_CONFIRMATION_STATUS
|
||||||
|
} from '../config/constants'
|
||||||
|
import { GetFailedTransactionParams, APITransaction } from './explorer'
|
||||||
|
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
|
||||||
|
|
||||||
|
export const getValidatorConfirmation = (
|
||||||
|
web3: Web3,
|
||||||
|
hashMsg: string,
|
||||||
|
bridgeContract: Contract,
|
||||||
|
confirmationContractMethod: Function
|
||||||
|
) => async (validator: string): Promise<ConfirmationParam> => {
|
||||||
|
const hashSenderMsg = web3.utils.soliditySha3Raw(validator, hashMsg)
|
||||||
|
|
||||||
|
const signatureFromCache = validatorsCache.get(hashSenderMsg)
|
||||||
|
if (signatureFromCache) {
|
||||||
|
return {
|
||||||
|
validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (confirmed) {
|
||||||
|
validatorsCache.set(hashSenderMsg, confirmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
validator,
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidatorFailedTransaction = (
|
||||||
|
bridgeContract: Contract,
|
||||||
|
messageData: string,
|
||||||
|
timestamp: number,
|
||||||
|
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||||
|
) => async (validatorData: ConfirmationParam): Promise<ConfirmationParam> => {
|
||||||
|
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}`
|
||||||
|
const failedFromCache = validatorsCache.get(validatorCacheKey)
|
||||||
|
|
||||||
|
if (failedFromCache) {
|
||||||
|
return {
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const failedTransactions = await getFailedTransactions({
|
||||||
|
account: validatorData.validator,
|
||||||
|
to: bridgeContract.options.address,
|
||||||
|
messageData,
|
||||||
|
startTimestamp: timestamp,
|
||||||
|
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||||
|
})
|
||||||
|
const newStatus =
|
||||||
|
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
|
||||||
|
// If validator signature failed, we cache the result to avoid doing future requests for a result that won't change
|
||||||
|
if (failedTransactions.length > 0) {
|
||||||
|
validatorsCache.set(validatorCacheKey, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
validator: validatorData.validator,
|
||||||
|
status: newStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getConfirmationsForTx = async (
|
export const getConfirmationsForTx = async (
|
||||||
messageData: string,
|
messageData: string,
|
||||||
@ -13,63 +87,69 @@ export const getConfirmationsForTx = async (
|
|||||||
requiredSignatures: number,
|
requiredSignatures: number,
|
||||||
setSignatureCollected: Function,
|
setSignatureCollected: Function,
|
||||||
waitingBlocksResolved: boolean,
|
waitingBlocksResolved: boolean,
|
||||||
subscriptions: number[]
|
subscriptions: number[],
|
||||||
|
timestamp: number,
|
||||||
|
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
|
||||||
|
setFailedConfirmations: Function
|
||||||
) => {
|
) => {
|
||||||
if (!web3 || !validatorList || !bridgeContract || !waitingBlocksResolved) return
|
if (!web3 || !validatorList || !bridgeContract || !waitingBlocksResolved) return
|
||||||
const hashMsg = web3.utils.soliditySha3Raw(messageData)
|
const hashMsg = web3.utils.soliditySha3Raw(messageData)
|
||||||
let validatorConfirmations = await Promise.all(
|
let validatorConfirmations = await Promise.all(
|
||||||
validatorList.map(async validator => {
|
validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, confirmationContractMethod))
|
||||||
const hashSenderMsg = web3.utils.soliditySha3Raw(validator, hashMsg)
|
|
||||||
|
|
||||||
const signatureFromCache = validatorsCache.get(hashSenderMsg)
|
|
||||||
if (signatureFromCache) {
|
|
||||||
return {
|
|
||||||
validator,
|
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (confirmed) {
|
|
||||||
validatorsCache.set(hashSenderMsg, confirmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
validator,
|
|
||||||
status
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
// If signatures not collected, it needs to retry in the next blocks
|
// If signatures not collected, it needs to retry in the next blocks
|
||||||
if (successConfirmations.length !== requiredSignatures) {
|
if (successConfirmations.length !== requiredSignatures) {
|
||||||
const timeoutId = setTimeout(
|
// Check if confirmation failed
|
||||||
() =>
|
const validatorFailedConfirmationsChecks = await Promise.all(
|
||||||
getConfirmationsForTx(
|
notSuccessConfirmations.map(
|
||||||
messageData,
|
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
|
||||||
web3,
|
)
|
||||||
validatorList,
|
|
||||||
bridgeContract,
|
|
||||||
confirmationContractMethod,
|
|
||||||
setResult,
|
|
||||||
requiredSignatures,
|
|
||||||
setSignatureCollected,
|
|
||||||
waitingBlocksResolved,
|
|
||||||
subscriptions
|
|
||||||
),
|
|
||||||
HOME_RPC_POLLING_INTERVAL
|
|
||||||
)
|
)
|
||||||
subscriptions.push(timeoutId)
|
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
|
||||||
|
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||||
|
)
|
||||||
|
validatorFailedConfirmations.forEach(validatorData => {
|
||||||
|
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
|
||||||
|
validatorConfirmations[index] = validatorData
|
||||||
|
})
|
||||||
|
const messageConfirmationsFailed = validatorFailedConfirmations.length > validatorList.length - requiredSignatures
|
||||||
|
if (messageConfirmationsFailed) {
|
||||||
|
setFailedConfirmations(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingConfirmations = validatorConfirmations.filter(
|
||||||
|
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||||
|
)
|
||||||
|
|
||||||
|
if (missingConfirmations.length > 0) {
|
||||||
|
const timeoutId = setTimeout(
|
||||||
|
() =>
|
||||||
|
getConfirmationsForTx(
|
||||||
|
messageData,
|
||||||
|
web3,
|
||||||
|
validatorList,
|
||||||
|
bridgeContract,
|
||||||
|
confirmationContractMethod,
|
||||||
|
setResult,
|
||||||
|
requiredSignatures,
|
||||||
|
setSignatureCollected,
|
||||||
|
waitingBlocksResolved,
|
||||||
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
getFailedTransactions,
|
||||||
|
setFailedConfirmations
|
||||||
|
),
|
||||||
|
HOME_RPC_POLLING_INTERVAL
|
||||||
|
)
|
||||||
|
subscriptions.push(timeoutId)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If signatures collected, it should set other signatures as not required
|
// If signatures collected, it should set other signatures as not required
|
||||||
const notSuccessConfirmations = validatorConfirmations.filter(
|
|
||||||
c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
|
||||||
)
|
|
||||||
const notRequiredConfirmations = notSuccessConfirmations.map(c => ({
|
const notRequiredConfirmations = notSuccessConfirmations.map(c => ({
|
||||||
validator: c.validator,
|
validator: c.validator,
|
||||||
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
|
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { Contract, EventData } from 'web3-eth-contract'
|
import { Contract, EventData } from 'web3-eth-contract'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
import { CACHE_KEY_EXECUTION_FAILED, THREE_DAYS_TIMESTAMP, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||||
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
||||||
|
import { APITransaction, GetFailedTransactionParams } from './explorer'
|
||||||
|
import { getBlock, MessageObject } from './web3'
|
||||||
|
import validatorsCache from '../services/ValidatorsCache'
|
||||||
|
|
||||||
export const getFinalizationEvent = async (
|
export const getFinalizationEvent = async (
|
||||||
contract: Maybe<Contract>,
|
contract: Maybe<Contract>,
|
||||||
@ -9,9 +12,13 @@ export const getFinalizationEvent = async (
|
|||||||
web3: Maybe<Web3>,
|
web3: Maybe<Web3>,
|
||||||
setResult: React.Dispatch<React.SetStateAction<ExecutionData>>,
|
setResult: React.Dispatch<React.SetStateAction<ExecutionData>>,
|
||||||
waitingBlocksResolved: boolean,
|
waitingBlocksResolved: boolean,
|
||||||
messageId: string,
|
message: MessageObject,
|
||||||
interval: number,
|
interval: number,
|
||||||
subscriptions: number[]
|
subscriptions: number[],
|
||||||
|
timestamp: number,
|
||||||
|
collectedSignaturesEvent: Maybe<EventData>,
|
||||||
|
getFailedExecution: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
|
||||||
|
setFailedExecution: Function
|
||||||
) => {
|
) => {
|
||||||
if (!contract || !web3 || !waitingBlocksResolved) return
|
if (!contract || !web3 || !waitingBlocksResolved) return
|
||||||
// Since it filters by the message id, only one event will be fetched
|
// Since it filters by the message id, only one event will be fetched
|
||||||
@ -20,14 +27,14 @@ export const getFinalizationEvent = async (
|
|||||||
fromBlock: 0,
|
fromBlock: 0,
|
||||||
toBlock: 'latest',
|
toBlock: 'latest',
|
||||||
filter: {
|
filter: {
|
||||||
messageId
|
messageId: message.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (events.length > 0) {
|
if (events.length > 0) {
|
||||||
const event = events[0]
|
const event = events[0]
|
||||||
const [txReceipt, block] = await Promise.all([
|
const [txReceipt, block] = await Promise.all([
|
||||||
web3.eth.getTransactionReceipt(event.transactionHash),
|
web3.eth.getTransactionReceipt(event.transactionHash),
|
||||||
web3.eth.getBlock(event.blockNumber)
|
getBlock(web3, event.blockNumber)
|
||||||
])
|
])
|
||||||
|
|
||||||
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
|
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
|
||||||
@ -41,6 +48,41 @@ export const getFinalizationEvent = async (
|
|||||||
executionResult: event.returnValues.status
|
executionResult: event.returnValues.status
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
// If event is defined, it means it is a message from Home to Foreign
|
||||||
|
if (collectedSignaturesEvent) {
|
||||||
|
const validator = collectedSignaturesEvent.returnValues.authorityResponsibleForRelay
|
||||||
|
|
||||||
|
const validatorExecutionCacheKey = `${CACHE_KEY_EXECUTION_FAILED}${validator}`
|
||||||
|
const failedFromCache = validatorsCache.get(validatorExecutionCacheKey)
|
||||||
|
|
||||||
|
if (!failedFromCache) {
|
||||||
|
const failedTransactions = await getFailedExecution({
|
||||||
|
account: validator,
|
||||||
|
to: contract.options.address,
|
||||||
|
messageData: message.data,
|
||||||
|
startTimestamp: timestamp,
|
||||||
|
endTimestamp: timestamp + THREE_DAYS_TIMESTAMP
|
||||||
|
})
|
||||||
|
|
||||||
|
if (failedTransactions.length > 0) {
|
||||||
|
const failedTx = failedTransactions[0]
|
||||||
|
|
||||||
|
// If validator execution failed, we cache the result to avoid doing future requests for a result that won't change
|
||||||
|
validatorsCache.set(validatorExecutionCacheKey, true)
|
||||||
|
|
||||||
|
const timestamp = parseInt(failedTx.timeStamp)
|
||||||
|
setResult({
|
||||||
|
status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
|
||||||
|
validator: validator,
|
||||||
|
txHash: failedTx.hash,
|
||||||
|
timestamp,
|
||||||
|
executionResult: false
|
||||||
|
})
|
||||||
|
setFailedExecution(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const timeoutId = setTimeout(
|
const timeoutId = setTimeout(
|
||||||
() =>
|
() =>
|
||||||
getFinalizationEvent(
|
getFinalizationEvent(
|
||||||
@ -49,9 +91,13 @@ export const getFinalizationEvent = async (
|
|||||||
web3,
|
web3,
|
||||||
setResult,
|
setResult,
|
||||||
waitingBlocksResolved,
|
waitingBlocksResolved,
|
||||||
messageId,
|
message,
|
||||||
interval,
|
interval,
|
||||||
subscriptions
|
subscriptions,
|
||||||
|
timestamp,
|
||||||
|
collectedSignaturesEvent,
|
||||||
|
getFailedExecution,
|
||||||
|
setFailedExecution
|
||||||
),
|
),
|
||||||
interval
|
interval
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
|
import { BlockTransactionString } from 'web3-eth'
|
||||||
import { TransactionReceipt } from 'web3-eth'
|
import { TransactionReceipt } from 'web3-eth'
|
||||||
import { AbiItem } from 'web3-utils'
|
import { AbiItem } from 'web3-utils'
|
||||||
import memoize from 'fast-memoize'
|
import memoize from 'fast-memoize'
|
||||||
|
import promiseRetry from 'promise-retry'
|
||||||
import { HOME_AMB_ABI, FOREIGN_AMB_ABI } from '../../../commons'
|
import { HOME_AMB_ABI, FOREIGN_AMB_ABI } from '../../../commons'
|
||||||
|
|
||||||
export interface MessageObject {
|
export interface MessageObject {
|
||||||
@ -48,3 +50,14 @@ export const getForeignMessagesFromReceipt = (txReceipt: TransactionReceipt, web
|
|||||||
)[0]
|
)[0]
|
||||||
return filterEventsByAbi(txReceipt, web3, bridgeAddress, userRequestForAffirmationAbi)
|
return filterEventsByAbi(txReceipt, web3, bridgeAddress, userRequestForAffirmationAbi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In some rare cases the block data is not available yet for the block of a new event detected
|
||||||
|
// so this logic retry to get the block in case it fails
|
||||||
|
export const getBlock = async (web3: Web3, blockNumber: number): Promise<BlockTransactionString> =>
|
||||||
|
promiseRetry(async retry => {
|
||||||
|
const result = await web3.eth.getBlock(blockNumber)
|
||||||
|
if (!result) {
|
||||||
|
return retry('Error getting block data')
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
30
yarn.lock
30
yarn.lock
@ -2980,6 +2980,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.59.tgz#9e34261f30183f9777017a13d185dfac6b899e04"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.59.tgz#9e34261f30183f9777017a13d185dfac6b899e04"
|
||||||
integrity sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==
|
integrity sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==
|
||||||
|
|
||||||
|
"@types/promise-retry@^1.1.3":
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/promise-retry/-/promise-retry-1.1.3.tgz#baab427419da9088a1d2f21bf56249c21b3dd43c"
|
||||||
|
integrity sha512-LxIlEpEX6frE3co3vCO2EUJfHIta1IOmhDlcAsR4GMMv9hev1iTI9VwberVGkePJAuLZs5rMucrV8CziCfuJMw==
|
||||||
|
dependencies:
|
||||||
|
"@types/retry" "*"
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*":
|
||||||
version "15.7.3"
|
version "15.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||||
@ -3042,6 +3049,11 @@
|
|||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
csstype "^2.2.0"
|
csstype "^2.2.0"
|
||||||
|
|
||||||
|
"@types/retry@*":
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||||
|
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||||
|
|
||||||
"@types/solidity-parser-antlr@^0.2.3":
|
"@types/solidity-parser-antlr@^0.2.3":
|
||||||
version "0.2.3"
|
version "0.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/solidity-parser-antlr/-/solidity-parser-antlr-0.2.3.tgz#bb2d9c6511bf483afe4fc3e2714da8a924e59e3f"
|
resolved "https://registry.yarnpkg.com/@types/solidity-parser-antlr/-/solidity-parser-antlr-0.2.3.tgz#bb2d9c6511bf483afe4fc3e2714da8a924e59e3f"
|
||||||
@ -7712,6 +7724,11 @@ err-code@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
|
resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
|
||||||
integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
|
integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
|
||||||
|
|
||||||
|
err-code@^2.0.2:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
|
||||||
|
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
|
||||||
|
|
||||||
errno@^0.1.3, errno@~0.1.1, errno@~0.1.7:
|
errno@^0.1.3, errno@~0.1.1, errno@~0.1.7:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||||
@ -16100,6 +16117,14 @@ promise-retry@^1.1.1:
|
|||||||
err-code "^1.0.0"
|
err-code "^1.0.0"
|
||||||
retry "^0.10.0"
|
retry "^0.10.0"
|
||||||
|
|
||||||
|
promise-retry@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
|
||||||
|
integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
|
||||||
|
dependencies:
|
||||||
|
err-code "^2.0.2"
|
||||||
|
retry "^0.12.0"
|
||||||
|
|
||||||
promise-to-callback@^1.0.0:
|
promise-to-callback@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7"
|
resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7"
|
||||||
@ -17410,6 +17435,11 @@ retry@^0.10.0:
|
|||||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
|
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
|
||||||
integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
|
integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
|
||||||
|
|
||||||
|
retry@^0.12.0:
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||||
|
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||||
|
|
||||||
rgb-regex@^1.0.1:
|
rgb-regex@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
|
resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
|
||||||
|
Loading…
Reference in New Issue
Block a user