From b8ccae461f0cda49b01ed5fe761acb892c337d5b Mon Sep 17 00:00:00 2001 From: wenyuanhust Date: Mon, 4 Dec 2023 20:32:30 +0800 Subject: [PATCH 1/4] refactor: abi err msg parse --- crates/relayer/src/chain/axon.rs | 3 +- crates/relayer/src/chain/axon/eth_err.rs | 194 +++++++++++++++++++++++ crates/relayer/src/chain/axon/utils.rs | 6 +- 3 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 crates/relayer/src/chain/axon/eth_err.rs diff --git a/crates/relayer/src/chain/axon.rs b/crates/relayer/src/chain/axon.rs index ff5d9193..d7569303 100644 --- a/crates/relayer/src/chain/axon.rs +++ b/crates/relayer/src/chain/axon.rs @@ -107,6 +107,7 @@ use super::{ use tokio::runtime::Runtime as TokioRuntime; pub mod contract; +mod eth_err; mod monitor; mod msg; mod rpc; @@ -1469,7 +1470,7 @@ impl AxonChain { } }; let tx_receipt = tx_receipt - .map_err(convert_err)? + .map_err(convert_abi_err)? .ok_or(Error::send_tx(String::from("fail to send tx")))?; let event: IbcEvent = { use contract::OwnableIBCHandlerEvents::*; diff --git a/crates/relayer/src/chain/axon/eth_err.rs b/crates/relayer/src/chain/axon/eth_err.rs new file mode 100644 index 00000000..6e1aba01 --- /dev/null +++ b/crates/relayer/src/chain/axon/eth_err.rs @@ -0,0 +1,194 @@ +use ethers::abi::ethabi::{decode, ParamType}; +use hex::FromHex; + +enum PanicError { + Generic, + AssertFailed, + ArithmeticOverflow = 0x11, + DivisionByZero, + InvalidEnumConversion = 0x21, + InvalidEncoding, + EmptyArrayPop = 0x31, + OutOfBoundsAccess, + ExcessiveAllocation = 0x41, + UninitializedInternalFunction = 0x51, + Unknown, +} + +fn parse_panic_error(hex_string: &str) -> PanicError { + match hex_string { + "00" => PanicError::Generic, + "01" => PanicError::AssertFailed, + "11" => PanicError::ArithmeticOverflow, + "12" => PanicError::DivisionByZero, + "21" => PanicError::InvalidEnumConversion, + "22" => PanicError::InvalidEncoding, + "31" => PanicError::EmptyArrayPop, + "32" => PanicError::OutOfBoundsAccess, + "41" => PanicError::ExcessiveAllocation, + "51" => PanicError::UninitializedInternalFunction, + _ => PanicError::Unknown, + } +} + +fn handle_panic_error(error: PanicError) -> String { + match error { + PanicError::Generic => format!( + "Panic code: 0x{:x}, Generic compiler inserted panic", + error as u16 + ), + PanicError::AssertFailed => format!("Panic code: 0x{:x}, Assertion failed", error as u16), + PanicError::ArithmeticOverflow => format!( + "Panic code: 0x{:x}, Arithmetic operation resulted in overflow", + error as u16 + ), + PanicError::DivisionByZero => { + format!( + "Panic code: 0x{:x}, Division or modulo by zero", + error as u16 + ) + } + PanicError::InvalidEnumConversion => { + format!("Panic code: 0x{:x}, Invalid enum conversion", error as u16) + } + PanicError::InvalidEncoding => { + format!("Panic code: 0x{:x}, Invalid encoding", error as u16) + } + PanicError::EmptyArrayPop => format!( + "Panic code: 0x{:x}, Attempted to pop an empty array", + error as u16 + ), + PanicError::OutOfBoundsAccess => { + format!("Panic code: 0x{:x}, Out-of-bounds access", error as u16) + } + PanicError::ExcessiveAllocation => format!( + "Panic code: 0x{:x}, Excessive memory allocation", + error as u16 + ), + PanicError::UninitializedInternalFunction => { + format!( + "Panic code: 0x{:x}, Called an uninitialized internal function", + error as u16 + ) + } + PanicError::Unknown => format!("Panic code: 0x{:x}, Unknown panic", error as u16), + } +} + +const ERROR_SELECTOR: &str = "08c379a0"; +const PANIC_SELECTOR: &str = "4e487b71"; + +#[derive(PartialEq)] +enum AbiErrorType { + Panic, + Revert, + Unknown, +} + +impl AbiErrorType { + fn from_signature(signature: &str) -> Self { + match signature { + PANIC_SELECTOR => AbiErrorType::Panic, + ERROR_SELECTOR => AbiErrorType::Revert, + _ => AbiErrorType::Unknown, + } + } + + fn param_type(&self) -> Option { + match self { + AbiErrorType::Panic => Some(ParamType::Uint(32)), + AbiErrorType::Revert => Some(ParamType::String), + AbiErrorType::Unknown => None, + } + } +} + +pub(crate) fn parse_abi_err_data(err: &str) -> String { + let start_index = "Contract call reverted with data: 0x".len(); + let method_signature_len = "08c379a0".len(); + + let method_signature = &err[start_index..(start_index + method_signature_len)]; + let hex_error_message = &err[(start_index + method_signature_len)..]; + + let hex_error: Vec = match FromHex::from_hex(hex_error_message) { + Ok(hex_error) => hex_error, + Err(err) => { + return format!( + "parse_abi_err_data from_hex: {:?}, data: {}!", + err, hex_error_message + ); + } + }; + + let error_type = AbiErrorType::from_signature(method_signature); + if let Some(error_abi) = error_type.param_type() { + let decoded = decode(&[error_abi], &hex_error); + match decoded { + Ok(decoded) => { + let strings: Vec = + decoded.into_iter().map(|token| token.to_string()).collect(); + let result = strings.join(";"); + if error_type == AbiErrorType::Panic { + let panic_error = parse_panic_error(&result); + handle_panic_error(panic_error) + } else { + result + } + } + Err(err) => { + format!( + "parse_abi_err_data decode: {:?}, data: {:x?}!", + err, hex_error + ) + } + } + } else { + format!( + "parse_abi_err_data: unknown exception method {}!", + method_signature + ) + } +} + +#[cfg(test)] +mod test { + use crate::chain::axon::eth_err::parse_abi_err_data; + + #[test] + fn test_sol_revert() { + let err_string = "Contract call reverted with data: 0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; + let err = parse_abi_err_data(err_string); + assert_eq!(err, "test failed to create client"); + } + + #[test] + fn test_sol_panic() { + let err_string = "Contract call reverted with data: 0x4e487b710000000000000000000000000000000000000000000000000000000000000012"; + let err = parse_abi_err_data(err_string); + assert_eq!(err, "Panic code: 0x12, Division or modulo by zero"); + } + + #[test] + fn test_sol_unknown_method() { + let err_string = "Contract call reverted with data: 0x18c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; + let err = parse_abi_err_data(err_string); + assert_eq!( + err, + "parse_abi_err_data: unknown exception method 18c379a0!" + ); + } + + #[test] + fn test_sol_invalid_hex() { + let err_string = "Contract call reverted with data: 0x08c379a00x00000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; + let err = parse_abi_err_data(err_string); + assert_eq!(err, "parse_abi_err_data from_hex: InvalidHexCharacter { c: 'x', index: 1 }, data: 0x00000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000!"); + } + + #[test] + fn test_sol_invalid_data() { + let err_string = "Contract call reverted with data: 0x08c379a00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; + let err = parse_abi_err_data(err_string); + assert_eq!(err, "parse_abi_err_data decode: InvalidData, data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1c, 74, 65, 73, 74, 20, 66, 61, 69, 6c, 65, 64, 20, 74, 6f, 20, 63, 72, 65, 61, 74, 65, 20, 63, 6c, 69, 65, 6e, 74, 0, 0, 0, 0]!"); + } +} diff --git a/crates/relayer/src/chain/axon/utils.rs b/crates/relayer/src/chain/axon/utils.rs index 5d141404..1796d6b4 100644 --- a/crates/relayer/src/chain/axon/utils.rs +++ b/crates/relayer/src/chain/axon/utils.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use axon_tools::types::{Block as AxonBlock, Proof as AxonProof, ValidatorExtend}; use crate::{ - chain::SEC_TO_NANO, + chain::{axon::eth_err::parse_abi_err_data, SEC_TO_NANO}, client_state::{AnyClientState, IdentifiedAnyClientState}, consensus_state::AnyConsensusState, error::Error, @@ -29,6 +29,10 @@ pub fn convert_err(err: T) -> Error { Error::other_error(err.to_string()) } +pub fn convert_abi_err(err: T) -> Error { + Error::other_error(parse_abi_err_data(&err.to_string())) +} + pub fn to_identified_any_client_state( client_state: ðers::core::types::Bytes, ) -> Result { From cda7dafcf1fcf709e62e7b84ee3c11c57c9b8ac5 Mon Sep 17 00:00:00 2001 From: Yin Guanhao Date: Tue, 5 Dec 2023 12:12:32 +0800 Subject: [PATCH 2/4] Use ContractError::Revert bytes directly and use impl EthError for String --- crates/relayer/src/chain/axon.rs | 14 +- crates/relayer/src/chain/axon/eth_err.rs | 254 ++++++++++------------- crates/relayer/src/chain/axon/utils.rs | 17 +- 3 files changed, 127 insertions(+), 158 deletions(-) diff --git a/crates/relayer/src/chain/axon.rs b/crates/relayer/src/chain/axon.rs index d7569303..e5f79ab6 100644 --- a/crates/relayer/src/chain/axon.rs +++ b/crates/relayer/src/chain/axon.rs @@ -1371,9 +1371,15 @@ impl AxonChain { macro_rules! convert { ($self:ident, $msg:ident, $eventy:ty, $method:ident) => {{ let msg: $eventy = $msg.try_into()?; - $self - .rt - .block_on(async { Ok($self.contract()?.$method(msg.clone()).send().await?.await?) }) + $self.rt.block_on(async { + Ok($self + .contract()? + .$method(msg.clone()) + .send() + .await + .map_err(decode_revert_error)? + .await?) + }) }}; } @@ -1470,7 +1476,7 @@ impl AxonChain { } }; let tx_receipt = tx_receipt - .map_err(convert_abi_err)? + .map_err(convert_err)? .ok_or(Error::send_tx(String::from("fail to send tx")))?; let event: IbcEvent = { use contract::OwnableIBCHandlerEvents::*; diff --git a/crates/relayer/src/chain/axon/eth_err.rs b/crates/relayer/src/chain/axon/eth_err.rs index 6e1aba01..18549c3d 100644 --- a/crates/relayer/src/chain/axon/eth_err.rs +++ b/crates/relayer/src/chain/axon/eth_err.rs @@ -1,6 +1,25 @@ -use ethers::abi::ethabi::{decode, ParamType}; -use hex::FromHex; +use ethers::abi::{Detokenize, ParamType, Uint}; +/// Panic(uint) +pub struct Panic(Uint); + +impl Panic { + // Can't get the right selector with `derive(EthError)`, so I implement this manually. + pub fn decode_with_selector(bytes: &[u8]) -> Option { + let bytes = bytes.strip_prefix(b"\x4e\x48\x7b\x71")?; + let tokens = ethers::abi::decode(&[ParamType::Uint(32)], bytes).ok()?; + Some(Panic(Uint::from_tokens(tokens).ok()?)) + } +} + +impl std::fmt::Display for Panic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let e = PanicError::from_code(self.0.low_u32()); + write!(f, "{e}") + } +} + +#[derive(Copy, Clone)] enum PanicError { Generic, AssertFailed, @@ -15,144 +34,103 @@ enum PanicError { Unknown, } -fn parse_panic_error(hex_string: &str) -> PanicError { - match hex_string { - "00" => PanicError::Generic, - "01" => PanicError::AssertFailed, - "11" => PanicError::ArithmeticOverflow, - "12" => PanicError::DivisionByZero, - "21" => PanicError::InvalidEnumConversion, - "22" => PanicError::InvalidEncoding, - "31" => PanicError::EmptyArrayPop, - "32" => PanicError::OutOfBoundsAccess, - "41" => PanicError::ExcessiveAllocation, - "51" => PanicError::UninitializedInternalFunction, - _ => PanicError::Unknown, +impl PanicError { + fn from_code(code: u32) -> Self { + match code { + 0 => PanicError::Generic, + 0x01 => PanicError::AssertFailed, + 0x11 => PanicError::ArithmeticOverflow, + 0x12 => PanicError::DivisionByZero, + 0x21 => PanicError::InvalidEnumConversion, + 0x22 => PanicError::InvalidEncoding, + 0x31 => PanicError::EmptyArrayPop, + 0x32 => PanicError::OutOfBoundsAccess, + 0x41 => PanicError::ExcessiveAllocation, + 0x51 => PanicError::UninitializedInternalFunction, + _ => PanicError::Unknown, + } } } -fn handle_panic_error(error: PanicError) -> String { - match error { - PanicError::Generic => format!( - "Panic code: 0x{:x}, Generic compiler inserted panic", - error as u16 - ), - PanicError::AssertFailed => format!("Panic code: 0x{:x}, Assertion failed", error as u16), - PanicError::ArithmeticOverflow => format!( - "Panic code: 0x{:x}, Arithmetic operation resulted in overflow", - error as u16 - ), - PanicError::DivisionByZero => { - format!( - "Panic code: 0x{:x}, Division or modulo by zero", +impl std::fmt::Display for PanicError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let error = *self; + match error { + PanicError::Generic => write!( + f, + "Panic code: 0x{:x}, Generic compiler inserted panic", error as u16 - ) - } - PanicError::InvalidEnumConversion => { - format!("Panic code: 0x{:x}, Invalid enum conversion", error as u16) - } - PanicError::InvalidEncoding => { - format!("Panic code: 0x{:x}, Invalid encoding", error as u16) - } - PanicError::EmptyArrayPop => format!( - "Panic code: 0x{:x}, Attempted to pop an empty array", - error as u16 - ), - PanicError::OutOfBoundsAccess => { - format!("Panic code: 0x{:x}, Out-of-bounds access", error as u16) - } - PanicError::ExcessiveAllocation => format!( - "Panic code: 0x{:x}, Excessive memory allocation", - error as u16 - ), - PanicError::UninitializedInternalFunction => { - format!( - "Panic code: 0x{:x}, Called an uninitialized internal function", + ), + PanicError::AssertFailed => { + write!(f, "Panic code: 0x{:x}, Assertion failed", error as u16) + } + PanicError::ArithmeticOverflow => write!( + f, + "Panic code: 0x{:x}, Arithmetic operation resulted in overflow", error as u16 - ) - } - PanicError::Unknown => format!("Panic code: 0x{:x}, Unknown panic", error as u16), - } -} - -const ERROR_SELECTOR: &str = "08c379a0"; -const PANIC_SELECTOR: &str = "4e487b71"; - -#[derive(PartialEq)] -enum AbiErrorType { - Panic, - Revert, - Unknown, -} - -impl AbiErrorType { - fn from_signature(signature: &str) -> Self { - match signature { - PANIC_SELECTOR => AbiErrorType::Panic, - ERROR_SELECTOR => AbiErrorType::Revert, - _ => AbiErrorType::Unknown, - } - } - - fn param_type(&self) -> Option { - match self { - AbiErrorType::Panic => Some(ParamType::Uint(32)), - AbiErrorType::Revert => Some(ParamType::String), - AbiErrorType::Unknown => None, - } - } -} - -pub(crate) fn parse_abi_err_data(err: &str) -> String { - let start_index = "Contract call reverted with data: 0x".len(); - let method_signature_len = "08c379a0".len(); - - let method_signature = &err[start_index..(start_index + method_signature_len)]; - let hex_error_message = &err[(start_index + method_signature_len)..]; - - let hex_error: Vec = match FromHex::from_hex(hex_error_message) { - Ok(hex_error) => hex_error, - Err(err) => { - return format!( - "parse_abi_err_data from_hex: {:?}, data: {}!", - err, hex_error_message - ); - } - }; - - let error_type = AbiErrorType::from_signature(method_signature); - if let Some(error_abi) = error_type.param_type() { - let decoded = decode(&[error_abi], &hex_error); - match decoded { - Ok(decoded) => { - let strings: Vec = - decoded.into_iter().map(|token| token.to_string()).collect(); - let result = strings.join(";"); - if error_type == AbiErrorType::Panic { - let panic_error = parse_panic_error(&result); - handle_panic_error(panic_error) - } else { - result - } + ), + PanicError::DivisionByZero => { + write!( + f, + "Panic code: 0x{:x}, Division or modulo by zero", + error as u16 + ) + } + PanicError::InvalidEnumConversion => { + write!( + f, + "Panic code: 0x{:x}, Invalid enum conversion", + error as u16 + ) + } + PanicError::InvalidEncoding => { + write!(f, "Panic code: 0x{:x}, Invalid encoding", error as u16) } - Err(err) => { - format!( - "parse_abi_err_data decode: {:?}, data: {:x?}!", - err, hex_error + PanicError::EmptyArrayPop => write!( + f, + "Panic code: 0x{:x}, Attempted to pop an empty array", + error as u16 + ), + PanicError::OutOfBoundsAccess => { + write!(f, "Panic code: 0x{:x}, Out-of-bounds access", error as u16) + } + PanicError::ExcessiveAllocation => write!( + f, + "Panic code: 0x{:x}, Excessive memory allocation", + error as u16 + ), + PanicError::UninitializedInternalFunction => { + write!( + f, + "Panic code: 0x{:x}, Called an uninitialized internal function", + error as u16 ) } + PanicError::Unknown => write!(f, "Panic code: 0x{:x}, Unknown panic", error as u16), } - } else { - format!( - "parse_abi_err_data: unknown exception method {}!", - method_signature - ) } } #[cfg(test)] mod test { - use crate::chain::axon::eth_err::parse_abi_err_data; + use ethers::contract::EthError; + + use super::Panic; + + fn parse_abi_err_data(err: &str) -> String { + let revert_data = hex::decode( + err.strip_prefix("Contract call reverted with data: 0x") + .unwrap(), + ) + .unwrap(); + if let Some(p) = Panic::decode_with_selector(&revert_data) { + p.to_string() + } else if let Some(s) = String::decode_with_selector(&revert_data) { + s + } else { + panic!("failed to decode") + } + } #[test] fn test_sol_revert() { @@ -167,28 +145,4 @@ mod test { let err = parse_abi_err_data(err_string); assert_eq!(err, "Panic code: 0x12, Division or modulo by zero"); } - - #[test] - fn test_sol_unknown_method() { - let err_string = "Contract call reverted with data: 0x18c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; - let err = parse_abi_err_data(err_string); - assert_eq!( - err, - "parse_abi_err_data: unknown exception method 18c379a0!" - ); - } - - #[test] - fn test_sol_invalid_hex() { - let err_string = "Contract call reverted with data: 0x08c379a00x00000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; - let err = parse_abi_err_data(err_string); - assert_eq!(err, "parse_abi_err_data from_hex: InvalidHexCharacter { c: 'x', index: 1 }, data: 0x00000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000!"); - } - - #[test] - fn test_sol_invalid_data() { - let err_string = "Contract call reverted with data: 0x08c379a00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001c74657374206661696c656420746f2063726561746520636c69656e7400000000"; - let err = parse_abi_err_data(err_string); - assert_eq!(err, "parse_abi_err_data decode: InvalidData, data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1c, 74, 65, 73, 74, 20, 66, 61, 69, 6c, 65, 64, 20, 74, 6f, 20, 63, 72, 65, 61, 74, 65, 20, 63, 6c, 69, 65, 6e, 74, 0, 0, 0, 0]!"); - } } diff --git a/crates/relayer/src/chain/axon/utils.rs b/crates/relayer/src/chain/axon/utils.rs index 1796d6b4..05adb88b 100644 --- a/crates/relayer/src/chain/axon/utils.rs +++ b/crates/relayer/src/chain/axon/utils.rs @@ -3,14 +3,14 @@ use std::str::FromStr; use axon_tools::types::{Block as AxonBlock, Proof as AxonProof, ValidatorExtend}; use crate::{ - chain::{axon::eth_err::parse_abi_err_data, SEC_TO_NANO}, + chain::{axon::eth_err::Panic, SEC_TO_NANO}, client_state::{AnyClientState, IdentifiedAnyClientState}, consensus_state::AnyConsensusState, error::Error, event::IbcEventWithHeight, ibc_contract::OwnableIBCHandlerEvents, }; -use ethers::types::H256; +use ethers::{contract::ContractError, providers::Middleware, types::H256}; use ibc_relayer_types::{ clients::{ ics07_axon::{client_state::AxonClientState, consensus_state::AxonConsensusState}, @@ -29,8 +29,17 @@ pub fn convert_err(err: T) -> Error { Error::other_error(err.to_string()) } -pub fn convert_abi_err(err: T) -> Error { - Error::other_error(parse_abi_err_data(&err.to_string())) +pub fn decode_revert_error(err: ContractError) -> eyre::Report +where + M: Middleware + 'static, +{ + if let Some(r) = err.decode_revert::() { + eyre::eyre!("Contract call reverted: {r}") + } else if let Some(p) = err.as_revert().and_then(|d| Panic::decode_with_selector(d)) { + eyre::eyre!("Contract call reverted: {p}") + } else { + err.into() + } } pub fn to_identified_any_client_state( From 0d3b125109635ad492874d53becf56fcaa8034c2 Mon Sep 17 00:00:00 2001 From: Yin Guanhao Date: Tue, 5 Dec 2023 12:44:27 +0800 Subject: [PATCH 3/4] Refactor panic code display, don't lose unknown code --- crates/relayer/src/chain/axon/eth_err.rs | 69 ++++++------------------ 1 file changed, 15 insertions(+), 54 deletions(-) diff --git a/crates/relayer/src/chain/axon/eth_err.rs b/crates/relayer/src/chain/axon/eth_err.rs index 18549c3d..dbc4a3f3 100644 --- a/crates/relayer/src/chain/axon/eth_err.rs +++ b/crates/relayer/src/chain/axon/eth_err.rs @@ -15,11 +15,10 @@ impl Panic { impl std::fmt::Display for Panic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let e = PanicError::from_code(self.0.low_u32()); - write!(f, "{e}") + write!(f, "Panic code: {:#x}, {e}", self.0) } } -#[derive(Copy, Clone)] enum PanicError { Generic, AssertFailed, @@ -54,60 +53,22 @@ impl PanicError { impl std::fmt::Display for PanicError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let error = *self; - match error { - PanicError::Generic => write!( - f, - "Panic code: 0x{:x}, Generic compiler inserted panic", - error as u16 - ), - PanicError::AssertFailed => { - write!(f, "Panic code: 0x{:x}, Assertion failed", error as u16) - } - PanicError::ArithmeticOverflow => write!( - f, - "Panic code: 0x{:x}, Arithmetic operation resulted in overflow", - error as u16 - ), - PanicError::DivisionByZero => { - write!( - f, - "Panic code: 0x{:x}, Division or modulo by zero", - error as u16 - ) - } - PanicError::InvalidEnumConversion => { - write!( - f, - "Panic code: 0x{:x}, Invalid enum conversion", - error as u16 - ) - } - PanicError::InvalidEncoding => { - write!(f, "Panic code: 0x{:x}, Invalid encoding", error as u16) - } - PanicError::EmptyArrayPop => write!( - f, - "Panic code: 0x{:x}, Attempted to pop an empty array", - error as u16 - ), - PanicError::OutOfBoundsAccess => { - write!(f, "Panic code: 0x{:x}, Out-of-bounds access", error as u16) - } - PanicError::ExcessiveAllocation => write!( - f, - "Panic code: 0x{:x}, Excessive memory allocation", - error as u16 - ), + let desc = match self { + PanicError::Generic => "Generic compiler inserted panic", + PanicError::AssertFailed => "Assertion failed", + PanicError::ArithmeticOverflow => "Arithmetic operation resulted in overflow", + PanicError::DivisionByZero => "Division or modulo by zero", + PanicError::InvalidEnumConversion => "Invalid enum conversion", + PanicError::InvalidEncoding => "Invalid encoding", + PanicError::EmptyArrayPop => "Attempted to pop an empty array", + PanicError::OutOfBoundsAccess => "Out-of-bounds access", + PanicError::ExcessiveAllocation => "Excessive memory allocation", PanicError::UninitializedInternalFunction => { - write!( - f, - "Panic code: 0x{:x}, Called an uninitialized internal function", - error as u16 - ) + "Called an uninitialized internal function" } - PanicError::Unknown => write!(f, "Panic code: 0x{:x}, Unknown panic", error as u16), - } + PanicError::Unknown => "Unknown panic", + }; + write!(f, "{desc}") } } From 5d01c7b035a2a240d0d5cb8875611ce404e5faa1 Mon Sep 17 00:00:00 2001 From: Yin Guanhao Date: Tue, 5 Dec 2023 13:04:35 +0800 Subject: [PATCH 4/4] Use derived decode and abi/selector for Panic --- crates/relayer/src/chain/axon/eth_err.rs | 21 ++++++++------------- crates/relayer/src/chain/axon/utils.rs | 4 ++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/crates/relayer/src/chain/axon/eth_err.rs b/crates/relayer/src/chain/axon/eth_err.rs index dbc4a3f3..140d2c9a 100644 --- a/crates/relayer/src/chain/axon/eth_err.rs +++ b/crates/relayer/src/chain/axon/eth_err.rs @@ -1,17 +1,12 @@ -use ethers::abi::{Detokenize, ParamType, Uint}; +use ethers::abi::Uint; +use ethers::contract::EthCall; -/// Panic(uint) +/// For decoding and displaying `Panic(uint256)` errors. +/// +/// EthError derived decode_with_selector is buggy, so we derive `EthCall` instead. Decode with `AbiDecode::decode`. +#[derive(EthCall)] pub struct Panic(Uint); -impl Panic { - // Can't get the right selector with `derive(EthError)`, so I implement this manually. - pub fn decode_with_selector(bytes: &[u8]) -> Option { - let bytes = bytes.strip_prefix(b"\x4e\x48\x7b\x71")?; - let tokens = ethers::abi::decode(&[ParamType::Uint(32)], bytes).ok()?; - Some(Panic(Uint::from_tokens(tokens).ok()?)) - } -} - impl std::fmt::Display for Panic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let e = PanicError::from_code(self.0.low_u32()); @@ -74,7 +69,7 @@ impl std::fmt::Display for PanicError { #[cfg(test)] mod test { - use ethers::contract::EthError; + use ethers::{abi::AbiDecode, contract::EthError}; use super::Panic; @@ -84,7 +79,7 @@ mod test { .unwrap(), ) .unwrap(); - if let Some(p) = Panic::decode_with_selector(&revert_data) { + if let Ok(p) = Panic::decode(&revert_data) { p.to_string() } else if let Some(s) = String::decode_with_selector(&revert_data) { s diff --git a/crates/relayer/src/chain/axon/utils.rs b/crates/relayer/src/chain/axon/utils.rs index 05adb88b..bdac2b7b 100644 --- a/crates/relayer/src/chain/axon/utils.rs +++ b/crates/relayer/src/chain/axon/utils.rs @@ -10,7 +10,7 @@ use crate::{ event::IbcEventWithHeight, ibc_contract::OwnableIBCHandlerEvents, }; -use ethers::{contract::ContractError, providers::Middleware, types::H256}; +use ethers::{abi::AbiDecode, contract::ContractError, providers::Middleware, types::H256}; use ibc_relayer_types::{ clients::{ ics07_axon::{client_state::AxonClientState, consensus_state::AxonConsensusState}, @@ -35,7 +35,7 @@ where { if let Some(r) = err.decode_revert::() { eyre::eyre!("Contract call reverted: {r}") - } else if let Some(p) = err.as_revert().and_then(|d| Panic::decode_with_selector(d)) { + } else if let Some(p) = err.as_revert().and_then(|d| Panic::decode(d).ok()) { eyre::eyre!("Contract call reverted: {p}") } else { err.into()