Comment on page
Transaction Tracing API
Last updated 8 March 2023
Transaction tracing enables deeper look into smart contract inner calls and account creation calls. It is inspired with the OpenEthereum (Parity) node trace module, which is used in lot of projects.
For now, following JSON-RPC methods are implemented:
- trace_block
- trace_filter
- trace_get
- trace_transaction
Currently it is deployed on endpoints for Fantom mainnet and testnet:
Because of stored traces, tracing node requires little bit bigger storage (around 1/4 more). For more information, please follow README.md file in txtracing branch.
You can check out and build tx tracing release version from git repository (release/txtracing/1.1.2-rc.5): https://github.com/Fantom-foundation/go-opera/tree/release/txtracing/1.1.2-rc.5.
It's recommended to launch new node from scratch with CLI option
--tracenode
as this flag has to be set to use stored transaction traces.$ opera --genesis /path/to/genesis.g --tracenode
Enable JSON-RPC API with option
trace
--http.api=eth,web3,net,ftm,trace
A complete example command
opera \
--genesis=/datadir/mainnet-5577-full-mpt.g \
--port=5050 \
--maxpeers=200 \
--datadir=/datadir \
--http \
--http.addr=0.0.0.0 \
--http.port=18545 \
--http.corsdomain="*" \
--http.vhosts="*" \
--ws \
--ws.addr=0.0.0.0 \
--ws.port=18546 \
--ws.origins="*" \
--nousb \
--db.preset pbl-1 \
--tracenode \
--http.api=eth,web3,net,ftm,trace
For the first start of the node, you have to specify the genesis file, which defines blockchain and contains his history.
Please select a genesis file ( from this site) with full history ("full-mpt" in filename), if you need transaction traces for history states.
Transaction traces are created on the node while processing transactions and then stored into node database. You can export these traces and import them on other nodes (this feature was introduced since version release/txtracing/1.1.0-rc.4.1). This was added to be able to react to some changes which could change content of the traces without need of doing a full sync of the blockchain again.
Another way is to delete traces, which are then recreated with next request to them from the rpc api. This option is not recomended as the recreating of the traces in this manner is slower with first request to non-existent transaction trace.
All these operations has to be done with node which is stopped as it is changing data in the core database. For export and delete, you can specify block range, which is narrowing amount of processed transaction traces.
The command for export is
export txtraces <filename.gz> <from block> <to block>
opera --tracenode --datadir=/path/to/opera/datadir export txtraces filename.gz 10000000 11000000
For import it is
import txtraces <filename.gz>
opera --tracenode --datadir=/path/to/opera/datadir import txtraces filename.gz
With delete there is no file specified
delete txtraces <from block> <to block>
opera --tracenode --datadir=/path/to/opera/datadir delete txtraces 10000000 11000000
This section contains information about migrating transaction tracing api node to newer go-opera version. It will mention, if you need to resync the node from scratch (because of stored transaction traces) or if node needs only the update of the code to migrate from previous version.
- release/txtracing/1.1.2-rc.2 - don't use this version
When using version 1.1.2 you need to add db.preset argument for starting opera command. You can see options for this parameters with
opera help
command. For standard conditions, please use this option:- db.preset=ldb-1
Returns traces created at given block.
- 1.
Quantity
orTag
- Integer of a block number, or the string'earliest'
,'latest'
or'pending'
.
params: [
"0x22DB3E" // 2284350
]
Array
- Block traces.
Request
curl --data '{"method":"trace_block","params":["0x22DB3E"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST https://rpcapi-tracing.fantom.network
Response
{
"jsonrpc":"2.0",
"id":1,
"result":[
{
"action":{
"callType":"call",
"from":"0x740c82e9009738b3ab6080e89da964a4704a6cba",
"to":"0xd4e0aca70d1fe8979e7e1015b30a2085803d0244",
"value":"0x0",
"gas":"0x689e",
"input":"0x6930fd2a0000000000000000000000000000000000000000000000000000000000000608"
},
"blockHash":"0x00000ce000001cf16b859ccacc281e32e2c17730ce48b3498bf1f7f05d90fe5c",
"blockNumber":2284350,
"result":{
"gasUsed":"0x688f",
"output":"0x"
},
"subtraces":1,
"traceAddress":[
],
"transactionHash":"0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",
"transactionPosition":0,
"type":"call"
},
{
"action":{
"callType":"delegatecall",
"from":"0xd4e0aca70d1fe8979e7e1015b30a2085803d0244",
"to":"0x1125eabf1eba69ef00266b7a038141474d8afff1",
"value":"0x0",
"gas":"0x125c",
"input":"0x6930fd2a0000000000000000000000000000000000000000000000000000000000000608"
},
"blockHash":"0x00000ce000001cf16b859ccacc281e32e2c17730ce48b3498bf1f7f05d90fe5c",
"blockNumber":2284350,
"result":{
"gasUsed":"0xf62",
"output":"0x"
},
"subtraces":0,
"traceAddress":[
0
],
"transactionHash":"0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",
"transactionPosition":0,
"type":"call"
}
]
}
Returns all traces of given transaction
- 1.
Hash
- Transaction hash
params: ["0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac"]
Array
- Traces of given transaction
Request
curl --data '{"method":"trace_transaction","params":["0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST https://rpcapi-tracing.fantom.network
Returns traces matching given filter
- 1.
Object
- The filter objectfromBlock
:Quantity
orTag
- (optional) From this block.toBlock
:Quantity
orTag
- (optional) To this block.fromAddress
:Array
- (optional) Sent from these addresses.toAddress
:Address
- (optional) Sent to these addresses.after
:Quantity
- (optional) The offset trace numbercount
:Quantity
- (optional) Integer number of traces to display in a batch.
params: [{
"fromBlock": "0x22DB29", // 2284329
"toBlock": "0x22DB34", // 2284340
"toAddress": ["0xda7a001b254cd22e46d3eab04d937489c93174c3"],
"after": 20,
"count": 10
}]
Array
- Traces matching given filter
Request
curl -H "Content-Type: application/json" -X POST --data '{"method":"trace_filter","params":[{"fromBlock":"0x22DB29", "toBlock":"0x22DB34", "fromAddress":["0xda7a001b254cd22e46d3eab04d937489c93174c3"], "count":10, "after":20}],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network
Returns trace at given position.
- 1.
Hash
- Transaction hash. - 2.
Array
- Index positions of the traces.
params: [
"0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",
["0x0"]
]
Object
- Trace object
Request
curl -H "Content-Type: application/json" -X POST --data '{"method":"trace_get","params":["0x58846dd2a9fa3ea9e700838db48fd9829ebbc95e103142ac7968f658fd1ed3ac",["0x0"]],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network
The method returns a full stack trace of all invoked opcodes of all transaction that were included in a block identified by a block number. The parent of this block must be present or it will fail.
- 1.
Number
- Block number to be traced. - 2.
Object
- (optional) Options for the trace.tracer
:String
- (optional) Setting this will enable JavaScript-based transaction tracing, described below.timeout
:String
- (optional) Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Check documentation for ParseDuration for valid values.
Array
- A trace object per transaction inside the block.
curl -H "Content-Type: application/json" -X POST --data '{"method":"debug_traceBlockByNumber","params":["0x2F5DD49"],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network
The method returns a full stack trace of all invoked opcodes of all transaction that were included in a block identified by a hash. The parent of this block must be present or it will fail.
- 1.
Hash
- Hash of the block to be traced. - 2.
Object
- (optional) Options for the trace.tracer
:String
- (optional) Setting this will enable JavaScript-based transaction tracing, described below.timeout
:String
- (optional) Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Check documentation for ParseDuration for valid values.
Array
- A trace object per transaction inside thw block.
curl -H "Content-Type: application/json" -X POST --data '{"method":"debug_traceBlockByHash","params":["0x0002825a0000008c2bc8a86b966c34797db3c21916ca033f4bd68461801217d0"],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network
The method will attempt to run the transaction in the exact same manner as it was executed on the network. It will replay any transaction that may have been executed prior to this one on the block before it will finally attempt to execute the transaction that corresponds to the given hash.
- 1.
Hash
- Hash of the transaction to be traced. - 2.
Object
- (optional) Options for the trace.tracer
:String
- (optional) Setting this will enable JavaScript-based transaction tracing, described below.timeout
:String
- (optional) Overrides the default timeout of 5 seconds for JavaScript-based tracing calls. Check documentation for ParseDuration for valid values.
Object
- A trace object of the traced transaction.
curl -H "Content-Type: application/json" -X POST --data '{"method":"debug_traceTransaction","params":["0xa6598be1f06b0aff5caddec2685ec53599f064f7b8ff9eaded0a719a378c8802"],"id":1,"jsonrpc":"2.0"}' https://rpcapi-tracing.fantom.network
Specifying the
tracer
option in the second argument of the debug tracing calls enables JavaScript based tracing. In this mode, tracer
is interpreted as a JavaScript expression that is expected to evaluate to an object which must expose the result
and fault
methods. There exist 4 additional methods, namely: setup
, step
, enter
, and exit
. enter
and exit
must be present or omitted together.Method
setup
is invoked once, in the beginning when the tracer is being constructed for each transaction being traced. It takes in one argument config
, which allows users to pass in options to the tracer. config
is to be JSON-decoded for usage and its default value is "{}"
.Method
step
is a function that takes two arguments, log
and db
, and is called for each step of the EVM, or when an error occurs, as a transaction is traced.log
has the following fields:op
: Object, an OpCode object representing the current opcodestack
: Object, a structure representing the EVM execution stackmemory
: Object, a structure representing the contract’s memory spacecontract
: Object, an object representing the account executing the current operation
and the following methods:
getPC()
- returns a Number with the current program countergetGas()
- returns a Number with the amount of gas remaininggetCost()
- returns the cost of the opcode as a NumbergetDepth()
- returns the execution depth as a NumbergetRefund()
- returns the amount to be refunded as a NumbergetError()
- returns information about the error if one occured, otherwise returnsundefined
If error is non-empty, all other fields should be ignored.
For efficiency, the same
log
object is reused on each execution step, updated with current values; make sure to copy values you want to preserve beyond the current call. log.op
has the following methods:isPush()
- returns true if the opcode is a PUSHntoString()
- returns the string representation of the opcodetoNumber()
- returns the opcode’s number
log.memory
has the following methods:slice(start, stop)
- returns the specified segment of memory as a byte slicegetUint(offset)
- returns the 32 bytes at the given offsetlength()
- returns the memory size
log.stack
has the following methods:peek(idx)
- returns the idx-th element from the top of the stack (0 is the topmost element) as a big.Intlength()
- returns the number of elements in the stack
log.contract
has the following methods:getCaller()
- returns the address of the callergetAddress()
- returns the address of the current contractgetValue()
- returns the amount of value sent from caller to contract as a big.IntgetInput()
- returns the input data passed to the contract
db
has the following methods:getBalance(address)
- returns abig.Int
with the specified account’s balancegetNonce(address)
- returns a Number with the specified account’s noncegetCode(address)
- returns a byte slice with the code for the specified accountgetState(address, hash)
- returns the state value for the specified account and the specified hashexists(address)
- returns true if the specified address exists
If the step function throws an exception or executes an illegal operation at any point, it will not be called on any further VM steps, and the error will be returned to the caller.
Method
result
is a function that takes two arguments ctx
and db
, and is expected to return a JSON-serializable value to return to the RPC caller.ctx
is the context in which the transaction is executing and has the following fields:type
- String, one of the two valuesCALL
andCREATE
from
- Address, sender of the transactionto
- Address, target of the transactioninput
- Buffer, input transaction datagas
- Number, gas budget of the transactiongasUsed
- Number, amount of gas used in executing the transaction (excludes txdata costs)gasPrice
- Number, gas price configured in the transaction being executedintrinsicGas
- Number, intrinsic gas for the transaction being executedvalue
- big.Int, amount to be transferred in weiblock
- Number, block numberoutput
- Buffer, value returned from EVMtime
- String, execution runtime
Method
fault
is a function that takes two arguments, log
and db
, just like step
and is invoked when an error happens during the execution of an opcode which wasn’t reported in step
. The method log.getError()
has information about the error.Methods
enter
and exit
are respectively invoked on stepping in and out of an internal call. More specifically they are invoked on the CALL
variants, CREATE
variants and also for the transfer implied by a SELFDESTRUCT
.enter
takes a callFrame
object as argument which has the following methods:getType()
- returns a string which has the type of the call framegetFrom()
- returns the address of the call frame sendergetTo()
- returns the address of the call frame targetgetInput()
- returns the input as a buffergetGas()
- returns a Number which has the amount of gas provided for the framegetValue()
- returns abig.Int
with the amount to be transferred only if available, otherwiseundefined
exit
takes in a frameResult
object which has the following methods:getGasUsed()
- returns amount of gas used throughout the frame as a NumbergetOutput()
- returns the output as a buffergetError()
- returns an error if one occured during execution and undefined otherwise
Note that several values are Golang big.Int objects, not JavaScript numbers or JS bigints. As such, they have the same interface as described in the godocs. Their default serialization to JSON is as a Javascript number; to serialize large numbers accurately call
.String()
on them. For convenience, big.NewInt(x)
is provided, and will convert a uint to a Go BigInt.
Last modified 4mo ago