Skip to content

Commit b3f873d

Browse files
authored
feat: fvm-bench: decode evm revert messages (#1874)
1 parent fc56bb2 commit b3f873d

File tree

3 files changed

+173
-5
lines changed

3 files changed

+173
-5
lines changed
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033
1+
608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610795806100656000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637bd703e81461005157806390b98a1114610081578063c0626aa2146100b1578063f8b2cb4f146100e1575b600080fd5b61006b600480360381019061006691906103eb565b610111565b6040516100789190610431565b60405180910390f35b61009b60048036038101906100969190610478565b61012f565b6040516100a891906104d3565b60405180910390f35b6100cb60048036038101906100c691906104ee565b61029a565b6040516100d891906104d3565b60405180910390f35b6100fb60048036038101906100f691906103eb565b610340565b6040516101089190610431565b60405180910390f35b6000600261011e83610340565b610128919061054a565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101805760009050610294565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101ce919061058c565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461022391906105c0565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516102879190610431565b60405180910390a3600190505b92915050565b6000600182116102ad576102ac6105f4565b5b60058210156102f45760026040517f442666110000000000000000000000000000000000000000000000000000000081526004016102eb91906106c5565b60405180910390fd5b600a8211610337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161032e9061073f565b60405180910390fd5b60019050919050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103b88261038d565b9050919050565b6103c8816103ad565b81146103d357600080fd5b50565b6000813590506103e5816103bf565b92915050565b60006020828403121561040157610400610388565b5b600061040f848285016103d6565b91505092915050565b6000819050919050565b61042b81610418565b82525050565b60006020820190506104466000830184610422565b92915050565b61045581610418565b811461046057600080fd5b50565b6000813590506104728161044c565b92915050565b6000806040838503121561048f5761048e610388565b5b600061049d858286016103d6565b92505060206104ae85828601610463565b9150509250929050565b60008115159050919050565b6104cd816104b8565b82525050565b60006020820190506104e860008301846104c4565b92915050565b60006020828403121561050457610503610388565b5b600061051284828501610463565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061055582610418565b915061056083610418565b925082820261056e81610418565b915082820484148315176105855761058461051b565b5b5092915050565b600061059782610418565b91506105a283610418565b92508282039050818111156105ba576105b961051b565b5b92915050565b60006105cb82610418565b91506105d683610418565b92508282019050808211156105ee576105ed61051b565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600082825260208201905092915050565b7f4c657373207468616e2066697665000000000000000000000000000000000000600082015250565b600061066a600e83610623565b915061067582610634565b602082019050919050565b6000819050919050565b6000819050919050565b60006106af6106aa6106a584610680565b61068a565b610418565b9050919050565b6106bf81610694565b82525050565b600060408201905081810360008301526106de8161065d565b90506106ed60208301846106b6565b92915050565b7f4c657373205468616e2074656e00000000000000000000000000000000000000600082015250565b6000610729600d83610623565b9150610734826106f3565b602082019050919050565b600060208201905081810360008301526107588161071c565b905091905056fea2646970667358221220a6ed6e1ad429eb3ff6712942fd81d137f4187d47310e3bbddc2af76ee5c585ba64736f6c63430008120033

tools/contracts/benchmarks/SimpleCoin.sol

+13-4
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,30 @@ contract SimpleCoin {
66

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

9+
error lessThanFive(string err, uint256 code);
10+
911
constructor() {
1012
balances[tx.origin] = 10000;
1113
}
1214

13-
function sendCoin(address receiver, uint256 amount)
14-
public
15-
returns (bool sufficient)
16-
{
15+
function sendCoin(
16+
address receiver,
17+
uint256 amount
18+
) public returns (bool sufficient) {
1719
if (balances[msg.sender] < amount) return false;
1820
balances[msg.sender] -= amount;
1921
balances[receiver] += amount;
2022
emit Transfer(msg.sender, receiver, amount);
2123
return true;
2224
}
2325

26+
function greaterThanTen(uint256 num) public pure returns (bool) {
27+
assert(num > 1);
28+
if (num < 5) revert lessThanFive("Less than five", 2);
29+
require(num > 10, "Less Than ten");
30+
return true;
31+
}
32+
2433
function getBalanceInEth(address addr) public view returns (uint256) {
2534
return getBalance(addr) * 2;
2635
}

tools/fvm-bench/src/fevm.rs

+159
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ use fvm_integration_tests::{tester, testkit};
77
use fvm_ipld_encoding::BytesDe;
88
use fvm_shared::address::Address;
99

10+
// Eth ABI (solidity) panic codes.
11+
const PANIC_ERROR_CODES: [(u64, &str); 10] = [
12+
(0x00, "Panic()"),
13+
(0x01, "Assert()"),
14+
(0x11, "ArithmeticOverflow()"),
15+
(0x12, "DivideByZero()"),
16+
(0x21, "InvalidEnumVariant()"),
17+
(0x22, "InvalidStorageArray()"),
18+
(0x31, "PopEmptyArray()"),
19+
(0x32, "ArrayIndexOutOfBounds()"),
20+
(0x41, "OutOfMemory()"),
21+
(0x51, "CalledUninitializedFunction()"),
22+
];
23+
24+
// Function Selectors
25+
const ERROR_FUNCTION_SELECTOR: &[u8] = b"\x08\xc3\x79\xa0"; // Error(string)
26+
const PANIC_FUNCTION_SELECTOR: &[u8] = b"\x4e\x48\x7b\x71"; // Panic(uint256)
27+
1028
fn handle_result(tester: &tester::BasicTester, name: &str, res: &ApplyRet) -> anyhow::Result<()> {
1129
let (trace, events) = tester
1230
.options
@@ -52,6 +70,10 @@ fn handle_result(tester: &tester::BasicTester, name: &str, res: &ApplyRet) -> an
5270
if res.msg_receipt.exit_code.is_success() {
5371
Ok(())
5472
} else {
73+
if res.msg_receipt.exit_code == 33.into() {
74+
let BytesDe(returnval) = res.msg_receipt.return_data.deserialize().unwrap();
75+
println!("Revert Reason: {}", parse_eth_revert(&returnval).unwrap());
76+
}
5577
Err(anyhow!("{name} failed"))
5678
}
5779
}
@@ -85,3 +107,140 @@ pub fn run(
85107

86108
handle_result(tester, "contract invocation", &invoke_res)
87109
}
110+
111+
// Parses the error message from a revert reason of type Error(string) or Panic(uint256)
112+
// See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require
113+
pub fn parse_eth_revert(returnval: &Vec<u8>) -> anyhow::Result<String> {
114+
if returnval.is_empty() {
115+
return Err(anyhow!("invalid return value"));
116+
}
117+
if returnval.len() < 4 + 32 {
118+
return Ok(hex::encode(returnval));
119+
}
120+
match &returnval[0..4] {
121+
PANIC_FUNCTION_SELECTOR => {
122+
let cbytes = &returnval[4..];
123+
match bytes_to_u64(&cbytes[..32]) {
124+
Ok(panic_code) => {
125+
let error = panic_error_codes(panic_code);
126+
match error {
127+
Some(s) => return Ok(format!("Panic Code: {}, msg: {}", s.0, s.1)),
128+
None => return Err(anyhow!("Returned with panic code({})", panic_code)),
129+
}
130+
}
131+
Err(_) => {
132+
return Ok(hex::encode(returnval));
133+
}
134+
}
135+
}
136+
ERROR_FUNCTION_SELECTOR => {
137+
let cbytes = &returnval[4..];
138+
let cbytes_len = cbytes.len() as u64;
139+
if let Ok(offset) = bytes_to_u64(&cbytes[0..32]) {
140+
if cbytes_len >= offset + 32 {
141+
if let Ok(length) = bytes_to_u64(&cbytes[offset as usize..offset as usize + 32])
142+
{
143+
if cbytes_len >= offset + 32 + length {
144+
let msg = String::from_utf8_lossy(
145+
&cbytes
146+
[offset as usize + 32..offset as usize + 32 + length as usize],
147+
);
148+
return Ok(msg.to_string());
149+
}
150+
}
151+
}
152+
}
153+
}
154+
_ => return Ok(hex::encode(returnval)),
155+
};
156+
Ok(hex::encode(returnval))
157+
}
158+
159+
// Converts a byte slice to a u64
160+
fn bytes_to_u64(bytes: &[u8]) -> Result<u64, anyhow::Error> {
161+
if bytes.len() != 32 {
162+
return Err(anyhow::anyhow!("Invalid byte slice length"));
163+
}
164+
let mut buf = [0u8; 8];
165+
buf.copy_from_slice(&bytes[24..32]);
166+
Ok(u64::from_be_bytes(buf))
167+
}
168+
169+
// Returns the panic code and message for a given panic code
170+
fn panic_error_codes(code: u64) -> Option<&'static (u64, &'static str)> {
171+
PANIC_ERROR_CODES.iter().find(|(c, _)| *c == code)
172+
}
173+
174+
//////////////////////
175+
/////// Tests ///////
176+
/////////////////////
177+
178+
#[cfg(test)]
179+
mod tests {
180+
use super::*;
181+
182+
#[test]
183+
fn test_parse_eth_revert_empty_returnval() {
184+
let returnval = vec![];
185+
let result = parse_eth_revert(&returnval);
186+
assert!(result.is_err());
187+
assert_eq!(result.unwrap_err().to_string(), "invalid return value");
188+
}
189+
190+
#[test]
191+
fn test_parse_eth_revert_short_returnval() {
192+
let returnval = vec![0x01, 0x02, 0x03];
193+
let result = parse_eth_revert(&returnval);
194+
assert!(result.is_ok());
195+
assert_eq!(result.unwrap(), "010203");
196+
}
197+
198+
#[test]
199+
fn test_parse_eth_revert_panic_function_selector() {
200+
let returnval = vec![
201+
0x4e, 0x48, 0x7b, 0x71, // function selector for "Panic(uint256)"
202+
0x00, 0x00, 0x00, 0x00,
203+
];
204+
let result = parse_eth_revert(&returnval);
205+
assert!(result.is_ok());
206+
assert_eq!(result.unwrap(), "4e487b7100000000");
207+
}
208+
209+
#[test]
210+
fn test_parse_eth_revert_panic_function_selector_with_message() {
211+
// assert error from simplecoin contract
212+
let returnval =
213+
hex::decode("4e487b710000000000000000000000000000000000000000000000000000000000000001")
214+
.unwrap();
215+
let result = parse_eth_revert(&returnval);
216+
assert!(result.is_ok());
217+
assert_eq!(result.unwrap(), "Panic Code: 1, msg: Assert()");
218+
}
219+
220+
#[test]
221+
fn test_parse_eth_revert_error_function_selector() {
222+
// "Less Than ten" error from simplecoin contract
223+
let returnval = hex::decode("08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d4c657373205468616e2074656e00000000000000000000000000000000000000").unwrap();
224+
let result = parse_eth_revert(&returnval);
225+
assert!(result.is_ok());
226+
assert_eq!(result.unwrap(), "Less Than ten");
227+
}
228+
229+
#[test]
230+
fn test_parse_eth_revert_error_function_selector_invalid_data() {
231+
// invalid data for error function selector
232+
let returnval = hex::decode("08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000").unwrap();
233+
let result = parse_eth_revert(&returnval);
234+
assert!(result.is_ok());
235+
assert_eq!(result.unwrap(), hex::encode(&returnval));
236+
}
237+
238+
#[test]
239+
fn test_parse_eth_revert_custom_error() {
240+
// any other data like custom error, etc. "lessThanFive" custom error of simplecoin contract in this case.
241+
let returnval = hex::decode("4426661100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4c657373207468616e2066697665000000000000000000000000000000000000").unwrap();
242+
let result = parse_eth_revert(&returnval);
243+
assert!(result.is_ok());
244+
assert_eq!(result.unwrap(), hex::encode(&returnval));
245+
}
246+
}

0 commit comments

Comments
 (0)