HIP-801: Add support for debug_traceTransaction RPC API
Author | Ivan Kavaldzhiev, Stoyan Panayotov |
---|---|
Working Group | Steven Sheehy, Nana Essilfie-Conduah, Danno Ferrin, David Bakin, Georgi Lazarov |
Discussions-To | https://github.com/hashgraph/hedera-improvement-proposal/discussions/802 |
Status | Final ⓘ |
Needs Council Approval | No ⓘ |
Review period ends ⓘ | Wed, 10 Jan 2024 07:00:00 +0000 |
Type | Standards Track ⓘ |
Category | Mirror ⓘ |
Created | 2023-08-31 |
Updated | 2024-07-10 |
Release | v0.108.0 |
Table of Contents
Abstract
This HIP describes the mechanism of adding initial support for handling DEBUG requests for historical EVM transaction to the JSON-RPC Relay and the Hedera Mirror Node.
Motivation
Ethereum nodes can be configured to enable support for debugging historical transactions and thus producing a detailed output log with information related to the transaction execution. Hedera’s current architecture does not support this functionality.
Ongoing efforts to achieve Ethereum equivalence require Hedera to enable debugging of historical EVM transactions. This enhancement would improve Hedera’s interoperability with different Ethereum tools and providers, including Remix, Hardhat, Ganache, Geth, Alchemy, etc.
Rationale
Hedera’s architecture consists of three types of nodes:
- JSON-RPC Relay nodes expose a JSON-RPC API similar to the one that Ethereum nodes use. The Relay handles user requests by either forwarding them to a Consensus or Mirror node.
- Consensus nodes execute transactions, produce transaction records, and maintain the world state.
- Mirror nodes ingest the record files produced by the consensus nodes, maintain a “mirror” of the world state, and provide an API for queries and simulations which can work with the current, or a historical state.
Enhancing the JSON-RPC Relay and the Mirror Archive Node with debug APIs will allow users to replay historical transactions and inspect and analyse them by having detailed information for the execution based on a tracer type that is specified in the request.
Add support of debug_traceTransaction
in the JSON-RPC Relay which returns detailed information for a historical transaction that was executed in the past.
The JSON-RPC Relay will in turn make a request to the Mirror Node REST API which will be enhanced with a new endpoint that returns opcode logs. The Mirror node already has an endpoint that returns call traces.
User stories
- As a user, I want to perform
debug_traceTransaction
calls to explore the op code traces for a historical transaction. - As a user, I want to perform
debug_traceTransaction
calls to explore the sub calls that were executed for a historical transaction.
Specification
In order to enable trace calls, we should meet some requirements on the Mirror Archive Node side:
- Support EVM versioning, so that we use the appropriate EVM version for a specific historical block.
- Use full mirror node, keeping state from the genesis block and configured with enabled ingest of
CONTRACT_BYTECODE
,CONTRACT_STATE_CHANGE
andCONTRACT_ACTION
sidecar types. - Re-execute historical transactions using the information for state values read from the downloaded
CONTRACT_STATE_CHANGE
sidecar record files.
JSON-RPC Relay Trace API
The JSON-RPC Relay will be enhanced with a debug_traceTransaction
endpoint. The endpoint has one required parameter - transactionHash
- and also accepts a list of properties - a tracer
, and a list of configuration flags
.
Example payload for callTracer
type:
{
"jsonrpc": "2.0",
"method": "debug_traceTransaction",
"params": [
// The transaction hash.
"0x123...",
{
tracer: "callTracer"
}
],
"id": 0
}
Output: detailed information for the transaction execution with list of actions
Example output:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"type": "CALL",
"from": "0x5067c042e35881843f2b31dfc2db1f4f272ef48c",
"to": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"value": "0x0",
"gas": "0x17459",
"gasUsed": "0x166cb",
"input": "0x0f5287b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000000000000000000000000000000000000000000167c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e400000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000006eca0000",
"output": "0x000000000000000000000000000000000000000000000000000000000001371e",
"calls": [
{
"type": "DELEGATECALL",
"from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"to": "0x76364611e457b1f97cd58ffc332ddc7561a193f6",
"gas": "0x15bc0",
"gasUsed": "0x1538e",
"input": "0x0f5287b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000000000000000000000000000000000000000000167c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e400000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000006eca0000",
"output": "0x000000000000000000000000000000000000000000000000000000000001371e",
"calls": [
{
"type": "STATICCALL",
"from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"gas": "0x123bb",
"gasUsed": "0x25c0",
"input": "0x313ce567",
"output": "0x0000000000000000000000000000000000000000000000000000000000000006",
"calls": [
{
"type": "DELEGATECALL",
"from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf",
"gas": "0x10357",
"gasUsed": "0x94d",
"input": "0x313ce567",
"output": "0x0000000000000000000000000000000000000000000000000000000000000006"
}
]
},
{
"type": "STATICCALL",
"from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"gas": "0xf9d6",
"gasUsed": "0xcf3",
"input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585",
"output": "0x00000000000000000000000000000000000000000000000000001691e551e115",
"calls": [
{
"type": "DELEGATECALL",
"from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf",
"gas": "0xf315",
"gasUsed": "0x9e1",
"input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585",
"output": "0x00000000000000000000000000000000000000000000000000001691e551e115"
}
]
},
{
"type": "CALL",
"from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"value": "0x0",
"gas": "0xe796",
"gasUsed": "0x5f48",
"input": "0x23b872dd0000000000000000000000005067c042e35881843f2b31dfc2db1f4f272ef48c0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585000000000000000000000000000000000000000000000000000000001debea42",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"calls": [
{
"type": "DELEGATECALL",
"from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf",
"gas": "0xe115",
"gasUsed": "0x5c2d",
"input": "0x23b872dd0000000000000000000000005067c042e35881843f2b31dfc2db1f4f272ef48c0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585000000000000000000000000000000000000000000000000000000001debea42",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
]
},
{
"type": "STATICCALL",
"from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"gas": "0x857c",
"gasUsed": "0x523",
"input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585",
"output": "0x00000000000000000000000000000000000000000000000000001692033dcb57",
"calls": [
{
"type": "DELEGATECALL",
"from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf",
"gas": "0x808c",
"gasUsed": "0x211",
"input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585",
"output": "0x00000000000000000000000000000000000000000000000000001692033dcb57"
}
]
},
{
"type": "CALL",
"from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"to": "0x98f3c9e6e3face36baad05fe09d375ef1464288b",
"value": "0x0",
"gas": "0x4f9f",
"gasUsed": "0x46c6",
"input": "0xb19a437e000000000000000000000000000000000000000000000000000000006eca00000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000008501000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000267c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e4000100000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000",
"output": "0x000000000000000000000000000000000000000000000000000000000001371e",
"calls": [
{
"type": "DELEGATECALL",
"from": "0x98f3c9e6e3face36baad05fe09d375ef1464288b",
"to": "0x8c0041566e0bc27efe285a9e98d0b4217a46809c",
"gas": "0x3b88",
"gasUsed": "0x3377",
"input": "0xb19a437e000000000000000000000000000000000000000000000000000000006eca00000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000008501000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000267c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e4000100000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000",
"output": "0x000000000000000000000000000000000000000000000000000000000001371e"
}
]
}
]
}
]
}
}
Example payload for a transaction execution with opcodeLogger
and disableMemory
, disableStack
and disableStorage
flags not set (default values of false
):
{
"jsonrpc": "2.0",
"method": "debug_traceTransaction",
"params": [
// The transaction hash.
"0x123...",
{
tracer: "opcodeLogger"
}
],
"id": 0
}
Output for the detailed transaction execution with memory
, stack
and storage
information
Example output:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"gas": 52139,
"failed": false,
"returnValue": "0000000000000000000000000000000000000000000000000000000000000001",
"structLogs": [
{
"pc": 542,
"op": "SWAP2",
"gas": 4419,
"gasCost": 3,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 543,
"op": "POP",
"gas": 4416,
"gasCost": 2,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 544,
"op": "PUSH1",
"gas": 4414,
"gasCost": 3,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 546,
"op": "SLOAD",
"gas": 4411,
"gasCost": 2100,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000001"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 547,
"op": "SWAP2",
"gas": 2311,
"gasCost": 3,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 548,
"op": "POP",
"gas": 2308,
"gasCost": 2,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
}
]
}
}
Example payload for a transaction execution with opcodeLogger
and disableMemory
set to true
:
{
"jsonrpc": "2.0",
"method": "debug_traceTransaction",
"params": [
// The transaction hash.
"0x123...",
{
tracer: "opcodeLogger",
disableMemory: true
}
],
"id": 0
}
Output: detailed information for the transaction execution without memory
information
Example output:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"gas": 24116,
"failed": false,
"returnValue": "",
"structLogs": [
{
"pc": 538,
"op": "SSTORE",
"gas": 4622,
"gasCost": 100,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": null,
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 539,
"op": "PUSH1",
"gas": 4522,
"gasCost": 3,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000014"
],
"memory": null,
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 541,
"op": "SLOAD",
"gas": 4519,
"gasCost": 100,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": null,
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
}
]
}
}
Example payload for a transaction execution with opcodeLogger
and disableStack
flag set to true
{
"jsonrpc": "2.0",
"method": "debug_traceTransaction",
"params": [
// The transaction hash.
"0x123...",
{
tracer: "opcodeLogger",
disableStack: true
}
],
"id": 0
}
Output: detailed information for the transaction execution without stack
information
Example output:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"gas": 24116,
"failed": false,
"returnValue": "",
"structLogs": [
{
"pc": 541,
"op": "SLOAD",
"gas": 4519,
"gasCost": 100,
"depth": 1,
"stack": null,
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 542,
"op": "SWAP2",
"gas": 4419,
"gasCost": 3,
"depth": 1,
"stack": null,
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 543,
"op": "POP",
"gas": 4416,
"gasCost": 2,
"depth": 1,
"stack": null,
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 544,
"op": "PUSH1",
"gas": 4414,
"gasCost": 3,
"depth": 1,
"stack": null,
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 546,
"op": "SLOAD",
"gas": 4411,
"gasCost": 2100,
"depth": 1,
"stack": null,
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
}
]
}
}
Example payload for a transaction execution with opcodeLogger
and disableStorage
flag set to true
{
"jsonrpc": "2.0",
"method": "debug_traceTransaction",
"params": [
// The transaction hash.
"0x123...",
{
tracer: "opcodeLogger",
disableStorage: true
}
],
"id": 0
}
Output: detailed information for the transaction execution without storage
information
Example output:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"gas": 24116,
"failed": false,
"returnValue": "",
"structLogs": [
{
"pc": 546,
"op": "SLOAD",
"gas": 4411,
"gasCost": 2100,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000001"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": null,
"reason": null
},
{
"pc": 547,
"op": "SWAP2",
"gas": 2311,
"gasCost": 3,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": null,
"reason": null
},
{
"pc": 548,
"op": "POP",
"gas": 2308,
"gasCost": 2,
"depth": 1,
"stack": [
"00000000000000000000000000000000000000000000000000000000094f6253",
"00000000000000000000000000000000000000000000000000000000000000a5",
"0000000000000000000000008fa74f0a79112c8c46c8e6739eb8cc929083298c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000014",
"0000000000000000000000000000000000000000000000000000000000000014"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": null,
"reason": null
}
]
}
}
Mirror node Trace REST API
The JSON-RPC Relay will in turn call the Mirror Node REST API. Depending on the tracer
that was provided as an input parameter to the debug_traceTransaction
RPC endpoint, one of two Mirror node endpoints will be called:
- For
debug_traceTransaction
called withcallTracer
, the Mirror Node/api/v1/contracts/results/{transactionIdOrHash}/actions
endpoint will be called. - For
debug_traceTransaction
called withopcodeLogger
, the Mirror Node/api/v1/contracts/results/{transactionIdOrHash}/opcodes
endpoint will be called.
The /actions
endpoint is already implemented.
This section will specify the /opcodes
endpoint.
/api/v1/contracts/results/{transactionIdOrHash}/opcodes
Input parameters: The endpoint will accept three optional parameters:
// Boolean flag controlling whether to return stack information. Defaults to `true`.
stack: <bool>
// Boolean flag controlling whether to return memory information. Defaults to `false`.
memory: <bool>
// Boolean flag controlling whether to return storage information. Defaults to `false`.
storage: <bool>
Disabling stack, memory and storage traces will significantly reduce the size of the opcode related data. This would be helpful for reducing the network bandwidth.
Output: detailed information for the transaction execution with stack
, memory
and storage
set to true
Example output:
{
"address": "0x8fa74f0a79112c8c46c8e6739eb8cc929083298c",
"contract_id": "0.0.1054",
"gas": 52139,
"failed": false,
"return_value": "0x0000000000000000000000000000000000000000000000000000000000000001",
"opcodes": [
{
"pc": 1273,
"op": "PUSH1",
"gas": 2731,
"gas_cost": 3,
"depth": 2,
"stack": [
"000000000000000000000000000000000000000000000000000000004700d305",
"00000000000000000000000000000000000000000000000000000000000000a7",
"0000000000000000000000000000000000000000000000000000000000000000",
"000000000000000000000000000000000000000000000000000000000000016c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000004",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000521",
"0000000000000000000000000000000000000000000000000000000000000024"
],
"memory": [
"4e487b7100000000000000000000000000000000000000000000000000000000",
"0000001200000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {},
"reason": null
},
{
"pc": 1275,
"op": "REVERT",
"gas": 2728,
"gas_cost": 0,
"depth": 2,
"stack": [
"000000000000000000000000000000000000000000000000000000004700d305",
"00000000000000000000000000000000000000000000000000000000000000a7",
"0000000000000000000000000000000000000000000000000000000000000000",
"000000000000000000000000000000000000000000000000000000000000016c",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000004",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000521",
"0000000000000000000000000000000000000000000000000000000000000024",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": [
"4e487b7100000000000000000000000000000000000000000000000000000000",
"0000001200000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {},
"reason": "0x4e487b710000000000000000000000000000000000000000000000000000000000000012"
},
{
"pc": 682,
"op": "SWAP3",
"gas": 2776,
"gas_cost": 3,
"depth": 1,
"stack": [
"000000000000000000000000000000000000000000000000000000000135b7d0",
"00000000000000000000000000000000000000000000000000000000000000a0",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000096769c2405eab9fdc59b25b178041e517ddc0f32",
"000000000000000000000000000000000000000000000000000000004700d305",
"0000000000000000000000000000000000000000000000000000000000000084",
"0000000000000000000000000000000000000000000000000000000000000000"
],
"memory": [
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"4e487b7100000000000000000000000000000000000000000000000000000000"
],
"storage": {},
"reason": null
}
]
}
Output: detailed information for the transaction execution with stack
set to false
Example output:
{
"address": "0x8fa74f0a79112c8c46c8e6739eb8cc929083298c",
"contract_id": "0.0.1059",
"gas": 24116,
"failed": false,
"return_value": "",
"opcodes": [
{
"pc": 541,
"op": "SLOAD",
"gas": 4519,
"gas_cost": 100,
"depth": 1,
"stack": [],
"memory": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 542,
"op": "SWAP2",
"gas": 4419,
"gas_cost": 3,
"depth": 1,
"stack": [],
"memory": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 543,
"op": "POP",
"gas": 4416,
"gas_cost": 2,
"depth": 1,
"stack": [],
"memory": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 544,
"op": "PUSH1",
"gas": 4414,
"gas_cost": 3,
"depth": 1,
"stack": [],
"memory": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
},
{
"pc": 546,
"op": "SLOAD",
"gas": 4411,
"gas_cost": 2100,
"depth": 1,
"stack": [],
"memory": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000080"
],
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000014"
},
"reason": null
}
]
}
For providing output formatted by opcodeLogger
, the Mirror node will re-execute the transaction using the state from the contract_state_changes
sidecars produced by the consensus nodes. In this way, we can have a track on all of the storage/memory information and the entire trace of opcodes that were executed during the replay.
⚠️ The output generated from the opcodeLogger is very verbose. The /opcodes endpoint will be disabled by default and can be enabled by users of the Hedera Local node or users running their own Mirror Nodes.
Performance considerations
Since the output generated by this tracer might be quite large, we could add a flag
for enabling different type of tracers, based on the usage and setup of the Mirror Archive Node. This would give us control and flexibility. A user would be able to enable opcodeLogger
in a local environment and get the full response data. The default tracer enabled by default on production, could be the callTracer
.
Since this API would use a lot of resources like memory, CPU usage or requests to the DB, we can have a very strict rate limit that can be configurable based on the context of usage. We can use 1 call per second as initial rate limit. If a given provider want to run the Archive Node with more resources, the rate limit could be increased.
Backward Compatibility
Mirror Nodes that will support the new REST APIs should have enabled importing of CONTRACT_BYTECODE , CONTRACT_STATE_CHANGE and CONTRACT_ACTION sidecar types. Otherwise, executing the endpoints would result in missing runtime bytecode, contract storage data or contract actions information. In addition, the mirror node should have full historical support (keeping state from the first block), so that it could replay transactions from the entire lifetime of the system.
Security Implications
There would be some security implementations for Mirror Nodes. Since the debug_trace RPC calls would be free of charge and the Mirror Nodes don’t have gas based throttle mechanism, some attack vectors would be possible. The Nodes could be overloaded with huge amount of calls or invoke smart contract methods with huge gas usage, both of which might take more system usage and slow down the network for other users. A rate limit per IP address and rate limit per Kubernetes pod will be implemented, so that the load put on Mirror Nodes to be balanced.
How to Teach This
Respective documentation will be added.
References
https://hips.hedera.com/hip/hip-584
https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
https://ganache.dev/#debug_traceTransaction
https://book.getfoundry.sh/reference/anvil/?highlight=debug_tra#description
https://docs.alchemy.com/reference/debug-tracetransaction
https://hardhat.org/hardhat-network/docs/reference#debug_tracetransaction
https://hardhat.org/hardhat-network/docs/overview#the-debug-tracetransaction-method
Open Issues
None.
Copyright/license
This document is licensed under the Apache License, Version 2.0 – see LICENSE or (https://www.apache.org/licenses/LICENSE-2.0)
Citation
Please cite this document as: