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

Support for require reason #18360

Closed
tlxnithin opened this issue Dec 24, 2018 · 34 comments
Closed

Support for require reason #18360

tlxnithin opened this issue Dec 24, 2018 · 34 comments
Assignees

Comments

@tlxnithin
Copy link

Hi ,

As of Solidity 0.4.22, we can have string reasons as part of require or revert functions. But this seems to be not supported in Geth. Here is the reference.

So is it true the Geth doesn't support it yet? I have been using Geth 1.8.16 Linux. Have checked the latest change log for 1.8.20. Couldn't find anything on that.

Is Geth going to provide this soon if not already? If providing already, may I know which version I can use?

Is there any work around I can come up with?

Here is my etherum stack exchange question.

@tlxnithin
Copy link
Author

Does anybody have information on this?

@0xsarvesh
Copy link

This feature will definitely save a lot of development and debugging effort. 👍

@holiman
Copy link
Contributor

holiman commented Jan 24, 2019

So, geth is an ethereum client. revert/require are using the new (well, from Byzantium) EIP 206 instruction REVERT, which provides ends the execution in a 'cheap throw' and returns some data.

However, the data returned does not wind up in the logs, so it's not accessible from the outer scope of the transaction. It might be that you are requesting something like this: #15508 . However, I'm not quite sure what it would mean to "support" it -- I guess that depends on the usecase you have in mind.

If we can agree with others (Parity, Ganache, etc) what is needed, that would make it actionable.

@tlxnithin
Copy link
Author

tlxnithin commented Jan 24, 2019

I have read that Ganache and Truffle already have this. Parity is yet to implement this.
I hope you understood what I am looking for. "Support" might not be the right word as you said, I just need the require string reason to be accessible by clients like Web3, ether.js.

I hope this gives the idea of what I am looking for. Stackoverflow Question.

@vlmold
Copy link

vlmold commented Mar 30, 2019

any progress on this?

@stevenlcf
Copy link

stevenlcf commented May 3, 2019

In case you are looking for it, there is a similar issue (feature request) in parity: openethereum/parity-ethereum#8068

@holiman
Copy link
Contributor

holiman commented Jun 19, 2019

Ah now I get it. So essentially, our tracers are just javascript code. It should be fairly simple to add the reason into the call-struct. I'll assign this to @karalabe for now , as he's the original author of the calltracer

@pcowgill
Copy link

@karalabe If you prefer, we could consider using https://gitcoin.co/requests to get this issue funded.

@gitcoinbot
Copy link

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


This issue now has a funding of 700.0 DAI (700.0 USD @ $1.0/DAI) attached to it.

@gitcoinbot
Copy link

gitcoinbot commented Jul 31, 2019

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Workers have applied to start work.

These users each claimed they can complete the work by 5 months from now.
Please review their action plans below:

1) daniyal-24 has applied to start work (Funders only: approve worker | reject worker).

testing testing testing testing testing testing testing testing testing testing testing testing
2) alexjg has applied to start work (Funders only: approve worker | reject worker).

I'm interested in working on this but I'm not entirely sure what exactly the requested feature is. Is the idea to write a new built in tracer which nicely prints the argument to REVERT?

Learn more on the Gitcoin Issue Details page.

@pcowgill
Copy link

@karalabe If you prefer, we could consider using https://gitcoin.co/requests to get this issue funded.

I ended up submitting it, and it got funded! Thanks, @ceresstation!

@spm32
Copy link

spm32 commented Aug 1, 2019

@pcowgill do you have a preference regarding who takes on this issue?

@alexjg
Copy link

alexjg commented Aug 20, 2019

I'm interested in working on this (I've expressed interest on the gitcoin page). To make sure I understand, what exactly is the end outcome that we want? At the moment the returnValue of the debug_traceTransaction response contains the ABI encoded Error(string) call which Solidity passes toREVERT. Would the intention be to attempt to decode that (presumably in eth/api_tracer.go and if it is possible to decode, then place the decoded string in returnValue?

@Zergity
Copy link

Zergity commented Sep 11, 2019

The revert reason is already recorded in the return value of the contract call. The problem is that, web3.eth.sendTransaction does not provide the return value, since the transaction is not yet confirmed by the time the sendTransaction is invoked.

There are 2 possible ways to retrieved the return value (and the revert reason) of a confirmed transaction:
(1) get the past tx and call web3.eth.call(tx, tx.blockNumber)
(2) upgrade the protocol to include the revert reason to a log in the failed tx receipt

Theorically, (1) can prefectly replay the past tx, and get the correct result, but geth doesn't do it properly now, so the result will or will not be correct. I'm not sure about other client. To perfectly replay a past tx, a node need to have the pre-state of a block (archive mode), and replay all txs in the block prior to the given tx.

(2) should be perfect in the long term, but requires a protocol change.

@alexjg
Copy link

alexjg commented Sep 11, 2019

Okay that makes sense. Am I right in thinking that the inconsistency of (1) is just a consequence of not running an archival node, rather than a geth specific problem? If that's the case then this isn't really something that can be fixed by geth and this issue should be closed?

@Zergity
Copy link

Zergity commented Sep 11, 2019

Okay that makes sense. Am I right in thinking that the inconsistency of (1) is just a consequence of not running an archival node, rather than a geth specific problem? If that's the case then this isn't really something that can be fixed by geth and this issue should be closed?

As I've already said, to replay a tx to get the correct result, a node need to:
(a) have the state of the given block,
(b) re-apply all txs in that block before the given tx

(a) can be achieved by running geth in archive mode
(b) requires to modify the code of (s *PublicBlockChainAPI) Call()

@alexjg
Copy link

alexjg commented Sep 11, 2019

Right, so this issue should be closed then? @holiman

@pcowgill
Copy link

If that's the case, then in order for this to work in web3.js and ethers.js, assuming the user using those is pointing at Infura or Alchemy or Etherscan for their node (unfortunately given the centralizing nature of this choice), then is the main thing that's needed moving this issue over to an the Infura, Alchemy, Etherscan etc. repos?

@PhABC
Copy link

PhABC commented Dec 19, 2019

Any update on this? Could be done like done in https://github.com/pertsev/web3_utilz/tree/master/revert%20reason

@Zergity
Copy link

Zergity commented Dec 20, 2019

Any update on this? Could be done like done in https://github.com/pertsev/web3_utilz/tree/master/revert%20reason

To get the correct past tx reason, clients (geth) still need to correctly reconstruct the pre-state of the tx. Currently you can still get the estimated result, which might be correct ~90% of the time.

To get the imcoming transaction revert reason, the best place to do is in the wallet/extension. I'm working on an extension (alternative to MetaMask), which asynchronously fetch the transaction result while waiting for user confirmation.

image

@xiaods
Copy link

xiaods commented Feb 15, 2020

hi, geth always support the reason string in tx. but you need asynchronously add a event tracking to extract the failed tx string. see this patch: eventeum eventeum/eventeum#80

web3j's code snippet:
https://github.com/web3j/web3j/blob/23beb6f394afabe143f1904d8179180152f8f212/core/src/main/java/org/web3j/protocol/core/methods/response/EthCall.java#L44

@xiaods
Copy link

xiaods commented Feb 16, 2020

i think this is not geth scope, please close this issue.

@sebastianst
Copy link
Contributor

sebastianst commented Mar 22, 2020

I successfully managed to extract revert reasons (from a simulated blockchain) in my tests with the following code:

func errorReason(ctx context.Context, b *ContractBackend, tx *types.Transaction, blockNum *big.Int) (string, error) {
	msg := ethereum.CallMsg{
		From:     b.account.Address,
		To:       tx.To(),
		Gas:      tx.Gas(),
		GasPrice: tx.GasPrice(),
		Value:    tx.Value(),
		Data:     tx.Data(),
	}
	res, err := b.CallContract(ctx, msg, blockNum)
	if err != nil {
		return "", errors.Wrap(err, "CallContract")
	}
	return unpackError(res)
}

var (
	errorSig            = []byte{0x08, 0xc3, 0x79, 0xa0} // Keccak256("Error(string)")[:4]
	abiString, _        = abi.NewType("string", "", nil)
)

func unpackError(result []byte) (string, error) {
	if len(result) < 4 || !bytes.Equal(result[:4], errorSig) {
		return "<tx result not Error(string)>", errors.New("TX result not of type Error(string)")
	}
	vs, err := abi.Arguments{{Type: abiString}}.UnpackValues(result[4:])
	if err != nil {
		return "<invalid tx result>", errors.Wrap(err, "unpacking revert reason")
	}
	return vs[0].(string), nil
}

Note that ContractBackend is a custom type in my application that embads the simulated blockchain backend, so it fulfills the ethereum.ContractCaller interface (i.e., it has method CallContract), and has a field account which is the account used for sending transactions.

After a transaction failed, you usually hold a *types.Receipt from bind.WaitMined, which has the field BlockNumber, so you know with which block number to call this.

(errors is package "github.com/pkg/errors")

@Zergity
Copy link

Zergity commented Mar 23, 2020

Or you can use https://chrome.google.com/webstore/detail/ezdefi/ejeemacpidnaejkhpbmfkadhgjhnolaa instead of MM, and you can see the revert reason clearly on confirmation dialog, or tx history UI.

@rjl493456442
Copy link
Member

We already support it for EstimateGas, and still a open PR for EthCall(Waiting for merging) #21083

@sebastianst
Copy link
Contributor

Great news @rjl493456442 @MariusVanDerWijden that EstimateGas now returns the revert reason!

I was wondering if the revert reason is accessible as a special error type field returned by EstimateGas implementations or just part of the larger error string? I had a look at commit b9df7ec where some changes related to this were implemented and here b9df7ec#diff-607ee6300c766df112070d851a1661c9R885 it looks like internally, there already is an error type with field revert string that is populated with the revert reason, if it can be extracted from the vm return value. But, without having tested it, it looks like EstimateGas only returns a larger error string that contains the revert reason somewhere in parenthesis, both for the SimulatedBackend and real API calls.

What do you think about adding a public error type like TransactionRevertedError that has a public field Reason string (and maybe also another vm error field) and having this error returned by EstimateGas (as well as maybe CallContract) implementations? This way, we could directly react to revert reasons in code using go-ethereum by type-checking the returned errors for this error type. Would be more stable than extracting the revert reason from the larger error string.

And thanks @rjl493456442 for adding abi.UnpackRevert, lets us ditch my own implementation I posted above ;)

@rjl493456442
Copy link
Member

rjl493456442 commented May 21, 2020

Hi @sebastianst

This way, we could directly react to revert reasons in code using go-ethereum by type-checking the returned errors for this error type

Do you mean you want to use the Geth as the library and call EstimateGas/ Call directly? If so, I can definitely change the error to a public one(maybe use the new error feature of Go). But usually this function is called via RPC, so the returned value is marshaled anyway.

@sebastianst
Copy link
Contributor

Do you mean you want to use the Geth as the library and call EstimateGas/ Call directly? If so, I can definitely change the error to a public one(maybe use the new error feature of Go). But usually this function is called via RPC, so the returned value is marshaled anyway.

Exactly :) we use go-ethereum in go-perun and in a few places, we'd react differently to different revert reasons. E.g., here we can currently just check whether a general tx failed error occurred. We plan to change our contracts to emit standardized revert reasons, so being able to extract the revert reason directly from an EstimateGas/ Call error would be great.

Also happy to use the new Go error semantics. Then we could do

var rerr *ethereum.TransactionRevertedError
if errors.As(err, &rerr) && rerr.Reason == "ALREADY_REGISTERED" {
  // handle ALREADY_REGISTERED revert error...
}

@juztin
Copy link

juztin commented May 21, 2020

I'm also using the Geth libs (using go generate ...) for a CLI. Previously the errors were being wrapped up in an error we could use, now with 1.9.14 we just get an ABI marshaling error.
So having this error would def be nice.

@rjl493456442
Copy link
Member

rjl493456442 commented May 22, 2020

@sebastianst Marius told me that you are using the ethclient to interact with the Geth node? In this way, all the response of the APIs is marshalled by JSON(ethclient is just a client which connect the Geth node through the RPC). It won't help you to react with revert reason directly.

@sebastianst
Copy link
Contributor

sebastianst commented May 22, 2020

Yes I also expected this to be a problem for when using the ethclient... it would only be beneficial in testing where we use the SimulatedBackend. I guess there's no possibility to add an additional field to the JSON so that the revert reason could cross API boundaries? Like

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "some error",
    "reason": "revert reason"
  }
}

Then the eth client could check for the existence of this field and assemble it back to a TransactionRevertedError.

@rjl493456442
Copy link
Member

rjl493456442 commented May 22, 2020

No, it's impossible to add another field reason in RPC response. But you can parse the error message from the message field, with some specific rules.

e.g. message: "always failing transaction (execution reverted) (revert reason)", then extract the "revert reason" out

@fjl
Copy link
Contributor

fjl commented Jun 8, 2020

This is now implemented as of #21083

The new format is

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

We chose this because "data" is supported in the JSON-RPC specification.

@fjl fjl closed this as completed Jun 8, 2020
@gorgos
Copy link

gorgos commented Sep 8, 2020

How about a special revert message when a function selector doesn't exist?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests