Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: abi err msg parse #388

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/relayer/src/chain/axon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ use super::{
use tokio::runtime::Runtime as TokioRuntime;

pub mod contract;
mod eth_err;
mod monitor;
mod msg;
mod rpc;
Expand Down Expand Up @@ -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::*;
Expand Down
194 changes: 194 additions & 0 deletions crates/relayer/src/chain/axon/eth_err.rs
Original file line number Diff line number Diff line change
@@ -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<ParamType> {
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)];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May panic if err is not long enough.

let hex_error_message = &err[(start_index + method_signature_len)..];

let hex_error: Vec<u8> = 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<String> =
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be necessary to convert the token to hex again.

} 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!"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data should be retained when we cannot decode it.

);
}

#[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]!");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data should be retained (in hex) when we cannot decode it.

}
}
6 changes: 5 additions & 1 deletion crates/relayer/src/chain/axon/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -29,6 +29,10 @@ pub fn convert_err<T: ToString>(err: T) -> Error {
Error::other_error(err.to_string())
}

pub fn convert_abi_err<T: ToString>(err: T) -> Error {
Error::other_error(parse_abi_err_data(&err.to_string()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error may not be a call revert error

}

pub fn to_identified_any_client_state(
client_state: &ethers::core::types::Bytes,
) -> Result<IdentifiedAnyClientState, Error> {
Expand Down
Loading