Solidity 0.4.22 支持 require 和 revert 函数加上错误原因的字符串,如果交易被Revert将会返回这个字符串,并编码成 Error(string)
。
1 2 3 4 5 6 7
| pragma solidity ^0.6.6;
contract Test { function test() public { revert("always failed"); } }
|
在测试环境中进行部署后,使用 eth_call 调用:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "id": "1", "jsonrpc": "2.0", "method": "eth_call", "params": [{ "from": "0xF59fB9ff455c68627bd852d6FEb87Ba73398B9Aa", "to": "0x6cC7f0784587DE4c3Da4c74b01707a4d0A3B32a4", "value": "0x0", "gas": "0x2dc6c0", "gasPrice": "0x1", "data": "0xf8a8fd6d" },"latest"] }
|
即可获取到返回结果:
1 2 3 4 5
| { "jsonrpc": "2.0", "id": "1", "result": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d616c77617973206661696c656400000000000000000000000000000000000000" }
|
可以看到返回的结果,是使用 Error 进行了 ABI 编码了,所以可以进行解码。
使用 Go 进行数据解析,如果所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package main
import ( "bytes" "context" "fmt" "math/big"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/ethclient" )
func main() { rpcclient, _ := ethclient.Dial("http://127.0.0.1:8545") contract := common.HexToAddress("0x6cC7f0784587DE4c3Da4c74b01707a4d0A3B32a4") calldata := ethereum.CallMsg{ From: common.HexToAddress("0xF59fB9ff455c68627bd852d6FEb87Ba73398B9Aa"), To: &contract, Gas: 300_000, GasPrice: big.NewInt(1), Data: hexutil.MustDecode("0xf8a8fd6d"), }
result, _ := rpcclient.CallContract(context.Background(), calldata, nil) if bytes.HasPrefix(result, []byte{0x08, 0xc3, 0x79, 0xa0}) { strtyp, _ := abi.NewType("string", "", nil) var errstr string _ = abi.Arguments{{Type: strtyp}}.Unpack(&errstr, result[4:]) fmt.Println(errstr) } }
|
最终返回 always failed
,和预期的情况一致。
如果要查找历史交易的错误原因,可以在 eth_call
调用时,第二个参数传入交易被确认的高度即可。默认情况下,Geth 客户端仅保存最近的 128 块的信息,如果要获取更久远的交易,那么需要开启 --prune=archive
。
在 go-ethereum 1.9.14 abi 中,原生提供了 revert 解码,升级之后,可以直接使用:
1 2
| data, _ := hex.DecodeString("...") reason, _ := abi.UnpackRevert(data)
|
在 go-ethereum 1.9.15 后,eth_call 调用如果含有 Revert ,那么会直接返回含有错误的信息。在 OpenEthereum(parity) 中很久之前就返回错误了,但是没有给出详细的错误的字符串。
1 2 3 4 5 6 7 8 9
| { "jsonrpc": "2.0", "id": 1, "error": { "code": 3, "message": "execution reverted: some error", "data": "0x08c379a000000000000000000..." } }
|