从 geth 事务跟踪中提取发出的事件(日志)(debug_traceCall)

pok*_*kyy 5 ethereum evm go-ethereum geth

使用debug_traceCall时,我可以获得执行期间所有操作码和状态更改的低级 EVM 跟踪。这实在是太详细了。当我使用 default 时callTracer,我可以获得更好的调用树。但是,无论哪种方式,我似乎都无法从跟踪中提取发出的事件。我可以在跟踪(LOG*操作码)中看到它们,但是没有简单的方法可以将它们实际解析为“可读”的东西(以及值和原始地址)必须有一种方法来获取日志 - 有什么想法吗?

例如。这是 Etherscan 显示的https://etherscan.io/tx-decoder?tx=0x3e3ad35fda1fddd9e154b3860b50371a1acd2fdb4f27f897e234846522bde732(请参阅“发出的事件”部分)

pok*_*kyy 11

所以我自己想到了这一点 - 我为 geth 创建了一个自定义 JavaScript 跟踪器,该跟踪器在debug_traceCall的第三个参数中传递给 geth (请参阅链接提供的 API 参考):

{
    data: [],
    fault: function (log) {
    },
    step: function (log) {
        var topicCount = (log.op.toString().match(/LOG(\d)/) || [])[1];
        if (topicCount) {
            var res = {
                address: log.contract.getAddress(),
                data: log.memory.slice(parseInt(log.stack.peek(0)), parseInt(log.stack.peek(0)) + parseInt(log.stack.peek(1))),
            };
            for (var i = 0; i < topicCount; i++)
                res['topic' + i.toString()] = log.stack.peek(i + 2);
            this.data.push(res);
        }
    },
    result: function () {
        return this.data;
    }
}
Run Code Online (Sandbox Code Playgroud)

该跟踪器由 geth 对跟踪中的每个操作执行。本质上它的作用是:

  • 检查这是否是LOG0LOG1LOG2或EVM 操作码之一LOG3LOG4
  • 从当前合约中提取合约地址
  • 提取默认topic0和后续主题(如果有)
  • 从内存中提取附加事件数据(注意:stack[0] 是偏移量,stack[1] 是数据大小)

将跟踪器传递给 geth 看起来像这样:

res = await ethersProvider.send('debug_traceCall', [{
    from: tx.from,
    to: tx.to,
    gas: BigNumber.from(tx.gas)._hex.replace('0x0', '0x'),
    gasPrice: BigNumber.from(tx.gasPrice)._hex.replace('0x0', '0x'),
    value: BigNumber.from(tx.value)._hex.replace('0x0', '0x'),
    data: tx.input
}, "latest", {
    tracer: "{\n" +
        "    data: [],\n" +
        "    fault: function (log) {\n" +
        "    },\n" +
        "    step: function (log) {\n" +
        "        var topicCount = (log.op.toString().match(/LOG(\\d)/) || [])[1];\n" +
        "        if (topicCount) {\n" +
        "            var res = {\n" +
        "                address: log.contract.getAddress(),\n" +
        "                data: log.memory.slice(parseInt(log.stack.peek(0)), parseInt(log.stack.peek(0)) + parseInt(log.stack.peek(1))),\n" +
        "            };\n" +
        "            for (var i = 0; i < topicCount; i++)\n" +
        "                res['topic' + i.toString()] = log.stack.peek(i + 2);\n" +
        "            this.data.push(res);\n" +
        "        }\n" +
        "    },\n" +
        "    result: function () {\n" +
        "        return this.data;\n" +
        "    }\n" +
        "}",
    enableMemory: true,
    enableReturnData: true,
    disableStorage: true
}])
Run Code Online (Sandbox Code Playgroud)