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

fix: handle error and revert data in EthEstimateGas and EthCall #12553

Merged
merged 27 commits into from
Oct 25, 2024

Conversation

virajbhartiya
Copy link
Member

Related Issues

Closes #10311

Proposed Changes

Modified EthCall and EthEstimateGas to handle errors and revert data

Checklist

Before you mark the PR ready for review, please make sure that:

@virajbhartiya
Copy link
Member Author

@aarshkshah1992 I am using

func parseEthRevert(ret []byte) string {
to get the reverted reason for the Data field.

@aarshkshah1992
Copy link
Contributor

aarshkshah1992 commented Oct 4, 2024

Best way to test this is to write an itest that deploys a contract that does a divide by 0 (so there's a revert) -> send a transaction that executes the contract -> see what error you get when you call EthCall in your itests for the block which has the execution data for that contract.

I think you'll then see what I'm talking about. The client should see the contract revert reason in the data field of the error.

@BigLep
Copy link
Member

BigLep commented Oct 7, 2024

@virajbhartiya: I assume you'll re-request review when you're ready for review again.

@virajbhartiya
Copy link
Member Author

Hey @BigLep, yes I'll request a re-review, currently I'm running into a few issues while testing out on devnet so working on fixing those.

@akaladarshi
Copy link
Contributor

@aarshkshah1992 me and @virajbhartiya was discussing about returning execution reverted as a message and adding reason behind it as data.

The changes I have pushed are not complete, need to get your thoughts on it, then will finish the changes.

@aarshkshah1992
Copy link
Contributor

@akaladarshi Would be great to first get this working e2e on a calibnet node and we can then discuss the exact semantics of the error message.

Also, thanks to both for this !

@akaladarshi
Copy link
Contributor

node

@aarshkshah1992 here is the eth_estimateGas response after the changes

Screenshot 2024-10-11 at 5 20 04 PM

Here is the response from ethereum node for same data

Screenshot 2024-10-11 at 5 24 44 PM

@aarshkshah1992
Copy link
Contributor

@akaladarshi @virajbhartiya Great stuff ! That looks correct. Can you also confirm that we ONLY get this data field for contract reverts and that the response for all other errors is the same as it was before ? Once you confirm that, I think we can implement this for the other API as well. Thanks a lot.

CHANGELOG.md Outdated Show resolved Hide resolved
go.mod Outdated Show resolved Hide resolved
itests/fevm_test.go Outdated Show resolved Hide resolved
@aarshkshah1992
Copy link
Contributor

@virajbhartiya @akaladarshi

Reviewed. Thanks a lot. Also need to rebase this on master and fix the conflicts.

@aarshkshah1992
Copy link
Contributor

aarshkshah1992 commented Oct 15, 2024

Let's address the current batch of comments for which we have solid answers to @akaladarshi.

For stuff that is still unclear/ambiguous -> we can then request a review from Stebalien 👍

@akaladarshi akaladarshi requested a review from rvagg October 23, 2024 03:51
CHANGELOG.md Outdated Show resolved Hide resolved
api/api_errors.go Outdated Show resolved Hide resolved
api/api_errors.go Outdated Show resolved Hide resolved
Copy link
Member

@rvagg rvagg left a comment

Choose a reason for hiding this comment

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

pretty close, some minor, mostly cosmetic suggestions

Also, I'm down to 2 failures in my local branch of https://github.com/filecoin-project/fevm-contract-tests and I think one of them is about a missing data field for a reverted contract, so I believe this will solve that.

@akaladarshi akaladarshi requested a review from rvagg October 24, 2024 04:32
go.mod Outdated Show resolved Hide resolved
Copy link
Member

@rvagg rvagg left a comment

Choose a reason for hiding this comment

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

just the go-jsonrpc dependency bump and we can merge this

@akaladarshi
Copy link
Contributor

@virajbhartiya We need to update the version of go-jsronrpc to the latest release.

@rvagg rvagg enabled auto-merge (squash) October 25, 2024 01:08
@rvagg
Copy link
Member

rvagg commented Oct 25, 2024

👌 very nice work @akaladarshi & @virajbhartiya! Especially challenging in that you had to coordinate design changes across repos.

@rvagg rvagg merged commit 082b7ca into filecoin-project:master Oct 25, 2024
81 checks passed
@akaladarshi akaladarshi deleted the jsonrpc branch October 25, 2024 04:52
@rvagg
Copy link
Member

rvagg commented Oct 31, 2024

Flagging that I'm having some problems with this testing fevm-contract-tests, I'm not sure we're doing a better job of conforming to expected go-ethereum output with this.

Most interestingly, there's a relevant chai assertion from Waffle that's now failing a lot with the Uniswap tests, revertedWith(string): https://github.com/TrueFiEng/Waffle/blob/238c11ccf9bcaf4b83c73eca16d25243c53f2210/waffle-chai/src/matchers/revertedWith.ts#L6

So I'm getting failures like this:

     AssertionError: Expected transaction to be reverted with Unable to reenter, but other exception was thrown: ProviderError: execution reverted

Coming out of an assertion like this:

await expect(reentrant.swapToReenter(pool.address)).to.be.revertedWith('Unable to reenter')

Which I believe is unmodified from the upstream uniswap tests.

I think that it's worked previously because we included the revert reason in the string and it matches the hardhat way of doing reverts (see decodeHardhatError, which is pretty liberal in its matching). But when you have a data field I think you're dealing with this: https://github.com/TrueFiEng/Waffle/blob/238c11ccf9bcaf4b83c73eca16d25243c53f2210/waffle-provider/src/revertString.ts#L28 which performs a full decode of a hex string: https://github.com/TrueFiEng/Waffle/blob/238c11ccf9bcaf4b83c73eca16d25243c53f2210/waffle-provider/src/revertString.ts#L37-L45

Maybe we should be returning the hex string rather than decoding it ourselves in

func parseEthRevert(ret []byte) string {
, or alternatively do both.

@rvagg
Copy link
Member

rvagg commented Nov 4, 2024

Recording some investigation progress as I try and get to the bottom of this.

I modified NewErrExecutionReverted locally to try and restore some of the original info in the error message, decoding the data string "reason" and also putting in the exit code like the original (although this isn't formatted quite the same, I need to try that next).

      err := &ErrExecutionReverted{
              Message: fmt.Sprintf("execution reverted (code=%d, reason=%s, vm error=%s)", exitCode, reason, error),
              Data:    fmt.Sprintf("0x%x", data),
        }

If I curl the exact request that the test I'm looking at is making, I can see its response is:

{
  "error": {
    "code": 11,
    "message": "execution reverted (code=33, reason=message failed with backtrace:\n00: f01004 (method 3844450837) -- contract reverted (33)\n (RetCode=33), vm error=Error(OLD))",
    "data": "0x586408c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034f4c44000000000000000000000000000
0000000000000000000000000000000"
  },
  "id": 46,
  "jsonrpc": "2.0"
}

Which has our decoded error message in it, and the point of data was that it should be able to find it in there but fall back to the message if necessary.

But modifying extern/fevm-uniswap-v3-core/node_modules/@ethereum-waffle/chai/dist/cjs/matchers/revertedWith.js to log the actual error object we're looking at for a reverted (console.error(revertedWith (looking for [${revertReason}] onError, error)), it comes out with this:

revertedWith (looking for [OLD] onError Error: call revert exception [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="observe(uint32[])", data="0x586408c379a000000000000
0000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034f4c440000000000000000000000000000000000000000000000000000000000", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)

Where's the error message gone? Has our new data field made the message get lost? Or, is there something about the error message formatting. Or is it the new error code field, which we've set to 11 because we're using a custom error, getting in the way and translating it some new way. I haven't been able to get to the bottom of this yet.

The code for @ethereum-waffle/chai version 3.4.4, which is an older one but it's what's packaged with fevm-uniswap-tests (and works upstream) that's doing the matching is:

            const reasonsList = error.results && Object.values(error.results).map((o) => o.reason);
            const message = (error instanceof Object && 'message' in error) ? error.message : JSON.stringify(error);
            const isReverted = reasonsList
                ? reasonsList.some((r) => r === revertReason)
                : message.includes('revert') && message.includes(revertReason);

We don't have a results and message.includes('revert') && message.includes(revertReason); should pass for this now, but the message no longer includes what we're sending it!

@rvagg
Copy link
Member

rvagg commented Nov 6, 2024

It seems to be all about the "data" field, once you add one then it fails to recognise it as a valid revert error. I've tried tinkering with the code, message and data field, and it's only when I remove the data field that I get it to pass. So the next step is to figure out why, is it decoding this wrong? Do we need a different format than this?


fail:

{
  "error": {
    "code": 1,
    "message": "message execution failed: exit 33, revert reason: message failed with backtrace:\n00: f01004 (method 3844450837) -- contract reverted (33)\n (RetCode=33), vm error: Error(O
LD)",
    "data": "0x586408c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034f4c440000000000000000000000000000000000000000000000000000000000"
  },
  "id": 46,
  "jsonrpc": "2.0"
}

fail:

{
  "error": {
    "code": 11,
    "message": "execution reverted (code=33, reason=message failed with backtrace:\n00: f01004 (method 3844450837) -- contract reverted (33)\n (RetCode=33), vm error=Error(OLD))",
    "data": "0x586408c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034f4c440000000000000000000000000000000000000000000000000000000000"
  },
  "id": 46,
  "jsonrpc": "2.0"
}

pass:

{
  "error": {
    "code": 1,
    "message": "message execution failed: exit 33, revert reason: message failed with backtrace:\n00: f01004 (method 3844450837) -- contract reverted (33)\n (RetCode=33), vm error: Error(OLD)"
  },
  "id": 46,
  "jsonrpc": "2.0"
}

pass:

{
  "error": {
    "code": 11,
    "message": "message execution failed: exit 33, revert reason: message failed with backtrace:\n00: f01004 (method 3844450837) -- contract reverted (33)\n (RetCode=33), vm error: Error(OLD)"
  },
  "id": 46,
  "jsonrpc": "2.0"
}

pass:

{
  "error": {
    "code": 11,
    "message": "execution reverted (code=33, reason=message failed with backtrace:\n00: f01004 (method 3844450837) -- contract reverted (33)\n (RetCode=33), vm error=Error(OLD))"
  },
  "id": 46,
  "jsonrpc": "2.0"
}

@rvagg
Copy link
Member

rvagg commented Nov 6, 2024

Got it, 0x586408c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034f4c440000000000000000000000000000000000000000000000000000000000 is cbor, it packs a byte slice which is 0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034f4c440000000000000000000000000000000000000000000000000000000000' when presented raw. When we return that then the eth libraries can read it.

Basically, we use cbor to pass these things around, but cbor is foreign to eth-land so we need to unpack.

@rvagg
Copy link
Member

rvagg commented Nov 7, 2024

addressed in #12675

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ☑️ Done (Archive)
Development

Successfully merging this pull request may close these issues.

Eth API: On revert, return data in the error
6 participants