From 981231fb4703fd9174058085afd7735bd9c95978 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Fri, 18 Feb 2022 17:35:12 +0300 Subject: [PATCH 1/5] Helpers for overriding AMB signatures (#640) --- alm/src/components/ConfirmationsContainer.tsx | 2 +- alm/src/components/ManualExecutionButton.tsx | 84 +++++++++++++++++-- alm/src/hooks/useValidatorContract.ts | 34 ++++---- alm/src/utils/contract.ts | 12 ++- oracle/package.json | 1 + oracle/scripts/signPendingMessages.js | 76 +++++++++++++++++ 6 files changed, 179 insertions(+), 30 deletions(-) create mode 100644 oracle/scripts/signPendingMessages.js diff --git a/alm/src/components/ConfirmationsContainer.tsx b/alm/src/components/ConfirmationsContainer.tsx index c9a37ab9a..a307fec97 100644 --- a/alm/src/components/ConfirmationsContainer.tsx +++ b/alm/src/components/ConfirmationsContainer.tsx @@ -54,7 +54,7 @@ export const ConfirmationsContainer = ({ home: { name: homeName }, foreign: { name: foreignName } } = useStateProvider() - const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt }) + const { requiredSignatures, validatorList } = useValidatorContract(fromHome, receipt ? receipt.blockNumber : 0) const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt }) const { confirmations, diff --git a/alm/src/components/ManualExecutionButton.tsx b/alm/src/components/ManualExecutionButton.tsx index 32492d2f8..a7ee8d1d2 100644 --- a/alm/src/components/ManualExecutionButton.tsx +++ b/alm/src/components/ManualExecutionButton.tsx @@ -14,6 +14,7 @@ import { useStateProvider } from '../state/StateProvider' import { signatureToVRS, packSignatures } from '../utils/signatures' import { getSuccessExecutionData } from '../utils/getFinalizationEvent' import { TransactionReceipt } from 'web3-eth' +import { useValidatorContract } from '../hooks/useValidatorContract' const ActionButton = styled.button` color: var(--button-color); @@ -45,7 +46,75 @@ export const ManualExecutionButton = ({ const { library, activate, account, active } = useWeb3React() const [manualExecution, setManualExecution] = useState(false) const [allowFailures, setAllowFailures] = useState(false) - const notReady = !foreign.bridgeContract || !signatureCollected || !signatureCollected.length + const [ready, setReady] = useState(false) + const [title, setTitle] = useState('Loading') + const [validSignatures, setValidSignatures] = useState([]) + + const { requiredSignatures, validatorList } = useValidatorContract(false, 'latest') + + useEffect( + () => { + if ( + !foreign.bridgeContract || + !foreign.web3 || + !signatureCollected || + !signatureCollected.length || + !requiredSignatures || + !validatorList || + !validatorList.length + ) + return + + const signatures = [] + const remainingValidators = Object.fromEntries(validatorList.map(validator => [validator, true])) + for (let i = 0; i < signatureCollected.length && signatures.length < requiredSignatures; i++) { + const { v, r, s } = signatureToVRS(signatureCollected[i]) + const signer = foreign.web3.eth.accounts.recover(messageData, `0x${v}`, `0x${r}`, `0x${s}`) + if (validatorList.includes(signer)) { + delete remainingValidators[signer] + signatures.push(signatureCollected[i]) + } + } + + if (signatures.length < requiredSignatures) { + console.log('On-chain collected signatures are not enough for message execution') + const manualValidators = Object.keys(remainingValidators) + const msgHash = foreign.web3.utils.sha3(messageData)! + for (let i = 0; i < manualValidators.length && signatures.length < requiredSignatures; i++) { + try { + const overrideSignatures: { + [key: string]: string + } = require(`../snapshots/signatures_${manualValidators[i]}.json`) + if (overrideSignatures[msgHash]) { + console.log(`Adding manual signature from ${manualValidators[i]}`) + signatures.push(overrideSignatures[msgHash]) + } else { + console.log(`No manual signature from ${manualValidators[i]} was found`) + } + } catch (e) { + console.log(`Signatures overrides are not present for ${manualValidators[i]}`) + } + } + } + + if (signatures.length >= requiredSignatures) { + setValidSignatures(signatures) + setTitle('Execute') + setReady(true) + } else { + setTitle('Unavailable') + } + }, + [ + foreign.bridgeContract, + foreign.web3, + signatureCollected, + validatorList, + requiredSignatures, + messageData, + setValidSignatures + ] + ) useEffect( () => { @@ -73,9 +142,9 @@ export const ManualExecutionButton = ({ return } - if (!library || !foreign.bridgeContract || !signatureCollected || !signatureCollected.length) return + if (!library || !foreign.bridgeContract || !foreign.web3 || !validSignatures || !validSignatures.length) return - const signatures = packSignatures(signatureCollected.map(signatureToVRS)) + const signatures = packSignatures(validSignatures.map(signatureToVRS)) const messageId = messageData.slice(0, 66) const bridge = foreign.bridgeContract const executeMethod = @@ -140,19 +209,20 @@ export const ManualExecutionButton = ({ foreign.bridgeContract, setError, messageData, - signatureCollected, setExecutionData, setPendingExecution, safeExecutionAvailable, - allowFailures + allowFailures, + foreign.web3, + validSignatures ] ) return (
- setManualExecution(true)}> - Execute + setManualExecution(true)}> + {title}
{safeExecutionAvailable && ( diff --git a/alm/src/hooks/useValidatorContract.ts b/alm/src/hooks/useValidatorContract.ts index cc232647c..7806b94c5 100644 --- a/alm/src/hooks/useValidatorContract.ts +++ b/alm/src/hooks/useValidatorContract.ts @@ -4,19 +4,13 @@ import Web3 from 'web3' import { getRequiredSignatures, getValidatorAddress, getValidatorList } from '../utils/contract' import { BRIDGE_VALIDATORS_ABI } from '../abis' import { useStateProvider } from '../state/StateProvider' -import { TransactionReceipt } from 'web3-eth' import { foreignSnapshotProvider, homeSnapshotProvider, SnapshotProvider } from '../services/SnapshotProvider' import { FOREIGN_EXPLORER_API, HOME_EXPLORER_API } from '../config/constants' -export interface useValidatorContractParams { - fromHome: boolean - receipt: Maybe -} - -export const useValidatorContract = ({ receipt, fromHome }: useValidatorContractParams) => { +export const useValidatorContract = (isHome: boolean, blockNumber: number | 'latest') => { const [validatorContract, setValidatorContract] = useState>(null) const [requiredSignatures, setRequiredSignatures] = useState(0) - const [validatorList, setValidatorList] = useState([]) + const [validatorList, setValidatorList] = useState([]) const { home, foreign } = useStateProvider() @@ -29,34 +23,34 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract const callRequiredSignatures = async ( contract: Maybe, - receipt: TransactionReceipt, + blockNumber: number | 'latest', setResult: Function, snapshotProvider: SnapshotProvider, web3: Web3, api: string ) => { if (!contract) return - const result = await getRequiredSignatures(contract, receipt.blockNumber, snapshotProvider, web3, api) + const result = await getRequiredSignatures(contract, blockNumber, snapshotProvider, web3, api) setResult(result) } const callValidatorList = async ( contract: Maybe, - receipt: TransactionReceipt, + blockNumber: number | 'latest', setResult: Function, snapshotProvider: SnapshotProvider, web3: Web3, api: string ) => { if (!contract) return - const result = await getValidatorList(contract, receipt.blockNumber, snapshotProvider, web3, api) + const result = await getValidatorList(contract, blockNumber, snapshotProvider, web3, api) setResult(result) } - const web3 = fromHome ? home.web3 : foreign.web3 - const api = fromHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API - const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract - const snapshotProvider = fromHome ? homeSnapshotProvider : foreignSnapshotProvider + const web3 = isHome ? home.web3 : foreign.web3 + const api = isHome ? HOME_EXPLORER_API : FOREIGN_EXPLORER_API + const bridgeContract = isHome ? home.bridgeContract : foreign.bridgeContract + const snapshotProvider = isHome ? homeSnapshotProvider : foreignSnapshotProvider useEffect( () => { @@ -68,11 +62,11 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract useEffect( () => { - if (!web3 || !receipt) return - callRequiredSignatures(validatorContract, receipt, setRequiredSignatures, snapshotProvider, web3, api) - callValidatorList(validatorContract, receipt, setValidatorList, snapshotProvider, web3, api) + if (!web3 || !blockNumber) return + callRequiredSignatures(validatorContract, blockNumber, setRequiredSignatures, snapshotProvider, web3, api) + callValidatorList(validatorContract, blockNumber, setValidatorList, snapshotProvider, web3, api) }, - [validatorContract, receipt, web3, snapshotProvider, api] + [validatorContract, blockNumber, web3, snapshotProvider, api] ) return { diff --git a/alm/src/utils/contract.ts b/alm/src/utils/contract.ts index 7e9824e59..895815d3c 100644 --- a/alm/src/utils/contract.ts +++ b/alm/src/utils/contract.ts @@ -52,11 +52,15 @@ export const getValidatorAddress = (contract: Contract) => contract.methods.vali export const getRequiredSignatures = async ( contract: Contract, - blockNumber: number, + blockNumber: number | 'latest', snapshotProvider: SnapshotProvider, web3: Web3 | null = null, api: string = '' ) => { + if (blockNumber === 'latest') { + return contract.methods.requiredSignatures().call() + } + const eventsFromSnapshot = snapshotProvider.requiredSignaturesEvents(blockNumber) const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber() @@ -78,11 +82,15 @@ export const getRequiredSignatures = async ( export const getValidatorList = async ( contract: Contract, - blockNumber: number, + blockNumber: number | 'latest', snapshotProvider: SnapshotProvider, web3: Web3 | null = null, api: string = '' ) => { + if (blockNumber === 'latest') { + return contract.methods.validatorList().call() + } + const addedEventsFromSnapshot = snapshotProvider.validatorAddedEvents(blockNumber) const removedEventsFromSnapshot = snapshotProvider.validatorRemovedEvents(blockNumber) const snapshotBlockNumber = snapshotProvider.snapshotBlockNumber() diff --git a/oracle/package.json b/oracle/package.json index 4bc3cd7b1..5d05623ef 100644 --- a/oracle/package.json +++ b/oracle/package.json @@ -19,6 +19,7 @@ "confirm:information-request": "./scripts/start-worker.sh confirmRelay information-request-watcher", "manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager", "helper:interestFether": "node ./scripts/interestFetcher.js", + "helper:signPendingMessages": "node ./scripts/signPendingMessages.js", "mev:watcher:collected-signatures": "./scripts/start-worker.sh mevWatcher mev-collected-signatures-watcher", "mev:sender:foreign": "./scripts/start-worker.sh mevSender foreign-mev-sender", "dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'", diff --git a/oracle/scripts/signPendingMessages.js b/oracle/scripts/signPendingMessages.js new file mode 100644 index 000000000..75c739ce7 --- /dev/null +++ b/oracle/scripts/signPendingMessages.js @@ -0,0 +1,76 @@ +require('dotenv').config() + +const { + COMMON_HOME_BRIDGE_ADDRESS, + COMMON_FOREIGN_BRIDGE_ADDRESS, + ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, + ORACLE_HOME_START_BLOCK, + ORACLE_HOME_END_BLOCK +} = process.env + +const fs = require('fs') +const promiseLimit = require('promise-limit') + +const { web3Home, web3Foreign } = require('../src/services/web3') +const { HOME_AMB_ABI, FOREIGN_AMB_ABI, getPastEvents, parseAMBMessage } = require('../../commons') +const { setLogger } = require('../src/services/injectedLogger') + +const mockLogger = { debug: () => {}, info: () => {}, error: () => {}, child: () => mockLogger } +setLogger(mockLogger) + +const limit = promiseLimit(50) + +const output = process.argv[2] + +async function main() { + const wallet = web3Home.eth.accounts.wallet.add(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY) + const homeBridge = new web3Home.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS) + const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) + const fromBlock = parseInt(ORACLE_HOME_START_BLOCK, 10) || 0 + let toBlock = parseInt(ORACLE_HOME_END_BLOCK, 10) + if (!toBlock) { + toBlock = await web3Home.eth.getBlockNumber() + } + console.log(`Getting CollectedSignatures events from block ${fromBlock} to block ${toBlock}`) + const events = await getPastEvents(homeBridge, { event: 'CollectedSignatures', fromBlock, toBlock }) + console.log(`Found ${events.length} CollectedSignatures events`) + console.log('Getting messages') + let messages = await Promise.all( + events.map((event, i) => () => getMessage(homeBridge, foreignBridge, event, i)).map(limit) + ) + messages = messages.filter(x => x) + console.log(`Filtered ${messages.length} pending messages`) + const result = {} + messages.forEach(msg => { + result[msg.msgHash] = wallet.sign(msg.message).signature + }) + + console.log('Writing results') + if (output === '-') { + console.log(JSON.stringify(result)) + } else { + fs.writeFileSync(output, JSON.stringify(result)) + } +} + +async function getMessage(homeBridge, foreignBridge, event, i) { + if (i % 50 === 0) { + console.log(`Processing event #${i}`) + } + const msgHash = event.returnValues.messageHash + const message = await homeBridge.methods.message(msgHash).call() + + const { messageId } = parseAMBMessage(message) + const alreadyProcessed = await foreignBridge.methods.relayedMessages(messageId).call() + + if (alreadyProcessed) { + return null + } + + return { + msgHash, + message + } +} + +main() From 2b51d4c209f4e210c0f76ddb24afc10420f561fb Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Thu, 10 Mar 2022 15:18:19 +0400 Subject: [PATCH 2/5] Support manual signatures in erc to native mode (#646) --- oracle/scripts/signPendingMessages.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/oracle/scripts/signPendingMessages.js b/oracle/scripts/signPendingMessages.js index 75c739ce7..ef8e6080d 100644 --- a/oracle/scripts/signPendingMessages.js +++ b/oracle/scripts/signPendingMessages.js @@ -5,14 +5,15 @@ const { COMMON_FOREIGN_BRIDGE_ADDRESS, ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY, ORACLE_HOME_START_BLOCK, - ORACLE_HOME_END_BLOCK + ORACLE_HOME_END_BLOCK, + ORACLE_BRIDGE_MODE } = process.env const fs = require('fs') const promiseLimit = require('promise-limit') const { web3Home, web3Foreign } = require('../src/services/web3') -const { HOME_AMB_ABI, FOREIGN_AMB_ABI, getPastEvents, parseAMBMessage } = require('../../commons') +const { getBridgeABIs, getPastEvents, parseAMBMessage, BRIDGE_MODES } = require('../../commons') const { setLogger } = require('../src/services/injectedLogger') const mockLogger = { debug: () => {}, info: () => {}, error: () => {}, child: () => mockLogger } @@ -23,9 +24,10 @@ const limit = promiseLimit(50) const output = process.argv[2] async function main() { + const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(ORACLE_BRIDGE_MODE) const wallet = web3Home.eth.accounts.wallet.add(ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY) - const homeBridge = new web3Home.eth.Contract(HOME_AMB_ABI, COMMON_HOME_BRIDGE_ADDRESS) - const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_AMB_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) + const homeBridge = new web3Home.eth.Contract(HOME_ABI, COMMON_HOME_BRIDGE_ADDRESS) + const foreignBridge = new web3Foreign.eth.Contract(FOREIGN_ABI, COMMON_FOREIGN_BRIDGE_ADDRESS) const fromBlock = parseInt(ORACLE_HOME_START_BLOCK, 10) || 0 let toBlock = parseInt(ORACLE_HOME_END_BLOCK, 10) if (!toBlock) { @@ -36,7 +38,7 @@ async function main() { console.log(`Found ${events.length} CollectedSignatures events`) console.log('Getting messages') let messages = await Promise.all( - events.map((event, i) => () => getMessage(homeBridge, foreignBridge, event, i)).map(limit) + events.map((event, i) => () => getMessage(ORACLE_BRIDGE_MODE, homeBridge, foreignBridge, event, i)).map(limit) ) messages = messages.filter(x => x) console.log(`Filtered ${messages.length} pending messages`) @@ -53,15 +55,20 @@ async function main() { } } -async function getMessage(homeBridge, foreignBridge, event, i) { +async function getMessage(bridgeMode, homeBridge, foreignBridge, event, i) { if (i % 50 === 0) { console.log(`Processing event #${i}`) } const msgHash = event.returnValues.messageHash const message = await homeBridge.methods.message(msgHash).call() - const { messageId } = parseAMBMessage(message) - const alreadyProcessed = await foreignBridge.methods.relayedMessages(messageId).call() + let msgId + if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) { + msgId = parseAMBMessage(message).messageId + } else { + msgId = `0x${message.slice(106, 170)}` + } + const alreadyProcessed = await foreignBridge.methods.relayedMessages(msgId).call() if (alreadyProcessed) { return null From 9c2d2f404ce626077f835849e9e5776974583b80 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Sun, 13 Mar 2022 14:54:33 +0400 Subject: [PATCH 3/5] ALM: Do not show unneeded failed confirmations (#641) --- alm/src/components/ExecutionConfirmation.tsx | 2 +- .../components/ValidatorsConfirmations.tsx | 2 +- alm/src/config/constants.ts | 3 +- alm/src/hooks/useMessageConfirmations.ts | 5 +- .../__tests__/getConfirmationsForTx.test.ts | 135 +++++++++++------- .../__tests__/getFinalizationEvent.test.ts | 2 +- alm/src/utils/getConfirmationsForTx.ts | 56 +++++--- alm/src/utils/getFinalizationEvent.ts | 2 +- 8 files changed, 125 insertions(+), 82 deletions(-) diff --git a/alm/src/components/ExecutionConfirmation.tsx b/alm/src/components/ExecutionConfirmation.tsx index c8ddced08..237eabd23 100644 --- a/alm/src/components/ExecutionConfirmation.tsx +++ b/alm/src/components/ExecutionConfirmation.tsx @@ -69,7 +69,7 @@ export const ExecutionConfirmation = ({ const getExecutionStatusElement = (validatorStatus = '') => { switch (validatorStatus) { - case VALIDATOR_CONFIRMATION_STATUS.SUCCESS: + case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS: return {validatorStatus} case VALIDATOR_CONFIRMATION_STATUS.FAILED: return {validatorStatus} diff --git a/alm/src/components/ValidatorsConfirmations.tsx b/alm/src/components/ValidatorsConfirmations.tsx index 127effcfb..3b7e058e9 100644 --- a/alm/src/components/ValidatorsConfirmations.tsx +++ b/alm/src/components/ValidatorsConfirmations.tsx @@ -94,7 +94,7 @@ export const ValidatorsConfirmations = ({ - {requiredSignatures} of {validatorList.length} confirmations required + At least {requiredSignatures} of {validatorList.length} confirmations required
) diff --git a/alm/src/config/constants.ts b/alm/src/config/constants.ts index 8aaaf5d2f..381993500 100644 --- a/alm/src/config/constants.ts +++ b/alm/src/config/constants.ts @@ -54,7 +54,8 @@ export const CONFIRMATIONS_STATUS = { } export const VALIDATOR_CONFIRMATION_STATUS = { - SUCCESS: 'Success', + SUCCESS: 'Confirmed', + EXECUTION_SUCCESS: 'Executed', FAILED: 'Failed', PENDING: 'Pending', WAITING: 'Waiting', diff --git a/alm/src/hooks/useMessageConfirmations.ts b/alm/src/hooks/useMessageConfirmations.ts index 84efc2f9d..abfa3bfb8 100644 --- a/alm/src/hooks/useMessageConfirmations.ts +++ b/alm/src/hooks/useMessageConfirmations.ts @@ -343,7 +343,10 @@ export const useMessageConfirmations = ({ // Sets the message status based in the collected information useEffect( () => { - if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS && existsConfirmation(confirmations)) { + if ( + executionData.status === VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS && + existsConfirmation(confirmations) + ) { const newStatus = executionData.executionResult ? CONFIRMATIONS_STATUS.SUCCESS : CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED diff --git a/alm/src/utils/__tests__/getConfirmationsForTx.test.ts b/alm/src/utils/__tests__/getConfirmationsForTx.test.ts index 689bb8d16..088a2d086 100644 --- a/alm/src/utils/__tests__/getConfirmationsForTx.test.ts +++ b/alm/src/utils/__tests__/getConfirmationsForTx.test.ts @@ -281,9 +281,9 @@ describe('getConfirmationsForTx', () => { ) expect(res2).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res3).toEqual( @@ -382,25 +382,25 @@ describe('getConfirmationsForTx', () => { ) expect(res2).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res3).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res4).toEqual( expect.arrayContaining([ { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED } ]) ) @@ -492,15 +492,15 @@ describe('getConfirmationsForTx', () => { ) expect(res2).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res3).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } ]) ) @@ -596,9 +596,9 @@ describe('getConfirmationsForTx', () => { ) expect(res2).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res3).toEqual( @@ -610,9 +610,13 @@ describe('getConfirmationsForTx', () => { ) }) test('should remove pending state after transaction mined', async () => { - // Validator1 success - // Validator2 failed - // Validator3 Pending + const validator4 = '0x9d2dC11C342F4eF3C5491A048D0f0eBCd2D8f7C3' + const validatorList = [validator1, validator2, validator3, validator4] + + // Validator1 success (ts=100) + // Validator2 failed (ts=200) + // Validator3 Pending (ts=300) + // Validator4 Excess confirmation (Failed) (ts=400) getValidatorConfirmation .mockImplementationOnce(() => async (validator: string) => ({ @@ -623,30 +627,44 @@ describe('getConfirmationsForTx', () => { .mockImplementation(() => async (validator: string) => ({ validator, status: - validator !== validator2 ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED + validator === validator1 || validator === validator3 + ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS + : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED })) getSuccessExecutionTransaction .mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ validator: validatorData.validator, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, - txHash: validatorData.validator === validator1 ? '0x123' : '', - timestamp: validatorData.validator === validator1 ? 123 : 0 + txHash: validatorData.validator === validator1 ? '0x100' : '', + timestamp: validatorData.validator === validator1 ? 100 : 0 })) .mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ validator: validatorData.validator, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, - txHash: validatorData.validator !== validator2 ? '0x123' : '', - timestamp: validatorData.validator !== validator2 ? 123 : 0 + txHash: + validatorData.validator === validator1 ? '0x100' : validatorData.validator === validator3 ? '0x300' : '', + timestamp: validatorData.validator === validator1 ? 100 : validatorData.validator === validator3 ? 300 : '' + })) + getValidatorFailedTransaction + .mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ + validator: validatorData.validator, + status: + validatorData.validator === validator2 + ? VALIDATOR_CONFIRMATION_STATUS.FAILED + : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, + txHash: validatorData.validator === validator2 ? '0x200' : '', + timestamp: validatorData.validator === validator2 ? 200 : 0 + })) + .mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ + validator: validatorData.validator, + status: + validatorData.validator === validator2 || validatorData.validator === validator4 + ? VALIDATOR_CONFIRMATION_STATUS.FAILED + : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, + txHash: + validatorData.validator === validator2 ? '0x200' : validatorData.validator === validator4 ? '0x400' : '', + timestamp: validatorData.validator === validator2 ? 200 : validatorData.validator === validator4 ? 400 : '' })) - getValidatorFailedTransaction.mockImplementation(() => async (validatorData: BasicConfirmationParam) => ({ - validator: validatorData.validator, - status: - validatorData.validator === validator2 - ? VALIDATOR_CONFIRMATION_STATUS.FAILED - : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, - txHash: validatorData.validator === validator2 ? '0x123' : '', - timestamp: validatorData.validator === validator2 ? 123 : 0 - })) getValidatorPendingTransaction .mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ validator: validatorData.validator, @@ -654,8 +672,8 @@ describe('getConfirmationsForTx', () => { validatorData.validator === validator3 ? VALIDATOR_CONFIRMATION_STATUS.PENDING : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED, - txHash: validatorData.validator === validator3 ? '0x123' : '', - timestamp: validatorData.validator === validator3 ? 123 : 0 + txHash: validatorData.validator === validator3 ? '0x300' : '', + timestamp: validatorData.validator === validator3 ? 300 : 0 })) .mockImplementationOnce(() => async (validatorData: BasicConfirmationParam) => ({ validator: validatorData.validator, @@ -712,28 +730,32 @@ describe('getConfirmationsForTx', () => { expect.arrayContaining([ { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res2).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res3).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x300', timestamp: 300 }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res4).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x123', timestamp: 123 } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.PENDING, txHash: '0x300', timestamp: 300 }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) @@ -781,23 +803,26 @@ describe('getConfirmationsForTx', () => { const res7 = setResult.mock.calls[6][0](res6) expect(res5).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res6).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED } ]) ) expect(res7).toEqual( expect.arrayContaining([ - { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 }, - { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x123', timestamp: 123 }, - { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x123', timestamp: 123 } + { validator: validator1, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x100', timestamp: 100 }, + { validator: validator2, status: VALIDATOR_CONFIRMATION_STATUS.FAILED, txHash: '0x200', timestamp: 200 }, + { validator: validator3, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x300', timestamp: 300 }, + { validator: validator4, status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, txHash: '0x400', timestamp: 400 } ]) ) }) diff --git a/alm/src/utils/__tests__/getFinalizationEvent.test.ts b/alm/src/utils/__tests__/getFinalizationEvent.test.ts index ae9c41009..98c4a4d7a 100644 --- a/alm/src/utils/__tests__/getFinalizationEvent.test.ts +++ b/alm/src/utils/__tests__/getFinalizationEvent.test.ts @@ -84,7 +84,7 @@ describe('getFinalizationEvent', () => { expect(setResult).toBeCalledTimes(1) expect(setResult.mock.calls[0][0]).toEqual({ validator: validator1, - status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, + status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS, txHash, timestamp, executionResult: true diff --git a/alm/src/utils/getConfirmationsForTx.ts b/alm/src/utils/getConfirmationsForTx.ts index a7d873547..bc725cc53 100644 --- a/alm/src/utils/getConfirmationsForTx.ts +++ b/alm/src/utils/getConfirmationsForTx.ts @@ -72,6 +72,28 @@ export const getConfirmationsForTx = async ( updateConfirmations(validatorConfirmations) setSignatureCollected(hasEnoughSignatures) + if (hasEnoughSignatures) { + setPendingConfirmations(false) + if (fromHome) { + // fetch collected signatures for possible manual processing + setSignatureCollected( + await Promise.all( + Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call()) + ) + ) + } + } + + // get transactions from success signatures + const successConfirmationWithData = await Promise.all( + successConfirmations.map( + getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, startBlock, getSuccessTransactions) + ) + ) + + const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '') + updateConfirmations(successConfirmationWithTxFound) + // If signatures not collected, look for pending transactions if (!hasEnoughSignatures) { // Check if confirmation is pending @@ -84,16 +106,6 @@ export const getConfirmationsForTx = async ( ) updateConfirmations(validatorPendingConfirmations) setPendingConfirmations(validatorPendingConfirmations.length > 0) - } else { - setPendingConfirmations(false) - if (fromHome) { - // fetch collected signatures for possible manual processing - setSignatureCollected( - await Promise.all( - Array.from(Array(requiredSignatures).keys()).map(i => bridgeContract.methods.signature(hashMsg, i).call()) - ) - ) - } } const undefinedConfirmations = validatorConfirmations.filter( @@ -106,9 +118,21 @@ export const getConfirmationsForTx = async ( getValidatorFailedTransaction(bridgeContract, messageData, startBlock, getFailedTransactions) ) ) - const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter( + let validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter( c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED ) + if (hasEnoughSignatures) { + const lastTS = Math.max(...successConfirmationWithTxFound.map(c => c.timestamp || 0)) + validatorFailedConfirmations = validatorFailedConfirmations.map( + c => + c.timestamp < lastTS + ? c + : { + ...c, + status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS + } + ) + } setFailedConfirmations(validatorFailedConfirmations.length > validatorList.length - requiredSignatures) updateConfirmations(validatorFailedConfirmations) @@ -125,16 +149,6 @@ export const getConfirmationsForTx = async ( updateConfirmations(notRequiredConfirmations) } - // get transactions from success signatures - const successConfirmationWithData = await Promise.all( - successConfirmations.map( - getSuccessExecutionTransaction(web3, bridgeContract, fromHome, messageData, startBlock, getSuccessTransactions) - ) - ) - - const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '') - updateConfirmations(successConfirmationWithTxFound) - // retry if not all signatures are collected and some confirmations are still missing // or some success transactions were not fetched successfully if ( diff --git a/alm/src/utils/getFinalizationEvent.ts b/alm/src/utils/getFinalizationEvent.ts index 81c0a6d7d..4489271ae 100644 --- a/alm/src/utils/getFinalizationEvent.ts +++ b/alm/src/utils/getFinalizationEvent.ts @@ -59,7 +59,7 @@ export const getSuccessExecutionData = async ( const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from) return { - status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS, + status: VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS, validator: validatorAddress, txHash: event.transactionHash, timestamp: blockTimestamp, From 910c3759c169b6451e81abadb6c2a0ccc2d3a0ec Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Thu, 17 Mar 2022 15:11:14 +0400 Subject: [PATCH 4/5] ALM: warning for auto-relayed messages (#644) --- alm/src/components/ConfirmationsContainer.tsx | 2 +- alm/src/components/ExecutionConfirmation.tsx | 27 ++++++-- alm/src/components/MainPage.tsx | 4 +- alm/src/components/commons/WarningAlert.tsx | 34 +++++++++++ alm/src/state/StateProvider.tsx | 11 +++- alm/src/themes/GlobalStyle.tsx | 2 + alm/src/themes/Light.ts | 4 ++ alm/src/utils/web3.ts | 61 +++++++++++++++++-- 8 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 alm/src/components/commons/WarningAlert.tsx diff --git a/alm/src/components/ConfirmationsContainer.tsx b/alm/src/components/ConfirmationsContainer.tsx index a307fec97..e7e2befa3 100644 --- a/alm/src/components/ConfirmationsContainer.tsx +++ b/alm/src/components/ConfirmationsContainer.tsx @@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({ /> {signatureCollected && ( { - const { foreign } = useStateProvider() + const { foreign, setWarning } = useStateProvider() const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false) const availableManualExecution = !isHome && @@ -67,6 +68,24 @@ export const ExecutionConfirmation = ({ [availableManualExecution, foreign.bridgeContract] ) + useEffect( + () => { + if (!message.data || !executionData || !availableManualExecution) return + + try { + const fileName = 'warnRules' + const rules: WarnRule[] = require(`../snapshots/${fileName}.json`) + for (let rule of rules) { + if (matchesRule(rule, message)) { + setWarning(rule.message) + return + } + } + } catch (e) {} + }, + [availableManualExecution, executionData, message, message.data, setWarning] + ) + const getExecutionStatusElement = (validatorStatus = '') => { switch (validatorStatus) { case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS: @@ -125,7 +144,7 @@ export const ExecutionConfirmation = ({ { const history = useHistory() - const { home, foreign, error, setError } = useStateProvider() + const { home, foreign, error, setError, warning, setWarning } = useStateProvider() const [networkName, setNetworkName] = useState('') const [receipt, setReceipt] = useState>(null) const [showInfoAlert, setShowInfoAlert] = useState(false) @@ -133,6 +134,7 @@ export const MainPage = () => { )} {error && setError('')} error={error} />} + {warning && setWarning('')} error={warning} />} } /> void; error: string }) => { + return ( +
+ + + {error} + + + + +
+ ) +} diff --git a/alm/src/state/StateProvider.tsx b/alm/src/state/StateProvider.tsx index e324c9b64..f753a91d5 100644 --- a/alm/src/state/StateProvider.tsx +++ b/alm/src/state/StateProvider.tsx @@ -27,6 +27,8 @@ export interface StateContext { loading: boolean error: string setError: Function + warning: string + setWarning: Function } const initialState = { @@ -46,7 +48,9 @@ const initialState = { }, loading: true, error: '', - setError: () => {} + setError: () => {}, + warning: '', + setWarning: () => {} } const StateContext = createContext(initialState) @@ -59,6 +63,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => { foreignWeb3: foreignNetwork.web3 }) const [error, setError] = useState('') + const [warning, setWarning] = useState('') const value = { home: { @@ -75,7 +80,9 @@ export const StateProvider = ({ children }: { children: ReactNode }) => { }, loading: homeNetwork.loading || foreignNetwork.loading, error, - setError + setError, + warning, + setWarning } return {children} diff --git a/alm/src/themes/GlobalStyle.tsx b/alm/src/themes/GlobalStyle.tsx index a8e62da9a..ca7af0d73 100644 --- a/alm/src/themes/GlobalStyle.tsx +++ b/alm/src/themes/GlobalStyle.tsx @@ -28,5 +28,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>` --not-required-bg-color: ${props => props.theme.notRequired.backgroundColor}; --failed-color: ${props => props.theme.failed.textColor}; --failed-bg-color: ${props => props.theme.failed.backgroundColor}; + --warning-color: ${props => props.theme.warning.textColor}; + --warning-bg-color: ${props => props.theme.warning.backgroundColor}; } ` diff --git a/alm/src/themes/Light.ts b/alm/src/themes/Light.ts index 0c972e841..1548dedef 100644 --- a/alm/src/themes/Light.ts +++ b/alm/src/themes/Light.ts @@ -17,6 +17,10 @@ const theme = { failed: { textColor: '#de4437', backgroundColor: 'rgba(222,68,55,.1)' + }, + warning: { + textColor: '#ffa758', + backgroundColor: 'rgba(222,68,55,.1)' } } export default theme diff --git a/alm/src/utils/web3.ts b/alm/src/utils/web3.ts index 09d129b72..ff3fd197c 100644 --- a/alm/src/utils/web3.ts +++ b/alm/src/utils/web3.ts @@ -10,6 +10,37 @@ import { SnapshotProvider } from '../services/SnapshotProvider' export interface MessageObject { id: string data: string + sender?: string + executor?: string + obToken?: string + obReceiver?: string +} + +export interface WarnRule { + message: string + sender?: string + executor?: string + obToken?: string + obReceiver?: string +} + +export const matchesRule = (rule: WarnRule, msg: MessageObject) => { + if (!msg.executor || !msg.sender) { + return false + } + if (!!rule.executor && rule.executor.toLowerCase() !== msg.executor.toLowerCase()) { + return false + } + if (!!rule.sender && rule.sender.toLowerCase() !== msg.sender.toLowerCase()) { + return false + } + if (!!rule.obToken && (!msg.obToken || rule.obToken.toLowerCase() !== msg.obToken.toLowerCase())) { + return false + } + if (!!rule.obReceiver && (!msg.obReceiver || rule.obReceiver.toLowerCase() !== msg.obReceiver.toLowerCase())) { + return false + } + return true } const rawGetWeb3 = (url: string) => new Web3(new Web3.providers.HttpProvider(url)) @@ -26,15 +57,33 @@ export const filterEventsByAbi = ( const eventHash = web3.eth.abi.encodeEventSignature(eventAbi) const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash) + if (!eventAbi || !eventAbi.inputs || !eventAbi.inputs.length) { + return [] + } + const inputs = eventAbi.inputs return events.map(e => { - let decodedLogs: { [p: string]: string } = { - messageId: '', - encodedData: '' + const { messageId, encodedData } = web3.eth.abi.decodeLog(inputs, e.data, [e.topics[1]]) + let sender, executor, obToken, obReceiver + if (encodedData.length >= 160) { + sender = `0x${encodedData.slice(66, 106)}` + executor = `0x${encodedData.slice(106, 146)}` + const dataOffset = + 160 + (parseInt(encodedData.slice(154, 156), 16) + parseInt(encodedData.slice(156, 158), 16)) * 2 + 8 + if (encodedData.length >= dataOffset + 64) { + obToken = `0x${encodedData.slice(dataOffset + 24, dataOffset + 64)}` + } + if (encodedData.length >= dataOffset + 128) { + obReceiver = `0x${encodedData.slice(dataOffset + 88, dataOffset + 128)}` + } } - if (eventAbi && eventAbi.inputs && eventAbi.inputs.length) { - decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]]) + return { + id: messageId || '', + data: encodedData || '', + sender, + executor, + obToken, + obReceiver } - return { id: decodedLogs.messageId, data: decodedLogs.encodedData } }) } From 735aa75f81715e96ed4e3cbca6af2aee1ecfab53 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Thu, 17 Mar 2022 17:41:33 +0400 Subject: [PATCH 5/5] ALM: reorder warnings --- alm/src/components/ExecutionConfirmation.tsx | 9 ++++++++- alm/src/components/MainPage.tsx | 6 +----- alm/src/components/ManualExecutionButton.tsx | 6 ++++-- alm/src/components/commons/ErrorAlert.tsx | 2 +- alm/src/components/commons/WarningAlert.tsx | 2 +- alm/src/state/StateProvider.tsx | 20 +++----------------- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/alm/src/components/ExecutionConfirmation.tsx b/alm/src/components/ExecutionConfirmation.tsx index f86334a7b..38cde65a5 100644 --- a/alm/src/components/ExecutionConfirmation.tsx +++ b/alm/src/components/ExecutionConfirmation.tsx @@ -11,6 +11,8 @@ import { Thead, AgeTd, StatusTd } from './commons/Table' import { ManualExecutionButton } from './ManualExecutionButton' import { useStateProvider } from '../state/StateProvider' import { matchesRule, MessageObject, WarnRule } from '../utils/web3' +import { WarningAlert } from './commons/WarningAlert' +import { ErrorAlert } from './commons/ErrorAlert' const StyledExecutionConfirmation = styled.div` margin-top: 30px; @@ -35,8 +37,10 @@ export const ExecutionConfirmation = ({ executionEventsFetched, setPendingExecution }: ExecutionConfirmationParams) => { - const { foreign, setWarning } = useStateProvider() + const { foreign } = useStateProvider() const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false) + const [error, setError] = useState('') + const [warning, setWarning] = useState('') const availableManualExecution = !isHome && (executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING || @@ -106,6 +110,8 @@ export const ExecutionConfirmation = ({ return ( + {error && setError('')} error={error} />} + {warning && setWarning('')} error={warning} />} @@ -148,6 +154,7 @@ export const ExecutionConfirmation = ({ setExecutionData={setExecutionData} signatureCollected={signatureCollected as string[]} setPendingExecution={setPendingExecution} + setError={setError} /> )} diff --git a/alm/src/components/MainPage.tsx b/alm/src/components/MainPage.tsx index e5055208c..88ced8153 100644 --- a/alm/src/components/MainPage.tsx +++ b/alm/src/components/MainPage.tsx @@ -8,8 +8,6 @@ import { TransactionReceipt } from 'web3-eth' import { InfoAlert } from './commons/InfoAlert' import { ExplorerTxLink } from './commons/ExplorerTxLink' import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants' -import { ErrorAlert } from './commons/ErrorAlert' -import { WarningAlert } from './commons/WarningAlert' const StyledMainPage = styled.div` text-align: center; @@ -53,7 +51,7 @@ export interface FormSubmitParams { export const MainPage = () => { const history = useHistory() - const { home, foreign, error, setError, warning, setWarning } = useStateProvider() + const { home, foreign } = useStateProvider() const [networkName, setNetworkName] = useState('') const [receipt, setReceipt] = useState>(null) const [showInfoAlert, setShowInfoAlert] = useState(false) @@ -133,8 +131,6 @@ export const MainPage = () => { )} - {error && setError('')} error={error} />} - {warning && setWarning('')} error={warning} />} } /> { - const { foreign, setError } = useStateProvider() + const { foreign } = useStateProvider() const { library, activate, account, active } = useWeb3React() const [manualExecution, setManualExecution] = useState(false) const [allowFailures, setAllowFailures] = useState(false) diff --git a/alm/src/components/commons/ErrorAlert.tsx b/alm/src/components/commons/ErrorAlert.tsx index 236353b00..28eed2af1 100644 --- a/alm/src/components/commons/ErrorAlert.tsx +++ b/alm/src/components/commons/ErrorAlert.tsx @@ -33,7 +33,7 @@ export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: str } return (
- + {text} diff --git a/alm/src/components/commons/WarningAlert.tsx b/alm/src/components/commons/WarningAlert.tsx index 8705b8f81..160147758 100644 --- a/alm/src/components/commons/WarningAlert.tsx +++ b/alm/src/components/commons/WarningAlert.tsx @@ -22,7 +22,7 @@ const TextContainer = styled.div` export const WarningAlert = ({ onClick, error }: { onClick: () => void; error: string }) => { return (
- + {error} diff --git a/alm/src/state/StateProvider.tsx b/alm/src/state/StateProvider.tsx index f753a91d5..c495ef65f 100644 --- a/alm/src/state/StateProvider.tsx +++ b/alm/src/state/StateProvider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, ReactNode, useState } from 'react' +import React, { createContext, ReactNode } from 'react' import { useNetwork } from '../hooks/useNetwork' import { HOME_RPC_URL, @@ -25,10 +25,6 @@ export interface StateContext { home: BaseNetworkParams foreign: BaseNetworkParams loading: boolean - error: string - setError: Function - warning: string - setWarning: Function } const initialState = { @@ -46,11 +42,7 @@ const initialState = { bridgeAddress: FOREIGN_BRIDGE_ADDRESS, bridgeContract: null }, - loading: true, - error: '', - setError: () => {}, - warning: '', - setWarning: () => {} + loading: true } const StateContext = createContext(initialState) @@ -62,8 +54,6 @@ export const StateProvider = ({ children }: { children: ReactNode }) => { homeWeb3: homeNetwork.web3, foreignWeb3: foreignNetwork.web3 }) - const [error, setError] = useState('') - const [warning, setWarning] = useState('') const value = { home: { @@ -78,11 +68,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => { bridgeContract: foreignBridge, ...foreignNetwork }, - loading: homeNetwork.loading || foreignNetwork.loading, - error, - setError, - warning, - setWarning + loading: homeNetwork.loading || foreignNetwork.loading } return {children}