使用EVM tracing调试合约说明
1.背景
OEC完全兼容以太坊合约的部署、运行,对于复杂的合约,开发者需要了解其执行过程,以便调试合约,高效定位合约问题
2.问题
目前OEC测试网和主网上未提供查看合约trace功能,浏览器上也未提供trace信息
3.解决方案
3.1.描述及限制
当前方案中,trace功能会请求OEC节点重新执行指定交易,并返回执行后的日志.
一个OEC节点如果想重新执行一笔交易,并返回与之前完全一样的结果,需要这个交易的历史状态, 所在区块的所有元数据 以及同一区块内的所有前置交易的状态.
因此根据节点同步以及裁剪机制不同, 可能会出现不同的trace结果:
3.2.配置及使用
3.2.1.配置节点, 开启trace功能
节点启动时,可以配置以下参数用于trace功能:
--debug-api=true
其中, --debug-api
是否开启debug功能,默认为false,即不开启该功能。
3.2.2.查看trace信息
3.3.2.1.入参说明
disableStack 默认为false, 若设置为true, 则返回日志中将不再包含Stack字段
disableStorage 默认为false, 若设置为true, 则返回日志中将不再包含Storage字段
disableMemory 默认为false, 若设置为true, 则返回日志中将不再包含Memory字段
disableReturnData 默认为false, 若设置为true, 则返回日志中将不再包含ReturnData字段
tracers 默认为空,即使用StructLogger记录全量trace日志.
- 可以配置预创建的脚本, 支持的脚本参见 https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/js/internal/tracers
- 也可以配置 自定义javaScript脚本. 语法参见 https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction
- 若tracer字段不为空, 则忽略所有disable字段.
3.3.2.2.配置举例
3.2.2.2.1.Disable配置
输入:
$ curl -H "Content-Type: application/json" -d '{"id": 1, "method": "debug_traceTransaction", "params": ["0xfc9359e49278b7ba99f59edac0e3de49956e46e530a53c15aa71226b7aa92c6f", {"disableStack": true, "disableMemory": true, "disableStorage": true}]}' localhost:8545
输出:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"failed": false,
"gas": 21449,
"returnValue": "",
"structLogs": [
{
"depth": 1,
"gas": 42749,
"gasCost": 3,
"op": "PUSH1",
"pc": 0
},
{
"depth": 1,
"gas": 42746,
"gasCost": 3,
"op": "PUSH1",
"pc": 2
},
...
...
...
{
"depth": 1,
"gas": 21301,
"gasCost": 1,
"op": "JUMPDEST",
"pc": 154
},
{
"depth": 1,
"gas": 21300,
"gasCost": 0,
"op": "STOP",
"pc": 155
}
]
}
}
3.2.2.2.2.预创建Tracer配置
可使用的预创建脚本 参见: https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/js/internal/tracers
输入:
curl -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["0x585218efd4f452912f00878703984c58d2361e89618c9dd57586415756b6476f", {"tracer" : "unigramTracer"}],"id":1}' localhost:8545
输出:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"ADD": 3,
"CALLDATALOAD": 2,
"CALLDATASIZE": 2,
"CALLVALUE": 1,
"DUP1": 7,
"DUP2": 9,
"DUP3": 9,
"DUP4": 1,
"DUP5": 2,
"DUP6": 1,
"EQ": 4,
"GT": 1,
"ISZERO": 3,
"JUMP": 16,
"JUMPDEST": 21,
"JUMPI": 8,
"LT": 1,
"MSTORE": 1,
"POP": 24,
"PUSH1": 15,
"PUSH2": 24,
"PUSH32": 1,
"PUSH4": 3,
"SHR": 1,
"SLOAD": 1,
"SLT": 1,
"SSTORE": 1,
"STOP": 1,
"SUB": 3,
"SWAP1": 12,
"SWAP2": 10,
"SWAP3": 5
}
}
3.2.2.2.3.自定义JavaScript-based Tracer配置
语法参见 https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction
输入:
curl -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["0x7E031D46C425A78F802E55058733699A341FF44E6778EEEFADD7BBD98F2A0DD6", {"tracer" : "{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"SSTORE\") this.data.push(log.stack); }, result: function() { return this.data; }}"}],"id":1}' localhost:8545
输出:
{
"jsonrpc": "2.0",
"id": 1,
"result": [
"0",
"128",
"0",
"0",
"43576122634770472758325941782982599838796957244005075818703754470792663924736",
"154",
"4",
"0",
"0",
"4",
"1",
"1",
"0",
"0",
"1"
]
}
3.2.2.3.错误信息
1. tx (585218EFD4F452912F00878703984C58D2361E89618C9DD57586415756B6476E) not found
说明: 无法找到指定的 tx
- 检查指定的 tx 是否已经正确执行(在mempool中也无法debug)
- 检查节点是否是归档节点(pruning=nothing)
- 检查节点是否使用了非s3的快照启动, 且tx所在高度是否包含在快照中.
2. tracer err : ReferenceError: identifier 'unig1ramTracer' undefined
说明: 不存在unig1ramTracer 这个预创建的tracer
- 支持脚本参见:https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/js/internal/tracers
3. tracer err : SyntaxError: parse error (line 1)
说明: tracer脚本语法错误.
3.2.2.4.配置限制
debug_traceTransaction(hash common.Hash, config. *vm.Config)入参config. *vm.Config中如果配置了tracer字段, 则会优先创建JavaScript-based Tracer 而忽略Disable配置.