go-ethereum/eth/tracers/native/contract.go
Daniel Liu ad9003c41e
eth/tracers: live chain tracing with hooks #29189 (#1352)
Here we add a Go API for running tracing plugins within the main block import process.

As an advanced user of geth, you can now create a Go file in eth/tracers/live/, and within
that file register your custom tracer implementation. Then recompile geth and select your tracer
on the command line. Hooks defined in the tracer will run whenever a block is processed.

The hook system is defined in package core/tracing. It uses a struct with callbacks, instead of
requiring an interface, for several reasons:

- We plan to keep this API stable long-term. The core/tracing hook API does not depend on
  on deep geth internals.
- There are a lot of hooks, and tracers will only need some of them. Using a struct allows you
   to implement only the hooks you want to actually use.

All existing tracers in eth/tracers/native have been rewritten to use the new hook system.

This change breaks compatibility with the vm.EVMLogger interface that we used to have.
If you are a user of vm.EVMLogger, please migrate to core/tracing, and sorry for breaking
your stuff. But we just couldn't have both the old and new tracing APIs coexist in the EVM.

---------

Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com>
Co-authored-by: Matthieu Vachon <matthieu.o.vachon@gmail.com>
Co-authored-by: Delweng <delweng@gmail.com>
Co-authored-by: Martin HS <martin@swende.se>
2025-09-09 17:30:56 +08:00

120 lines
3.2 KiB
Go

package native
import (
"encoding/json"
"fmt"
"math/big"
"strings"
"sync/atomic"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
)
func init() {
tracers.DefaultDirectory.Register("contractTracer", NewContractTracer, false)
}
type contractTracer struct {
Addrs map[string]string
config contractTracerConfig
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
}
type contractTracerConfig struct {
OpCode string `json:"opCode"` // Target opcode to trace
}
// NewContractTracer returns a native go tracer which tracks the contracr was created
func NewContractTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
var config contractTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, err
}
}
t := &contractTracer{
Addrs: make(map[string]string, 1),
config: config,
}
// handle invalid opcode case
op := vm.StringToOp(t.config.OpCode)
if op == 0 && t.config.OpCode != "STOP" && t.config.OpCode != "" {
t.reason = fmt.Errorf("opcode %s not defined", t.config.OpCode)
return nil, t.reason
}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnEnter: t.OnEnter,
OnExit: t.OnExit,
OnOpcode: t.OnOpcode,
OnFault: t.OnFault,
},
GetResult: t.GetResult,
Stop: t.Stop,
}, nil
}
func (*contractTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
}
func (*contractTracer) OnTxEnd(receipt *types.Receipt, err error) {}
func (t *contractTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
// Skip if tracing was interrupted
if atomic.LoadUint32(&t.interrupt) > 0 {
return
}
// If the OpCode is empty , exit early.
if t.config.OpCode == "" {
return
}
targetOp := vm.StringToOp(t.config.OpCode)
op := vm.OpCode(opcode)
if op == targetOp {
addr := scope.Address()
t.Addrs[addrToHex(addr)] = ""
}
}
func (t *contractTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
}
func (t *contractTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
create := vm.OpCode(typ) == vm.CREATE
if create && t.config.OpCode == "" {
t.Addrs[addrToHex(to)] = ""
}
}
func (t *contractTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
}
func (t *contractTracer) GetResult() (json.RawMessage, error) {
// return Address array without duplicate address
AddrArray := make([]string, 0, len(t.Addrs))
for addr := range t.Addrs {
AddrArray = append(AddrArray, addr)
}
res, err := json.Marshal(AddrArray)
if err != nil {
return nil, err
}
return json.RawMessage(res), t.reason
}
func (t *contractTracer) Stop(err error) {
t.reason = err
atomic.StoreUint32(&t.interrupt, 1)
}
func addrToHex(a common.Address) string {
return strings.ToLower(a.String0x())
}