|
| 1 | +# Debug API design |
| 2 | + |
| 3 | +## Purpose |
| 4 | + |
| 5 | +The Debug API gives you access to several non-standard RPC methods, which will allow you to inspect, debug and set certain debugging flags during runtime. Also, it could help you replay all the transactions that have been executed prior and save considerable effort for your debugging process. |
| 6 | + |
| 7 | +## Goals |
| 8 | + |
| 9 | +1. **Debugging Capabilities**: Enable developers to retrieve detailed information about transactions, execution traces, and other debugging-related data. |
| 10 | +2. **Enhanced Insights**: Provide deeper insights into the behavior of smart contracts, transaction execution, and state changes on the Hedera network. |
| 11 | +3. **Ethereum Equivalent**: Makes the Hedera JSON-RPC Relay more equivalent to other providers used in the ethereum ecosystem, by providing the needed endpoints for easier debugging by developers. |
| 12 | + |
| 13 | +## Architecture |
| 14 | + |
| 15 | +New Debug API methods can be added and handled by adding debug service, which will be blueprinted by new `debug` interface added in the relay interface itself. Each method will be separate from the other and they won't overlap in terms of functionality. |
| 16 | + |
| 17 | +### Interface |
| 18 | +```javascript |
| 19 | +export interface Debug { |
| 20 | + traceTransaction(transactionHash, { tracer, tracerConfig }); |
| 21 | + |
| 22 | + getModifiedAccountsByHash(startHash, endHash); |
| 23 | + |
| 24 | + getModifiedAccountsByNumber(startNum, endNum); |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +### Service class |
| 29 | +```javascript |
| 30 | +class DebugService implements Debug{ |
| 31 | + traceTransaction(transactionHash, { tracer, tracerConfig }) { |
| 32 | + // Implementation of traceTranscation depending on passed params. |
| 33 | + } |
| 34 | + |
| 35 | + getModifiedAccountsByHash(startHash, endHash){ |
| 36 | + // Implementation of getModifiedAccountsByHash depending on passed params. |
| 37 | + } |
| 38 | + |
| 39 | + getModifiedAccountsByNumber(startNum, endNum){ |
| 40 | + // Implementation of getModifiedAccountsByNumber depending on passed params. |
| 41 | + } |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +### Method description |
| 46 | + |
| 47 | +`debug_traceTransaction` - Attempts to run the transaction in the exact same manner as it was executed on the network. |
| 48 | +This is achieved by utilizing the /api/v1/contracts/results/{transactionIdOrHash}/actions endpoint of the mirror node. |
| 49 | +The relevant fields retrieved from this endpoint are processed and formatted to generate the expected response as outlined below. |
| 50 | + |
| 51 | +#### Parameters |
| 52 | +`transactionHash` - string - This is the hash of the transaction that we want to trace. <br> |
| 53 | +`tracer` - string - to specify the type of tracer. Possible values are `callTracer` or `opcodeLogger`. In the beginning only `callTracer` will be accepted. <br> |
| 54 | +`tracerConfig` - object |
| 55 | + * One property for log tracer called `onlyTopCall`, which is a boolean. <br> |
| 56 | + * For `opcodeLogger` it can have four properties - enableMemory, disableStack, disableStorage, enableReturnData - all booleans |
| 57 | +#### Returns for callTracer |
| 58 | +`object` - trace object: |
| 59 | + |
| 60 | +1. `type` - string - CALL or CREATE |
| 61 | +2. `from` - string - address |
| 62 | +3. `to` - string - address |
| 63 | +4. `value` - string - hex-encoded amount of value transfer |
| 64 | +5. `gas` - string - hex-encoded gas provided for call |
| 65 | +6. `gasUsed` - string - hex-encoded gas used during call |
| 66 | +7. `input` - string - call data |
| 67 | +8. `output` - string - return data |
| 68 | +9. `error` - string - error, if any |
| 69 | +10. `revertReason` - string - Solidity revert reason, if any |
| 70 | +11. `calls` - []callframe - list of sub-calls |
| 71 | + |
| 72 | +#### Example Request callTracer |
| 73 | + |
| 74 | +```JSON |
| 75 | +{ |
| 76 | + "jsonrpc": "2.0", |
| 77 | + "id": 1, |
| 78 | + "method": "debug_traceTransaction", |
| 79 | + "params": |
| 80 | + [ |
| 81 | + "0x8fc90a6c3ee3001cdcbbb685b4fbe67b1fa2bec575b15b0395fea5540d0901ae", |
| 82 | + { |
| 83 | + "tracer": "callTracer" |
| 84 | + } |
| 85 | + ] |
| 86 | +} |
| 87 | +``` |
| 88 | +#### Example Response |
| 89 | +```JSON |
| 90 | +{ |
| 91 | + "jsonrpc": "2.0", |
| 92 | + "id": 1, |
| 93 | + "result": { |
| 94 | + "type": "CALL", |
| 95 | + "from": "0x5067c042e35881843f2b31dfc2db1f4f272ef48c", |
| 96 | + "to": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 97 | + "value": "0x0", |
| 98 | + "gas": "0x17459", |
| 99 | + "gasUsed": "0x166cb", |
| 100 | + "input": "0x0f5287b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000000000000000000000000000000000000000000167c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e400000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000006eca0000", |
| 101 | + "output": "0x000000000000000000000000000000000000000000000000000000000001371e", |
| 102 | + "calls": [ |
| 103 | + { |
| 104 | + "type": "DELEGATECALL", |
| 105 | + "from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 106 | + "to": "0x76364611e457b1f97cd58ffc332ddc7561a193f6", |
| 107 | + "gas": "0x15bc0", |
| 108 | + "gasUsed": "0x1538e", |
| 109 | + "input": "0x0f5287b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000000000000000000000000000000000000000000167c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e400000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000006eca0000", |
| 110 | + "output": "0x000000000000000000000000000000000000000000000000000000000001371e", |
| 111 | + "calls": [ |
| 112 | + { |
| 113 | + "type": "STATICCALL", |
| 114 | + "from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 115 | + "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 116 | + "gas": "0x123bb", |
| 117 | + "gasUsed": "0x25c0", |
| 118 | + "input": "0x313ce567", |
| 119 | + "output": "0x0000000000000000000000000000000000000000000000000000000000000006", |
| 120 | + "calls": [ |
| 121 | + { |
| 122 | + "type": "DELEGATECALL", |
| 123 | + "from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 124 | + "to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf", |
| 125 | + "gas": "0x10357", |
| 126 | + "gasUsed": "0x94d", |
| 127 | + "input": "0x313ce567", |
| 128 | + "output": "0x0000000000000000000000000000000000000000000000000000000000000006" |
| 129 | + } |
| 130 | + ] |
| 131 | + }, |
| 132 | + { |
| 133 | + "type": "STATICCALL", |
| 134 | + "from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 135 | + "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 136 | + "gas": "0xf9d6", |
| 137 | + "gasUsed": "0xcf3", |
| 138 | + "input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585", |
| 139 | + "output": "0x00000000000000000000000000000000000000000000000000001691e551e115", |
| 140 | + "calls": [ |
| 141 | + { |
| 142 | + "type": "DELEGATECALL", |
| 143 | + "from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 144 | + "to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf", |
| 145 | + "gas": "0xf315", |
| 146 | + "gasUsed": "0x9e1", |
| 147 | + "input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585", |
| 148 | + "output": "0x00000000000000000000000000000000000000000000000000001691e551e115" |
| 149 | + } |
| 150 | + ] |
| 151 | + }, |
| 152 | + { |
| 153 | + "type": "CALL", |
| 154 | + "from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 155 | + "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 156 | + "value": "0x0", |
| 157 | + "gas": "0xe796", |
| 158 | + "gasUsed": "0x5f48", |
| 159 | + "input": "0x23b872dd0000000000000000000000005067c042e35881843f2b31dfc2db1f4f272ef48c0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585000000000000000000000000000000000000000000000000000000001debea42", |
| 160 | + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", |
| 161 | + "calls": [ |
| 162 | + { |
| 163 | + "type": "DELEGATECALL", |
| 164 | + "from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 165 | + "to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf", |
| 166 | + "gas": "0xe115", |
| 167 | + "gasUsed": "0x5c2d", |
| 168 | + "input": "0x23b872dd0000000000000000000000005067c042e35881843f2b31dfc2db1f4f272ef48c0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585000000000000000000000000000000000000000000000000000000001debea42", |
| 169 | + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" |
| 170 | + } |
| 171 | + ] |
| 172 | + }, |
| 173 | + { |
| 174 | + "type": "STATICCALL", |
| 175 | + "from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 176 | + "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 177 | + "gas": "0x857c", |
| 178 | + "gasUsed": "0x523", |
| 179 | + "input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585", |
| 180 | + "output": "0x00000000000000000000000000000000000000000000000000001692033dcb57", |
| 181 | + "calls": [ |
| 182 | + { |
| 183 | + "type": "DELEGATECALL", |
| 184 | + "from": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", |
| 185 | + "to": "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf", |
| 186 | + "gas": "0x808c", |
| 187 | + "gasUsed": "0x211", |
| 188 | + "input": "0x70a082310000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585", |
| 189 | + "output": "0x00000000000000000000000000000000000000000000000000001692033dcb57" |
| 190 | + } |
| 191 | + ] |
| 192 | + }, |
| 193 | + { |
| 194 | + "type": "CALL", |
| 195 | + "from": "0x3ee18b2214aff97000d974cf647e7c347e8fa585", |
| 196 | + "to": "0x98f3c9e6e3face36baad05fe09d375ef1464288b", |
| 197 | + "value": "0x0", |
| 198 | + "gas": "0x4f9f", |
| 199 | + "gasUsed": "0x46c6", |
| 200 | + "input": "0xb19a437e000000000000000000000000000000000000000000000000000000006eca00000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000008501000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000267c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e4000100000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000", |
| 201 | + "output": "0x000000000000000000000000000000000000000000000000000000000001371e", |
| 202 | + "calls": [ |
| 203 | + { |
| 204 | + "type": "DELEGATECALL", |
| 205 | + "from": "0x98f3c9e6e3face36baad05fe09d375ef1464288b", |
| 206 | + "to": "0x8c0041566e0bc27efe285a9e98d0b4217a46809c", |
| 207 | + "gas": "0x3b88", |
| 208 | + "gasUsed": "0x3377", |
| 209 | + "input": "0xb19a437e000000000000000000000000000000000000000000000000000000006eca00000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000008501000000000000000000000000000000000000000000000000000000001debea42000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000267c46aa713cfe47608dd1c16f8a0325208df084c3cbebf9f366ad0eafc2653e4000100000000000000000000000000000000000000000000000000000000001e8542000000000000000000000000000000000000000000000000000000", |
| 210 | + "output": "0x000000000000000000000000000000000000000000000000000000000001371e" |
| 211 | + } |
| 212 | + ] |
| 213 | + } |
| 214 | + ] |
| 215 | + } |
| 216 | + ] |
| 217 | + } |
| 218 | +} |
| 219 | +``` |
| 220 | +#### Returns for opcodeLogger |
| 221 | + |
| 222 | +`object` - trace object: |
| 223 | +1. `pc` - int - program counter |
| 224 | +2. `op` - string - opcode to be executed |
| 225 | +3. `gas`- int - remaining gas |
| 226 | +4. `gasCost`- int - cost for executing op |
| 227 | +5. `memory` - string[] - EVM memory. Enabled via enableMemory |
| 228 | +6. `memSize`- int - Size of memory |
| 229 | +7. `stack`- int[] - EVM stack. Disabled via disableStack |
| 230 | +8. `returnData` - string[] - Last call's return data. Enabled via enableReturnData |
| 231 | +9. `storage` - map[hash]hash - Storage slots of current contract read from and written to. Only emitted for SLOAD and SSTORE. Disabled via disableStorage |
| 232 | +10. `depth` - int - Current call depth |
| 233 | +11. `refund` - int - Refund counter |
| 234 | +12. `error` - string - Error message if any |
| 235 | + |
| 236 | +#### Example Request opcodeLogger |
| 237 | +```JSON |
| 238 | +{ |
| 239 | + "jsonrpc": "2.0", |
| 240 | + "id": 1, |
| 241 | + "method": "debug_traceTransaction", |
| 242 | + "params": |
| 243 | + [ |
| 244 | + "0x8fc90a6c3ee3001cdcbbb685b4fbe67b1fa2bec575b15b0395fea5540d0901ae", |
| 245 | + { |
| 246 | + "tracer": "opcodeLogger" |
| 247 | + } |
| 248 | + ] |
| 249 | +} |
| 250 | +``` |
| 251 | +#### Example Response |
| 252 | +```JSON |
| 253 | +{ |
| 254 | + "jsonrpc": "2.0", |
| 255 | + "id": 1, |
| 256 | + "result": { |
| 257 | + "gas": 85301, |
| 258 | + "returnValue": "", |
| 259 | + "structLogs": [{ |
| 260 | + "depth": 1, |
| 261 | + "error": "", |
| 262 | + "gas": 162106, |
| 263 | + "gasCost": 3, |
| 264 | + "memory": null, |
| 265 | + "op": "PUSH1", |
| 266 | + "pc": 0, |
| 267 | + "stack": [], |
| 268 | + "storage": {} |
| 269 | + }, |
| 270 | + /* skip */ |
| 271 | + { |
| 272 | + "depth": 1, |
| 273 | + "error": "", |
| 274 | + "gas": 100000, |
| 275 | + "gasCost": 0, |
| 276 | + "memory": ["0000000000000000000000000000000000000000000000000000000000000006", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000060"], |
| 277 | + "op": "STOP", |
| 278 | + "pc": 120, |
| 279 | + "stack": ["00000000000000000000000000000000000000000000000000000000d67cbec9"], |
| 280 | + "storage": { |
| 281 | + "0000000000000000000000000000000000000000000000000000000000000004": "8241fa522772837f0d05511f20caa6da1d5a3209000000000000000400000001", |
| 282 | + "0000000000000000000000000000000000000000000000000000000000000006": "0000000000000000000000000000000000000000000000000000000000000001", |
| 283 | + "f652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f": "00000000000000000000000002e816afc1b5c0f39852131959d946eb3b07b5ad" |
| 284 | + } |
| 285 | + }] |
| 286 | + } |
| 287 | +} |
| 288 | +``` |
| 289 | + |
| 290 | +## Error Codes |
| 291 | + |
| 292 | +| Error Codes | Error message | Solution | |
| 293 | +| :---------: | :-------------------------: | :-----------------------------------------------------------------: | |
| 294 | +| 32000 | Transaction not found. | Occurs when user is trying to trace non-existing transaction. | |
| 295 | +| 32001 | Block hash not found. | Occurs when user is trying to get modified accounts for non-existing block hash. | |
| 296 | +| 32002 | Block number not found. | Occurs when user is trying to get modified accounts for non-existing block number. | |
| 297 | + |
| 298 | +## Limits |
| 299 | +1. Trying to get modified accounts for more than ex. 100 blocks. Env. variable can be `DEBUG_MODIFIED_ACCOUNTS_LIMIT` |
| 300 | + |
| 301 | + |
| 302 | +## Metric Capturing |
| 303 | + |
| 304 | +Capture metrics for the following: |
| 305 | + |
| 306 | +1. Record each invocation of all debug API methods and maintain a cumulative count. |
| 307 | +2. Log every success or fail for each new API method and maintain a cumulative count. |
| 308 | + |
| 309 | +## Tests |
| 310 | +The following test cases should be covered but additional tests would be welcome. |
| 311 | + |
| 312 | +1. Test `debug_traceTransaction` with `callTracer` and `onlyTopCall` set to true. |
| 313 | +2. Test `debug_traceTransaction` with `callTracer` and `onlyTopCall` set to false. |
| 314 | +3. Test `debug_traceTransaction` with `opcodeLogger` and all the tracerConfig values set to false. |
| 315 | +4. Test `debug_traceTransaction` with `opcodeLogger` and all the tracerConfig values set to true. |
| 316 | +5. Test `debug_traceTransaction` with `opcodeLogger` and all different combinations of tracerConfig values. |
| 317 | +6. Test `debug_traceTransaction` with hashes for the different types of transactions e.g Legacy, 1559, 2930. |
| 318 | +7. Case where transaction is not found for `debug_traceTransaction`. |
| 319 | + |
| 320 | +## Deployment |
| 321 | + |
| 322 | +Debug API will run alongside the already available HTTP server. |
| 323 | + |
| 324 | +## Answered Questions |
| 325 | + |
| 326 | +1. What is the purpose of Debug API and what kind of goals adding it fulfil ? |
| 327 | +2. What new interfaces and classes would it need ? |
| 328 | +3. What new endpoints will be exposed ? |
| 329 | +4. What kind of parameters each method accepts ? |
| 330 | +5. What kind of response is expected to return ? |
| 331 | +6. What is the expected request and response ? |
| 332 | +7. What limits should be introduced ? |
| 333 | +8. What metrics should be captured ? |
| 334 | + |
| 335 | + |
| 336 | +## Tasks (in suggested order): |
| 337 | + |
| 338 | +#### Milestone 1 |
| 339 | + |
| 340 | +1. Finalize design document |
| 341 | + |
| 342 | +#### Milestone 2 |
| 343 | + |
| 344 | +1. Implement new interfaces and classes. |
| 345 | +2. Implement `debug_traceTransaction`. |
| 346 | +3. Add needed acceptance tests for `debug_traceTransaction`. |
| 347 | + |
| 348 | +#### Milestone 3 |
| 349 | +1. Implement `debug_getModifiedAccountsByHash`. |
| 350 | +2. Implement `debug_getModifiedAccountsByNumber`. |
| 351 | +3. Add needed acceptance tests for both new methods. |
0 commit comments