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

eth_call cannot distinguish between success and failure with require-reason #19027

Closed
epheph opened this issue Feb 8, 2019 · 6 comments · Fixed by #21083
Closed

eth_call cannot distinguish between success and failure with require-reason #19027

epheph opened this issue Feb 8, 2019 · 6 comments · Fixed by #21083
Assignees

Comments

@epheph
Copy link

epheph commented Feb 8, 2019

Geth version: 1.8.22
OS & Version: Linux
Commit hash : (Official docker image)

Expected behaviour

When using the JSON-RPC interface to make an eth_call to a contract call that fails with a require-with-reason, I expect the response to this request to clearly and unambiguously indicate that call has failed.

Actual behaviour

When using the JSON-RPC, an eth_call that hits a require with a reason will return a response to the call which does not indicate anything failed, except that the response starts with 0x08c379a0 .

Steps to reproduce the behaviour

Take this contract as an example:

pragma solidity 0.5.1;

contract hi {
    function bad() public pure {
        require(false, "some error");
    }

    function good() public pure {
        assembly {
            mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
            mstore(0x4, 0x0000000000000000000000000000000000000000000000000000000000000020)
            mstore(0x24, 0x000000000000000000000000000000000000000000000000000000000000000a)
            mstore(0x44, 0x736f6d65206572726f7200000000000000000000000000000000000000000000)
            return(0x0, 0x64)
        }
    }
}

Making an eth_call against good() (0xaa8fb40b):

$ curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x913da4198e6be1d5f5e4a40d0667f70c0b5430eb","to":"0x3b126a0d103f020d8cc56d9d69d861a0c387366e","data":"0xaa8fb40b","value":"0x0","gas":"0x2dc6c0"}, "latest"],"id":1}' localhost:8545 -H "Content-Type: application/json" | jq .
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a736f6d65206572726f7200000000000000000000000000000000000000000000"
}

A call against bad() (0x9c3674fc):

$ curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x913da4198e6be1d5f5e4a40d0667f70c0b5430eb","to":"0x3b126a0d103f020d8cc56d9d69d861a0c387366e","data":"0x9c3674fc","value":"0x0","gas":"0x2dc6c0"}, "latest"],"id":1}' localhost:8545 -H "Content-Type: application/json" | jq .
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a736f6d65206572726f7200000000000000000000000000000000000000000000"
}

The result here is identical, even though one indicates a failure/revert and the other succeeded.

Backtrace

n/a

Additional Notes:

For context, parity returns the bad() call with:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32015,
    "message": "VM execution error.",
    "data": "Reverted 0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000104e6f7420656e6f7567682066756e647300000000000000000000000000000000"
  },
  "id": 38
}

It seems like this could be a security problem, since it doesn't matter where in the call stack the require-with-reason occurs, the reason will be returned in the eth_call directly. If some balance lookup triggered an external contract which performed this require (easily performed within a gas stipend), you could trick the caller into reading data improperly from their own node

@glesaint
Copy link

glesaint commented Jul 22, 2019

Replying as there hasn't been update in the past few months. Geth doesn't return the revert reason on view/pure functions.

Now that OpenZeppelin 2.3.0 and others includes revert reasons on each require, more and more people will face this. For example: 1, 2, 3, 4, 5, 6

You can test it here, read "getPersonIdForName" with the name "0". The code is verified so you can see it. The returned value is always 0x8C379A0...0. Which is the EVM function selector for Error(string).

@Vasiliy-Bondarenko
Copy link

This is a shame that this bug was not fixed for such a long time... :(
This is not an edge case, this is kind of core functionality of smart contracts and it should work the same way on every client...

@dev-boom
Copy link

I am having the same problem with this. I should get revert but instead, I got 3963877391197344453575983046348115674221700746820753546331534351508065746944. Imagine we have functions that expect uint256 and you get this weird number.

(The number above when you turn to hex it's 8C379A000000000000000000000000000000000000000000000000000000000)

@Vasiliy-Bondarenko
Copy link

Do we have any hope for this issue to be resolved?..

@wighawag
Copy link

wighawag commented Nov 6, 2019

I agree, every eth_call that result in revert should be differentiable from the one that simply return data indistinguishable by itself from a revert-reason
This is very important for any service that want to cache/build data out of contracts.

@fjl
Copy link
Contributor

fjl commented Jun 8, 2020

This was implemented in the PR above. The error format we chose is:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 3,
    "message": "execution reverted: some error",
    "data": "0x08c379a000000000000000000..."
  }
}

DarianShawn pushed a commit to dogechain-lab/dogechain that referenced this issue Jun 17, 2023
# Description

upstream PR:
[0xPolygon#1566](0xPolygon#1566)

This allows returning custom revert errors from solidity, they are ABI
encoded functions that can represent rich and gas efficient revert
messages.

Details of custom errors:
https://blog.soliditylang.org/2021/04/21/custom-errors/

This is the implementation expected by ethers.js and web3.js and the
format go-ethereum chose
ethereum/go-ethereum#19027 (comment)

# Changes include

- [x] New feature (non-breaking change that adds functionality)

# Checklist

- [x] I have assigned this PR to myself
- [x] I have added at least 1 reviewer
- [x] I have added the relevant labels

## Testing

- [x] I have tested this code with the official test suite
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants