Skip to content

Commit

Permalink
feat: fvm-bench: decode evm revert messages (#1874)
Browse files Browse the repository at this point in the history
  • Loading branch information
lordshashank authored Sep 12, 2023
1 parent fc56bb2 commit b3f873d
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 5 deletions.
2 changes: 1 addition & 1 deletion tools/contracts/benchmarks/SimpleCoin.bin
Original file line number Diff line number Diff line change
@@ -1 +1 @@
608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033
608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610795806100656000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637bd703e81461005157806390b98a1114610081578063c0626aa2146100b1578063f8b2cb4f146100e1575b600080fd5b61006b600480360381019061006691906103eb565b610111565b6040516100789190610431565b60405180910390f35b61009b60048036038101906100969190610478565b61012f565b6040516100a891906104d3565b60405180910390f35b6100cb60048036038101906100c691906104ee565b61029a565b6040516100d891906104d3565b60405180910390f35b6100fb60048036038101906100f691906103eb565b610340565b6040516101089190610431565b60405180910390f35b6000600261011e83610340565b610128919061054a565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101805760009050610294565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101ce919061058c565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461022391906105c0565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516102879190610431565b60405180910390a3600190505b92915050565b6000600182116102ad576102ac6105f4565b5b60058210156102f45760026040517f442666110000000000000000000000000000000000000000000000000000000081526004016102eb91906106c5565b60405180910390fd5b600a8211610337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161032e9061073f565b60405180910390fd5b60019050919050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103b88261038d565b9050919050565b6103c8816103ad565b81146103d357600080fd5b50565b6000813590506103e5816103bf565b92915050565b60006020828403121561040157610400610388565b5b600061040f848285016103d6565b91505092915050565b6000819050919050565b61042b81610418565b82525050565b60006020820190506104466000830184610422565b92915050565b61045581610418565b811461046057600080fd5b50565b6000813590506104728161044c565b92915050565b6000806040838503121561048f5761048e610388565b5b600061049d858286016103d6565b92505060206104ae85828601610463565b9150509250929050565b60008115159050919050565b6104cd816104b8565b82525050565b60006020820190506104e860008301846104c4565b92915050565b60006020828403121561050457610503610388565b5b600061051284828501610463565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061055582610418565b915061056083610418565b925082820261056e81610418565b915082820484148315176105855761058461051b565b5b5092915050565b600061059782610418565b91506105a283610418565b92508282039050818111156105ba576105b961051b565b5b92915050565b60006105cb82610418565b91506105d683610418565b92508282019050808211156105ee576105ed61051b565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600082825260208201905092915050565b7f4c657373207468616e2066697665000000000000000000000000000000000000600082015250565b600061066a600e83610623565b915061067582610634565b602082019050919050565b6000819050919050565b6000819050919050565b60006106af6106aa6106a584610680565b61068a565b610418565b9050919050565b6106bf81610694565b82525050565b600060408201905081810360008301526106de8161065d565b90506106ed60208301846106b6565b92915050565b7f4c657373205468616e2074656e00000000000000000000000000000000000000600082015250565b6000610729600d83610623565b9150610734826106f3565b602082019050919050565b600060208201905081810360008301526107588161071c565b905091905056fea2646970667358221220a6ed6e1ad429eb3ff6712942fd81d137f4187d47310e3bbddc2af76ee5c585ba64736f6c63430008120033
17 changes: 13 additions & 4 deletions tools/contracts/benchmarks/SimpleCoin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,30 @@ contract SimpleCoin {

event Transfer(address indexed _from, address indexed _to, uint256 _value);

error lessThanFive(string err, uint256 code);

constructor() {
balances[tx.origin] = 10000;
}

function sendCoin(address receiver, uint256 amount)
public
returns (bool sufficient)
{
function sendCoin(
address receiver,
uint256 amount
) public returns (bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Transfer(msg.sender, receiver, amount);
return true;
}

function greaterThanTen(uint256 num) public pure returns (bool) {
assert(num > 1);
if (num < 5) revert lessThanFive("Less than five", 2);
require(num > 10, "Less Than ten");
return true;
}

function getBalanceInEth(address addr) public view returns (uint256) {
return getBalance(addr) * 2;
}
Expand Down
159 changes: 159 additions & 0 deletions tools/fvm-bench/src/fevm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ use fvm_integration_tests::{tester, testkit};
use fvm_ipld_encoding::BytesDe;
use fvm_shared::address::Address;

// Eth ABI (solidity) panic codes.
const PANIC_ERROR_CODES: [(u64, &str); 10] = [
(0x00, "Panic()"),
(0x01, "Assert()"),
(0x11, "ArithmeticOverflow()"),
(0x12, "DivideByZero()"),
(0x21, "InvalidEnumVariant()"),
(0x22, "InvalidStorageArray()"),
(0x31, "PopEmptyArray()"),
(0x32, "ArrayIndexOutOfBounds()"),
(0x41, "OutOfMemory()"),
(0x51, "CalledUninitializedFunction()"),
];

// Function Selectors
const ERROR_FUNCTION_SELECTOR: &[u8] = b"\x08\xc3\x79\xa0"; // Error(string)
const PANIC_FUNCTION_SELECTOR: &[u8] = b"\x4e\x48\x7b\x71"; // Panic(uint256)

fn handle_result(tester: &tester::BasicTester, name: &str, res: &ApplyRet) -> anyhow::Result<()> {
let (trace, events) = tester
.options
Expand Down Expand Up @@ -52,6 +70,10 @@ fn handle_result(tester: &tester::BasicTester, name: &str, res: &ApplyRet) -> an
if res.msg_receipt.exit_code.is_success() {
Ok(())
} else {
if res.msg_receipt.exit_code == 33.into() {
let BytesDe(returnval) = res.msg_receipt.return_data.deserialize().unwrap();
println!("Revert Reason: {}", parse_eth_revert(&returnval).unwrap());
}
Err(anyhow!("{name} failed"))
}
}
Expand Down Expand Up @@ -85,3 +107,140 @@ pub fn run(

handle_result(tester, "contract invocation", &invoke_res)
}

// Parses the error message from a revert reason of type Error(string) or Panic(uint256)
// See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require
pub fn parse_eth_revert(returnval: &Vec<u8>) -> anyhow::Result<String> {
if returnval.is_empty() {
return Err(anyhow!("invalid return value"));
}
if returnval.len() < 4 + 32 {
return Ok(hex::encode(returnval));
}
match &returnval[0..4] {
PANIC_FUNCTION_SELECTOR => {
let cbytes = &returnval[4..];
match bytes_to_u64(&cbytes[..32]) {
Ok(panic_code) => {
let error = panic_error_codes(panic_code);
match error {
Some(s) => return Ok(format!("Panic Code: {}, msg: {}", s.0, s.1)),
None => return Err(anyhow!("Returned with panic code({})", panic_code)),
}
}
Err(_) => {
return Ok(hex::encode(returnval));
}
}
}
ERROR_FUNCTION_SELECTOR => {
let cbytes = &returnval[4..];
let cbytes_len = cbytes.len() as u64;
if let Ok(offset) = bytes_to_u64(&cbytes[0..32]) {
if cbytes_len >= offset + 32 {
if let Ok(length) = bytes_to_u64(&cbytes[offset as usize..offset as usize + 32])
{
if cbytes_len >= offset + 32 + length {
let msg = String::from_utf8_lossy(
&cbytes
[offset as usize + 32..offset as usize + 32 + length as usize],
);
return Ok(msg.to_string());
}
}
}
}
}
_ => return Ok(hex::encode(returnval)),
};
Ok(hex::encode(returnval))
}

// Converts a byte slice to a u64
fn bytes_to_u64(bytes: &[u8]) -> Result<u64, anyhow::Error> {
if bytes.len() != 32 {
return Err(anyhow::anyhow!("Invalid byte slice length"));
}
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[24..32]);
Ok(u64::from_be_bytes(buf))
}

// Returns the panic code and message for a given panic code
fn panic_error_codes(code: u64) -> Option<&'static (u64, &'static str)> {
PANIC_ERROR_CODES.iter().find(|(c, _)| *c == code)
}

//////////////////////
/////// Tests ///////
/////////////////////

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_eth_revert_empty_returnval() {
let returnval = vec![];
let result = parse_eth_revert(&returnval);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "invalid return value");
}

#[test]
fn test_parse_eth_revert_short_returnval() {
let returnval = vec![0x01, 0x02, 0x03];
let result = parse_eth_revert(&returnval);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "010203");
}

#[test]
fn test_parse_eth_revert_panic_function_selector() {
let returnval = vec![
0x4e, 0x48, 0x7b, 0x71, // function selector for "Panic(uint256)"
0x00, 0x00, 0x00, 0x00,
];
let result = parse_eth_revert(&returnval);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "4e487b7100000000");
}

#[test]
fn test_parse_eth_revert_panic_function_selector_with_message() {
// assert error from simplecoin contract
let returnval =
hex::decode("4e487b710000000000000000000000000000000000000000000000000000000000000001")
.unwrap();
let result = parse_eth_revert(&returnval);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Panic Code: 1, msg: Assert()");
}

#[test]
fn test_parse_eth_revert_error_function_selector() {
// "Less Than ten" error from simplecoin contract
let returnval = hex::decode("08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d4c657373205468616e2074656e00000000000000000000000000000000000000").unwrap();
let result = parse_eth_revert(&returnval);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Less Than ten");
}

#[test]
fn test_parse_eth_revert_error_function_selector_invalid_data() {
// invalid data for error function selector
let returnval = hex::decode("08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000").unwrap();
let result = parse_eth_revert(&returnval);
assert!(result.is_ok());
assert_eq!(result.unwrap(), hex::encode(&returnval));
}

#[test]
fn test_parse_eth_revert_custom_error() {
// any other data like custom error, etc. "lessThanFive" custom error of simplecoin contract in this case.
let returnval = hex::decode("4426661100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4c657373207468616e2066697665000000000000000000000000000000000000").unwrap();
let result = parse_eth_revert(&returnval);
assert!(result.is_ok());
assert_eq!(result.unwrap(), hex::encode(&returnval));
}
}

0 comments on commit b3f873d

Please sign in to comment.