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

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

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.

}

func (e *EthCallError) Error() string {
return e.Message
Copy link
Contributor

Choose a reason for hiding this comment

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

But how will the go-jsonrpc library extract the data here to then send it to the client ?

Copy link
Contributor

Choose a reason for hiding this comment

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

We're not exposing the data at all here.

@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
@@ -3,6 +3,17 @@
# UNRELEASED

## New features

* Add `EthSendRawTransactionUntrusted` RPC method to be used for the gateway when accepting `EthSendRawTransaction` and `eth_sendRawTransaction`. Applies a tighter limit on the number of messages in the queue from a single sender and applies additional restrictions on nonce increments. ([filecoin-project/lotus#12431](https://github.com/filecoin-project/lotus/pull/12431))
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably need to rebase on master again for all of these to disappear.


// ErrorCode returns the JSON error code for a revert.
func (e *ExecutionRevertedError) ErrorCode() int {
return 3
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this hard coded to 3 ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we have a Code field on ExecutionRevertedError ? Shouldn't we use that ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah this (3) should be default error code for EthCall and EstimateGas. Will update it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean we should pass the error code/exit code from the receipt returned by applyMessage here, right ? So when EthCall/EthEstimateGas create this reverted error, they should use the exit code returned by FVM here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah that make sense, So let me confirm once what kind of error codes FVM returns and I will update it accordingly.

type ExecutionRevertedError struct {
Message string
Code int
Meta []byte
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't see this Meta field being used anywhere. It's neither written to or read from. Should be accepting it in the constructor and setting it ?

Copy link
Contributor

Choose a reason for hiding this comment

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

We can remove this as we have Data field now.

go.mod Outdated
@@ -12,6 +12,8 @@ replace github.com/filecoin-project/test-vectors => ./extern/test-vectors // pro

replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi // provided via a git submodule

replace github.com/filecoin-project/go-jsonrpc => github.com/virajbhartiya/go-jsonrpc v0.0.0-20241011111701-53eab64ec154
Copy link
Contributor

Choose a reason for hiding this comment

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

We will need to change this once the go-jsonrpc PR lands and we have a release for it.

@@ -31,6 +31,10 @@ import (
"github.com/filecoin-project/lotus/chain/vm"
)

const (
errcodeDefault = -32000
Copy link
Contributor

Choose a reason for hiding this comment

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

What's this for ? Why did we have to add it ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Will remove it in favor of error code 3 for EthCall and EstimateGas, but in other places for execution reverted we need to return this(-32000) code.

Copy link
Contributor

Choose a reason for hiding this comment

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

@akaladarshi But then why was it not a pre-existing code ? How did we handle this before you raised this PR ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I have to look into it little bit what exact error code FVM is returning, that's why I asked it in slack (do we have same error code as ethereum): https://filecoinproject.slack.com/archives/CP50PPW2X/p1728633105115299,

Will check this and update it accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Irrespective of what it is returning, let's not change the error codes here. We only want the data field to be in like with ETH here -> everything else should stay the same right ?

@@ -7,14 +7,17 @@ import (
"encoding/binary"
"encoding/hex"
Copy link
Contributor

@aarshkshah1992 aarshkshah1992 Oct 15, 2024

Choose a reason for hiding this comment

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

Why do we have such a large diff in this file ? Can we get rid of all the unnecessary whitespace changes and only change the test code we need to ? Reviewing diffs like this is tricky.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's a linter fix, everywhere there is whitespace after //

Copy link
Member

Choose a reason for hiding this comment

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

if this is from your editor doing this automatically then it's typically good practice to disable that--it's fine to make lint fixes but large diffs like that should be done as a separate PR and not come in with a PR where the changes are unrelated, or at least make them minimal (e.g. it's OK to fix up some of these around the site where you're changing things, but usually not the whole file if you can help it.)

Copy link
Contributor

Choose a reason for hiding this comment

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

So generally I try to do go fmt in files after changing things that's why sometimes I get large diff, anyway, I will disable it and revert these whitespace changes so we can keep it clean.

reason := parseEthRevert(res.MsgRct.Return)
return nil, xerrors.Errorf("message execution failed: exit %s, revert reason: %s, vm error: %s", res.MsgRct.ExitCode, reason, res.Error)
return nil, ethtypes.NewExecutionRevertedWithDataError(
errcodeDefault,
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 we pass the exit code on the receipt here ? Why are we passing errcodeDefault here ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@aarshkshah1992 actually there was some confusion about what error code to return because, in ethereum there two different kind of error codes for the same message (execution reverted), In EthCall and EstimateGas it returns error code 3 and generally while executing message it returns -3200.

I will the default error code to 3 for EthCall and EstimateGas which we were already returning in ErrorCode()

@@ -1612,6 +1623,13 @@ func ethGasSearch(
}

func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {
var result ethtypes.EthBytes
defer func() {
if r := recover(); r != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this for ? Why do we need it ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed.

return ethtypes.EthBytes{}, nil
} else if len(invokeResult.MsgRct.Return) > 0 {
return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return)))
result, err = cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return)))
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this change doing ? I don't see us returning an ExecutionRevertedError anywhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

It was added just to add more context in the error:
return nil, xerrors.Errorf("failed to read msg recipient: %w", err)

ExecutionRevertedError will be returned by the applyMessage directly as it was already returning the execution reverted error.

@@ -1612,6 +1623,13 @@ func ethGasSearch(
}

func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockNumberOrHash) (ethtypes.EthBytes, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Where are we returning an ExecutionRevertedError in this function ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@aarshkshah1992
We are returning the ExecutionRevertedError from applyMessage function directly, so we don't to do it separately.

In EstimateGas we are checking separately because of GasEstimateMessageGas function also returns an error.

https://github.com/virajbhartiya/lotus/blob/b1c146a3a2f2f9ca0a1c56a7f0ef7bf438a7db59/node/impl/full/eth.go#L1435

@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
Copy link
Contributor

@aarshkshah1992 So I have removed all the hardcoded error codes, and I am directly using the FVM returned error code.

Now the only thing we need to answer is whether we should register the ExecutionRevertedError with an error code like this.

  • If we do then we can remove the ErrorWithCode interface from go-jsonrpc.
  • If not then we need to keep it in order to get the code for the ExecutionRevertedError error

@aarshkshah1992
Copy link
Contributor

@Stebalien Please can you take a look at just the one comment above i.e. #12553 (comment) ?

@aarshkshah1992
Copy link
Contributor

@akaladarshi Please can you fix the CI ? Looks like it's all red.

@virajbhartiya
Copy link
Member Author

@aarshkshah1992 I was fixing a test, I'll check why the CI is failing

@Stebalien
Copy link
Member

@Stebalien Please can you take a look at just the one comment above i.e. #12553 (comment)?

Personally, I'd:

  1. Register the error using the existing error registration logic in this PR.
  2. If you're up for it, make a new patch that switches to an interface-based version with ErrorWithCode. IMO, that's a much better design but it's a larger refactor. I'd rather not use both mechanisms at the same time.

@aarshkshah1992
Copy link
Contributor

@Stebalien

Register the error using the existing error registration logic in this PR.

Which PR ? Your comment is missing a link.

}

// NewExecutionRevertedWithDataError returns an ExecutionRevertedError with the given code and data.
func NewExecutionRevertedWithDataError(code int, data string) *ExecutionRevertedError {
Copy link
Member

Choose a reason for hiding this comment

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

constructor name should match struct name with a New in front

"golang.org/x/xerrors"
)

var ErrExecutionReverted = xerrors.New("execution reverted")
Copy link
Member

Choose a reason for hiding this comment

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

You've made 2 error objects in this file (this one, and the struct that conforms to the interface) and the only time you use this one is for its string in this file and once in the itest. I'd just either use this string, "execution reverted" inline in the constructor, or make it a private const here. It's OK to expect ErrorContains "execution reverted" in the test without linking it to an existing const, in fact that's desirable in a test so make sure what you think is a const is actually what you want it to be.

Comment on lines 20 to 22
if e.Message == "" {
return fmt.Sprintf("json-rpc error %d", e.Code)
}
Copy link
Member

Choose a reason for hiding this comment

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

do we expect to ever hit this?

maybe we should do away with Message entirely and just always return this, make it ("execution reverted: %d", e.Code)

Copy link
Member

Choose a reason for hiding this comment

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

and if we don't hit this code, I wonder what the point of Code is, do we use that anywhere?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah so If you look at the ErrorCode() function it returns the code, which is basically for not registered errors.

If you look at this comment above I think you will get the idea: #12553 (comment)
Whatever we decide based on it, we will either remove the code or keep it.

@@ -7,6 +7,7 @@
- Reduce size of embedded genesis CAR files by removing WASM actor blocks and compressing with zstd. This reduces the `lotus` binary size by approximately 10 MiB. ([filecoin-project/lotus#12439](https://github.com/filecoin-project/lotus/pull/12439))
- Add ChainSafe operated Calibration archival node to the bootstrap list ([filecoin-project/lotus#12517](https://github.com/filecoin-project/lotus/pull/12517))
- Fix hotloop in F3 pariticpation API ([filecoin-project/lotus#12575](https://github.com/filecoin-project/lotus/pull/12575))
- Return data with `eth_call` and `eth_estimateGas` APIs `execution reverted` error ([filecoin-project/lotus#12553](https://github.com/filecoin-project/lotus/pull/12553))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- Return data with `eth_call` and `eth_estimateGas` APIs `execution reverted` error ([filecoin-project/lotus#12553](https://github.com/filecoin-project/lotus/pull/12553))
- Return a `Data` field from RPC when `eth_call` and `eth_estimateGas` APIs encounter `execution reverted` errors ([filecoin-project/lotus#12553](https://github.com/filecoin-project/lotus/pull/12553))

Comment on lines 125 to 132
func (e *ErrExecutionRevertedWithData) Error() string { return e.message }

// ErrorData returns the error data.
func (e *ErrExecutionRevertedWithData) ErrorData() interface{} { return e.data }

// NewErrExecutionRevertedWithData creates a new ErrExecutionRevertedWithData.
func NewErrExecutionRevertedWithData(data string) *ErrExecutionRevertedWithData {
return &ErrExecutionRevertedWithData{
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
func (e *ErrExecutionRevertedWithData) Error() string { return e.message }
// ErrorData returns the error data.
func (e *ErrExecutionRevertedWithData) ErrorData() interface{} { return e.data }
// NewErrExecutionRevertedWithData creates a new ErrExecutionRevertedWithData.
func NewErrExecutionRevertedWithData(data string) *ErrExecutionRevertedWithData {
return &ErrExecutionRevertedWithData{
func (e ErrExecutionRevertedWithData) Error() string { return e.message }
// ErrorData returns the error data.
func (e ErrExecutionRevertedWithData) ErrorData() interface{} { return e.data }
// NewErrExecutionRevertedWithData creates a new ErrExecutionRevertedWithData.
func NewErrExecutionRevertedWithData(data string) ErrExecutionRevertedWithData {
return ErrExecutionRevertedWithData{

Does jsonrpc bork still on non-interface? Not a big deal but if we can avoid pointer receivers where they are not necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

@rvagg They are necessary as of now because we are registering the errors as RPCErrors.Register(EExecutionRevertedWithData, new(*ErrExecutionRevertedWithData)) } so go-jsonrpc will expect error to be returned as a pointer type for c, ok := e.byType[reflect.TypeOf(err)]; .

I am trying to figure out a way, so we can get the code without worrying if the error is pointer type or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ⌨️ In Progress
Development

Successfully merging this pull request may close these issues.

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