mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Zero transaction tracer created
This commit is contained in:
parent
509a64ffb9
commit
f3ab484304
1 changed files with 161 additions and 0 deletions
161
eth/tracers/native/zero.go
Normal file
161
eth/tracers/native/zero.go
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"math/big"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tracers.DefaultDirectory.Register("zeroTracer", newZeroTracer, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Balance *big.Int `json:"balance,omitempty"`
|
||||||
|
Nonce uint64 `json:"nonce,omitempty"`
|
||||||
|
ReadStorage map[common.Hash]common.Hash `json:"storage_read,omitempty"`
|
||||||
|
WriteStorage map[common.Hash]common.Hash `json:"storage_write,omitempty"`
|
||||||
|
CodeUsage string `json:"code_usage,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TX struct {
|
||||||
|
ByteCode string `json:"byte_code,omitempty"` // TX CallData
|
||||||
|
GasUsed uint64 `json:"gas_used,omitempty"`
|
||||||
|
Trace map[common.Address]*Account `json:"trace,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type zeroTracer struct {
|
||||||
|
noopTracer // stub struct to mock not used interface methods
|
||||||
|
env *vm.EVM
|
||||||
|
tx TX
|
||||||
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
|
reason error // Textual reason for the interruption
|
||||||
|
}
|
||||||
|
|
||||||
|
func newZeroTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
||||||
|
return &zeroTracer{
|
||||||
|
tx: TX{
|
||||||
|
Trace: make(map[common.Address]*Account),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
|
func (t *zeroTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||||
|
t.env = env
|
||||||
|
t.tx.ByteCode = common.Bytes2Hex(input)
|
||||||
|
|
||||||
|
t.addAccountToTrace(from, false)
|
||||||
|
t.addAccountToTrace(to, false)
|
||||||
|
t.addAccountToTrace(env.Context.Coinbase, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||||
|
func (t *zeroTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if tracing was interrupted
|
||||||
|
if t.interrupt.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stack := scope.Stack
|
||||||
|
stackData := stack.Data()
|
||||||
|
stackLen := len(stackData)
|
||||||
|
caller := scope.Contract.Address()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case stackLen >= 1 && op == vm.SLOAD:
|
||||||
|
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||||
|
t.addSLOADToAccount(caller, slot)
|
||||||
|
case stackLen >= 1 && op == vm.SSTORE:
|
||||||
|
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||||
|
t.addSSTOREToAccount(caller, slot)
|
||||||
|
case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT):
|
||||||
|
addr := common.Address(stackData[stackLen-1].Bytes20())
|
||||||
|
t.addAccountToTrace(addr, false)
|
||||||
|
case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE):
|
||||||
|
addr := common.Address(stackData[stackLen-2].Bytes20())
|
||||||
|
t.addAccountToTrace(addr, false)
|
||||||
|
case op == vm.CREATE:
|
||||||
|
nonce := t.env.StateDB.GetNonce(caller)
|
||||||
|
addr := crypto.CreateAddress(caller, nonce)
|
||||||
|
t.addAccountToTrace(addr, true)
|
||||||
|
case stackLen >= 4 && op == vm.CREATE2:
|
||||||
|
offset := stackData[stackLen-2]
|
||||||
|
size := stackData[stackLen-3]
|
||||||
|
init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inithash := crypto.Keccak256(init)
|
||||||
|
salt := stackData[stackLen-4]
|
||||||
|
addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
|
||||||
|
t.addAccountToTrace(addr, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *zeroTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
|
t.tx.GasUsed = gasUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult returns the json-encoded nested list of call traces, and any
|
||||||
|
// error arising from the encoding or forceful termination (via `Stop`).
|
||||||
|
func (t *zeroTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
var res []byte
|
||||||
|
var err error
|
||||||
|
res, err = json.Marshal(t.tx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.RawMessage(res), t.reason
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
|
func (t *zeroTracer) Stop(err error) {
|
||||||
|
t.reason = err
|
||||||
|
t.interrupt.Store(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *zeroTracer) addAccountToTrace(addr common.Address, created bool) {
|
||||||
|
if _, ok := t.tx.Trace[addr]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var code string
|
||||||
|
if created {
|
||||||
|
code = common.Bytes2Hex(t.env.StateDB.GetCode(addr))
|
||||||
|
} else {
|
||||||
|
code = common.Hash.String(t.env.StateDB.GetCodeHash(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.tx.Trace[addr] = &Account{
|
||||||
|
Balance: t.env.StateDB.GetBalance(addr),
|
||||||
|
Nonce: t.env.StateDB.GetNonce(addr),
|
||||||
|
CodeUsage: code,
|
||||||
|
WriteStorage: make(map[common.Hash]common.Hash),
|
||||||
|
ReadStorage: make(map[common.Hash]common.Hash),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *zeroTracer) addSLOADToAccount(addr common.Address, key common.Hash) {
|
||||||
|
t.tx.Trace[addr].ReadStorage[key] = t.env.StateDB.GetState(addr, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *zeroTracer) addSSTOREToAccount(addr common.Address, key common.Hash) {
|
||||||
|
t.tx.Trace[addr].WriteStorage[key] = t.env.StateDB.GetState(addr, key)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue