From da229405045c3dbba62122be564b2f10fade59e4 Mon Sep 17 00:00:00 2001 From: Richard Watts Date: Tue, 24 Dec 2024 12:12:51 +0000 Subject: [PATCH] (feat) An exceptions facility so that we can retroactively correct (so long as we have enough validators agreeing) "bad" transactions like those arising from #24 --- bridge-validators/config.toml | 9 ++++++ bridge-validators/src/client.rs | 2 +- bridge-validators/src/exceptions.rs | 44 ++++++++++++++++++++++++++--- bridge-validators/src/main.rs | 28 +++++++++++++++++- 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/bridge-validators/config.toml b/bridge-validators/config.toml index 5a7e990..5276538 100644 --- a/bridge-validators/config.toml +++ b/bridge-validators/config.toml @@ -42,6 +42,15 @@ legacy_gas_estimation_percent = 130 ## fetching the receipt and scanning it for relevant logs. This is slow, but at least it works .. ## use_get_transactions = true +## Defines the exception set - events that should be treated like other events. +## This is used for exceptional reprocessing of bridge transactions that have been generated +## as the result of bugs and need to be reprocessed. +## Annoyingly, the exceptions must be single-line as otherwise the toml parser will complain. +##exceptions = [ +## { transaction_id = "0xa4738bffe20fff0d71d449685ad8e252dc2645948034d0e1ca12772422744b0c", block_hash="0xb71c1c134044480d1ffd1b7822382769b160b38729a27b35d14c5093b93911c9", block_number=46770995, chain_id="0x61", replacement_bytes="0x00000000000000000000000041823941d00f47ea1a98d75586915bf828f4a038000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e41a90748a000000000000000000000000000000000000000000000000000000000000006100000000000000000000000036b8a9cd6bf9bfa5984093005cf81cafb1bf06f7000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009be4dcfb335a263c65a8a763d55710718bbdb416000000000000000000000000b85ff091342e2e7a7461238796d5224fa81ca556000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000", replacement_chainid="0x000000000000000000000000000000000000000000000000000000000000814d" }] + + + # BSC Mainnet # [[chain_configs]] diff --git a/bridge-validators/src/client.rs b/bridge-validators/src/client.rs index 77a25ac..9239c68 100644 --- a/bridge-validators/src/client.rs +++ b/bridge-validators/src/client.rs @@ -107,7 +107,7 @@ impl ChainClient { scan_behind_blocks: config.scan_behind_blocks.unwrap_or_default(), log_strategy: strategy, to_block_number: config.to_block_number, - except: exceptions::ExceptionProcessor::new(), + except: exceptions::ExceptionProcessor::new(config, chain_id), }) } } diff --git a/bridge-validators/src/exceptions.rs b/bridge-validators/src/exceptions.rs index da0d923..4ea2273 100644 --- a/bridge-validators/src/exceptions.rs +++ b/bridge-validators/src/exceptions.rs @@ -1,18 +1,54 @@ -use ethers::types::Log; +use crate::{ChainConfig, Exception}; +use ethers::types::{Log, H256, U256}; +use std::collections::HashMap; +use tracing::warn; /// The exception processor handles exceptions - these are txns which were issued in error, usually as a result /// of a bug in the relayer contracts; their parameters are corrected here before being passed on to the rest of /// the relayer logic for execution. #[derive(Debug, Clone)] -pub struct ExceptionProcessor {} +pub struct ExceptionProcessor { + exceptions_by_txnhash: HashMap, +} impl ExceptionProcessor { - pub fn new() -> Self { - ExceptionProcessor {} + pub fn new(config: &ChainConfig, chain_id: U256) -> Self { + let mut exceptions_by_txnhash = HashMap::new(); + config.exceptions.iter().for_each(|v| { + v.iter().for_each(|i| { + if i.chain_id == chain_id { + exceptions_by_txnhash.insert(i.transaction_id, i.clone()); + } + }) + }); + warn!( + "Loaded {0} exceptions for chain_id {1}", + exceptions_by_txnhash.len(), + chain_id + ); + ExceptionProcessor { + exceptions_by_txnhash, + } } // We'll warn!() if we have to drop a log. pub fn transform_log(&self, log: &Log) -> Option { + if let Some(hash) = log.transaction_hash { + if let Some(except) = self.exceptions_by_txnhash.get(&hash) { + if log + .block_number + .map_or(false, |x| x.as_u64() == except.block_number) + && log.block_hash.map_or(false, |x| x == except.block_hash) + && log.topics.len() == 2 + { + let mut new_log = log.clone(); + new_log.topics[1] = except.replacement_chainid; + new_log.data = except.replacement_bytes.clone(); + warn!("Found a match for exception {except:?} with log {log:?} - replacing log data to form {new_log:?}"); + return Some(new_log); + } + } + } Some(log.clone()) } } diff --git a/bridge-validators/src/main.rs b/bridge-validators/src/main.rs index 85443b2..b374884 100644 --- a/bridge-validators/src/main.rs +++ b/bridge-validators/src/main.rs @@ -13,9 +13,14 @@ use std::{fs, path::PathBuf}; use anyhow::Result; use clap::Parser; -use ethers::{contract::abigen, types::Address}; +use ethers::{ + contract::abigen, + types::{Address, Bytes, H256, U256}, + utils::hex, +}; use libp2p::{Multiaddr, PeerId}; use serde::Deserialize; +use serde::{de::Error, Deserializer}; use tracing::info; use tracing_subscriber::EnvFilter; use validator_node::ValidatorNodeConfig; @@ -25,6 +30,26 @@ use crate::{crypto::SecretKey, p2p_node::P2pNode}; abigen!(ChainGateway, "abi/ChainGateway.json",); abigen!(ValidatorManager, "abi/ValidatorManager.json"); +#[derive(Debug, Clone, Deserialize)] +pub struct Exception { + pub transaction_id: H256, + pub block_hash: H256, + pub block_number: u64, + pub chain_id: U256, + #[serde(deserialize_with = "from_hex")] + pub replacement_bytes: Bytes, + pub replacement_chainid: H256, +} + +fn from_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = Deserialize::deserialize(deserializer)?; + let some_bytes = hex::decode(s).map_err(D::Error::custom)?; + Ok(Bytes::from(some_bytes)) +} + #[derive(Debug, Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct ChainConfig { @@ -36,6 +61,7 @@ pub struct ChainConfig { pub scan_behind_blocks: Option, pub use_get_transactions: Option, pub to_block_number: Option, + pub exceptions: Option>, } #[derive(Debug, Clone, Deserialize)]