mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
166 lines
4.7 KiB
Go
166 lines
4.7 KiB
Go
package tracers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
)
|
|
|
|
type BlockTraceResult struct {
|
|
// Trace of each transaction executed
|
|
Transactions []*TxTraceResult `json:"transactions,omitempty"`
|
|
|
|
// Block that we are executing on the trace
|
|
Block interface{} `json:"block"`
|
|
}
|
|
|
|
type TxTraceResult struct {
|
|
// Trace results produced by the tracer
|
|
Result interface{} `json:"result,omitempty"`
|
|
|
|
// Trace failure produced by the tracer
|
|
Error string `json:"error,omitempty"`
|
|
|
|
// IntermediateHash of the execution if succeeds
|
|
IntermediateHash common.Hash `json:"intermediatehash"`
|
|
}
|
|
|
|
func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *TraceConfig) (*BlockTraceResult, error) {
|
|
if block.NumberU64() == 0 {
|
|
return nil, fmt.Errorf("genesis is not traceable")
|
|
}
|
|
|
|
res := &BlockTraceResult{
|
|
Block: block,
|
|
}
|
|
|
|
// block object cannot be converted to JSON since much of the fields are non-public
|
|
blockFields := ethapi.RPCMarshalBlock(block, true, true, api.backend.ChainConfig(), api.backend.ChainDb())
|
|
|
|
res.Block = blockFields
|
|
|
|
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
reexec := defaultTraceReexec
|
|
if config != nil && config.Reexec != nil {
|
|
reexec = *config.Reexec
|
|
}
|
|
|
|
// TODO: discuss consequences of setting preferDisk false.
|
|
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer release()
|
|
|
|
// Execute all the transaction contained within the block concurrently
|
|
var (
|
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
|
txs, stateSyncPresent, stateSyncHash = api.getAllBlockTransactions(ctx, block)
|
|
deleteEmptyObjects = api.backend.ChainConfig().IsEIP158(block.Number())
|
|
)
|
|
|
|
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
|
|
|
traceTxn := func(indx int, tx *types.Transaction, borTx bool, stateSyncHash common.Hash) *TxTraceResult {
|
|
message, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
|
txContext := core.NewEVMTxContext(message)
|
|
txHash := tx.Hash()
|
|
if borTx {
|
|
txHash = stateSyncHash
|
|
}
|
|
|
|
tracer := logger.NewStructLogger(config.Config)
|
|
|
|
// Run the transaction with tracing enabled.
|
|
vmenv := vm.NewEVM(blockCtx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
|
|
|
// Call Prepare to clear out the statedb access list
|
|
// Not sure if we need to do this
|
|
statedb.SetTxContext(txHash, indx)
|
|
|
|
var execRes *core.ExecutionResult
|
|
|
|
if borTx {
|
|
callmsg := prepareCallMessage(*message)
|
|
execRes, err = statefull.ApplyBorMessage(vmenv, callmsg)
|
|
} else {
|
|
execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit), nil)
|
|
}
|
|
|
|
if err != nil {
|
|
return &TxTraceResult{
|
|
Error: err.Error(),
|
|
}
|
|
}
|
|
|
|
returnVal := fmt.Sprintf("%x", execRes.Return())
|
|
if len(execRes.Revert()) > 0 {
|
|
returnVal = fmt.Sprintf("%x", execRes.Revert())
|
|
}
|
|
|
|
result := ðapi.ExecutionResult{
|
|
Gas: execRes.UsedGas,
|
|
Failed: execRes.Failed(),
|
|
ReturnValue: returnVal,
|
|
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
|
}
|
|
res := &TxTraceResult{
|
|
Result: result,
|
|
IntermediateHash: statedb.IntermediateRoot(deleteEmptyObjects),
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
for indx, tx := range txs {
|
|
if stateSyncPresent && indx == len(txs)-1 {
|
|
res.Transactions = append(res.Transactions, traceTxn(indx, tx, true, stateSyncHash))
|
|
} else {
|
|
res.Transactions = append(res.Transactions, traceTxn(indx, tx, false, stateSyncHash))
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
type TraceBlockRequest struct {
|
|
Number int64
|
|
Hash string
|
|
IsBadBlock bool
|
|
Config *TraceConfig
|
|
}
|
|
|
|
// If you use context as first parameter this function gets exposed automaticall on rpc endpoint
|
|
func (api *API) TraceBorBlock(req *TraceBlockRequest) (*BlockTraceResult, error) {
|
|
ctx := context.Background()
|
|
|
|
var blockNumber rpc.BlockNumber
|
|
if req.Number == -1 {
|
|
blockNumber = rpc.LatestBlockNumber
|
|
} else {
|
|
blockNumber = rpc.BlockNumber(req.Number)
|
|
}
|
|
|
|
log.Debug("Tracing Bor Block", "block number", blockNumber)
|
|
|
|
block, err := api.blockByNumber(ctx, blockNumber)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return api.traceBorBlock(ctx, block, req.Config)
|
|
}
|