Helpers for overriding AMB signatures (#640)

This commit is contained in:
Kirill Fedoseev 2022-02-18 17:35:12 +03:00 committed by GitHub
parent 5bc562e810
commit 981231fb47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 30 deletions

@ -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,

@ -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<string[]>([])
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 (
<div>
<div className="is-center">
<ActionButton disabled={notReady} className="button outline" onClick={() => setManualExecution(true)}>
Execute
<ActionButton disabled={!ready} className="button outline" onClick={() => setManualExecution(true)}>
{title}
</ActionButton>
</div>
{safeExecutionAvailable && (

@ -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<TransactionReceipt>
}
export const useValidatorContract = ({ receipt, fromHome }: useValidatorContractParams) => {
export const useValidatorContract = (isHome: boolean, blockNumber: number | 'latest') => {
const [validatorContract, setValidatorContract] = useState<Maybe<Contract>>(null)
const [requiredSignatures, setRequiredSignatures] = useState(0)
const [validatorList, setValidatorList] = useState([])
const [validatorList, setValidatorList] = useState<string[]>([])
const { home, foreign } = useStateProvider()
@ -29,34 +23,34 @@ export const useValidatorContract = ({ receipt, fromHome }: useValidatorContract
const callRequiredSignatures = async (
contract: Maybe<Contract>,
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<Contract>,
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 {

@ -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()

@ -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'",

@ -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()