从Geth交易跟踪中提取发射事件(日志)(debug_tracecall)

发布于 2025-01-25 04:27:29 字数 624 浏览 5 评论 0 原文

当使用 debug_tracecall 并在执行过程中发生变化。这太详细了。当我使用默认 callTracer 时,我可以得到一个更好的调用树。但是,我似乎都无法从跟踪中提取发射事件。我可以在跟踪中看到它们( log* opcodes),但是没有简单的方法可以将它们实际解析为“可读”的东西(以及值和原始地址),必须有一种方法来获取日志 - 有什么想法吗?

例如。这是埃塞卡(Etherscan)显示(请参阅发射事件部分)

When using debug_traceCall, I get a low-level EVM trace of all opcodes and state changes during the execution. This is excessively detailed. When I use default callTracer, I can get a much nicer call tree. However, neither way I cannot seem to be able to extract the emitted events from the trace. I can see them in the trace (LOG* opcodes) however there is no easy way to actually parse them to something "readable" (along with values and originating address) There must be a way to get the logs - any ideas?

Eg. this is what Etherscan shows https://etherscan.io/tx-decoder?tx=0x3e3ad35fda1fddd9e154b3860b50371a1acd2fdb4f27f897e234846522bde732 (see Emitted Events section)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冧九 2025-02-01 04:27:29

因此,我自己想到了这一点 - 我为Geth创建了一个自定义的JavaScript示踪剂,该示踪剂以3rd param传递给geth to debug_tracecall (请参阅链接提供的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;
    }
}

该示踪剂由Geth执行,用于跟踪中的每个操作。本质上是什么:

  • 检查这是 log0 log1 log2 log3 log4 evm opcodes
  • 从当前合同提取提取合同地址
  • 默认主题0 和后续主题(如果有)
  • 从内存中提取其他事件数据(注意:stack [0]是偏移,堆栈[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
}])

So I figured this myself - I created a custom JavaScript tracer for geth that is passed to geth in 3rd param to debug_traceCall (see provided API reference by the link):

{
    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;
    }
}

This tracer is executed by geth for each operation in the trace. Essentially what it does:

  • check if this is one of LOG0, LOG1, LOG2, LOG3 or LOG4 EVM opcodes
  • extract contract address from current contract
  • extract default topic0 and subsequent topics (if any)
  • extract additional event data from memory (note: stack[0] is offset, stack[1] is data size)

Passing the tracer to geth looks like this:

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
}])
找回味觉 2025-02-01 04:27:29
{
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;
}

}

我看到我们可以在这里提取主题。我认为我们还可以一起提取主题和存储。我们需要做什么

{
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;
}

}

I see that we can extract topics here. I think we can also extract topics and storage together. What do we need to do

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文