consensus-shipyard / ipc

🌳 Spawn multi-level trees of customized, scalable, EVM-compatible networks with IPC. L2++ powered by FVM, Wasm, libp2p, IPFS/IPLD, and CometBFT.
https://ipc.space
Apache License 2.0
40 stars 33 forks source link

ipc-cli: on transaction/call failure, print out the revert reason and attempt to match it against known ABI errors #944

Open maciejwitowski opened 3 months ago

maciejwitowski commented 3 months ago

From @raulk

I would approach it this way: First, I would focus on printing the JSON-RPC error as it comes through. Right now we're swalling the JSON-RPC error string, and just printing "Contract call reverted with data". Instead, if we dump the error string the user would see the same thing that cast prints out [1].

Whatever you do here must be deep enough that it automatically applies to all JSON-RPC calls we make.

This would be a first PR. Next, I would attempt to extract the revert reason from the error string and match it against all ABIs we have. Unfortunately, the errors are split across a number of contract bindings.

For example, errors for the GatewayManagerFacet are in this generated file: ipc/contracts/binding/src/gateway_manager_facet.rs:754, under enum GatewayManagerFacetErrors.

You can use the decode() method to decode from bytes into a higher type.

It's extremely impractical to check against all error structs of all contracts, so I think this will require some codegen logic in ipc/contracts/binding/build.rs + some macros.

I think this is too complex (especially because you're only getting started with the Rust side of things) and it'll probably be a timesink at the moment.

Let's punt it to later.

[1]:

⟩ cast call 0x6d25fbfac9e6215e03c687e54f7c74f489949eaf 0x0517e1aa000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000000004cb2f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000011877f702a73aa7c5c9391aeb450f3c69ea4db7c0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014021cff4f552a9a57bb040b723b53aa257f9b231a000000000000000000000000 --rpc-url https://api.calibration.node.glif.io --from 0xbe492100ab71e51e99a80ae5adc0e00f7e2e256c
Error:
(code: 1, message: message execution failed: exit 33, revert reason: 0xe991abd0, vm error: message failed with backtrace:
00: f088265 (method 3844450837) -- contract reverted (33)
01: f088265 (method 6) -- contract reverted (33)
 (RetCode=33), data: None)
maciejwitowski commented 3 months ago

Notes from @snissn

Part One

According to this open ticket on the javascript (not rust) ethers repo there is a long standing issue wth error messages not properly propagating: https://github.com/ethers-io/ethers.js/discussions/2849

I've sanity checked that the rust ethers ipc code is doing a reasonable job at using the ethers library to reproduce error messages. Here is a code snippet I added to the subnet join command that still only prints out Simulation failed, transaction would fail with error: Revert(Bytes(0x))

let result = txn.call().await;
match result {
        Ok(response) => {
                println!("Simulation succeeded, transaction would succeed with output: {:?}", response);
        },
        Err(e) => {
                println!("Simulation failed, transaction would fail with error: {:?}", e);
        }
}

ethers rust has a deprecation warning that indicates to use alloy or foundry: https://github.com/gakonst/ethers-rs/issues/2667

Part Two When we call

ipc-cli subnet join --subnet /r314159/t410f3kfub4nv2n6ixbwo37t523upt77l4ezy2altnpy  --collateral 1 --public-key b210a01080a99b57fb3ce1a9c028de45aa5685e8 

and there is a transaction failure the ethers-rs error handling library ends up losing track of the error message when it calls error.into() in this line:

https://github.com/gakonst/ethers-rs/blob/0644a651fa9d099a39269195b2572a049c65f190/ethers-providers/src/rpc/transports/http.rs#L101C1-L101C75

if i make the following diff on the line that loses the error message I correctly get the error message we are expecting:

-            Ok(Response::Error { error, .. }) => return Err(error.into()),
+            Ok(Response::Error { error, .. }) =>{
+                return Err(ClientError::SerdeJson {
+                    err: serde::de::Error::custom("provider error"),
+                    text: error.to_string()
+                })
+            } 

outputs the following:

[2024-05-02T23:51:38Z ERROR ipc_cli] main process failed: error processing command Some(Subnet(SubnetCommandsArgs { 
    command: Join(
        JoinSubnetArgs { 
            from: None, 
            subnet: "/r314159/t410f3kfub4nv2n6ixbwo37t523upt77l4ezy2altnpy", 
            collateral: 1.0, 
            public_key: "b210a01080a99b57fb3ce1a9c028de45aa5685e8", 
            initial_balance: None 
        }
    ) 
})): Failed to send transaction: MiddlewareError { 
    e: MiddlewareError(
        JsonRpcClientError(
            SerdeJson { 
                err: Error("provider error", line: 0, column: 0), 
                text: "(code: 1, message: failed to estimate gas: message execution failed: exit 33, revert reason: 0x015538b1000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000324d6574686f64206e6f7420616c6c6f7765642069662076616c696461746f722068617320616c7265616479206a6c77656420696e206d657373616765206661696c65642077697468206261636b74726163653a5c6e30303a20666d7468657265642077697468206261636b74726163653a5c6e30303a5c6e20696e206865786564203333290a30303a20663130313534353720286d6574686f64203338343434353038333729202d2d20636f6e747261637420726576657274656420283333290a2028292c20766d206572726f723a206d657373616765206661696c65642077697468206261636b74726163653a5c6e30303a2920290a", 
                vm error: message failed with backtrace:\n00: f0105457 (method 3844450837) -- contract reverted (33)\n01: f0105457 (method 6) -- contract reverted (33)\n (RetCode=33), data: None)" 
            }
        )
    ) 
}