eth/tracers: improve tracing performance (#23016)
Improves the performance of debug.traceTransaction
This commit is contained in:
parent
bbbeb7d8ba
commit
f5f906dd0d
@ -4,11 +4,11 @@ package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var _ = (*structLogMarshaling)(nil)
|
||||
@ -22,8 +22,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
||||
GasCost math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory hexutil.Bytes `json:"memory"`
|
||||
MemorySize int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
|
||||
Stack []uint256.Int `json:"stack"`
|
||||
ReturnData hexutil.Bytes `json:"returnData"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
@ -39,12 +38,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
||||
enc.GasCost = math.HexOrDecimal64(s.GasCost)
|
||||
enc.Memory = s.Memory
|
||||
enc.MemorySize = s.MemorySize
|
||||
if s.Stack != nil {
|
||||
enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack))
|
||||
for k, v := range s.Stack {
|
||||
enc.Stack[k] = (*math.HexOrDecimal256)(v)
|
||||
}
|
||||
}
|
||||
enc.Stack = s.Stack
|
||||
enc.ReturnData = s.ReturnData
|
||||
enc.Storage = s.Storage
|
||||
enc.Depth = s.Depth
|
||||
@ -64,7 +58,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
GasCost *math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory *hexutil.Bytes `json:"memory"`
|
||||
MemorySize *int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
Stack []uint256.Int `json:"stack"`
|
||||
ReturnData *hexutil.Bytes `json:"returnData"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth *int `json:"depth"`
|
||||
@ -94,10 +88,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
s.MemorySize = *dec.MemorySize
|
||||
}
|
||||
if dec.Stack != nil {
|
||||
s.Stack = make([]*big.Int, len(dec.Stack))
|
||||
for k, v := range dec.Stack {
|
||||
s.Stack[k] = (*big.Int)(v)
|
||||
}
|
||||
s.Stack = dec.Stack
|
||||
}
|
||||
if dec.ReturnData != nil {
|
||||
s.ReturnData = *dec.ReturnData
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Storage represents a contract's storage.
|
||||
@ -66,7 +67,7 @@ type StructLog struct {
|
||||
GasCost uint64 `json:"gasCost"`
|
||||
Memory []byte `json:"memory"`
|
||||
MemorySize int `json:"memSize"`
|
||||
Stack []*big.Int `json:"stack"`
|
||||
Stack []uint256.Int `json:"stack"`
|
||||
ReturnData []byte `json:"returnData"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
@ -76,7 +77,6 @@ type StructLog struct {
|
||||
|
||||
// overrides for gencodec
|
||||
type structLogMarshaling struct {
|
||||
Stack []*math.HexOrDecimal256
|
||||
Gas math.HexOrDecimal64
|
||||
GasCost math.HexOrDecimal64
|
||||
Memory hexutil.Bytes
|
||||
@ -135,6 +135,14 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
return logger
|
||||
}
|
||||
|
||||
// Reset clears the data held by the logger.
|
||||
func (l *StructLogger) Reset() {
|
||||
l.storage = make(map[common.Address]Storage)
|
||||
l.output = make([]byte, 0)
|
||||
l.logs = l.logs[:0]
|
||||
l.err = nil
|
||||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
@ -157,16 +165,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
copy(mem, memory.Data())
|
||||
}
|
||||
// Copy a snapshot of the current stack state to a new buffer
|
||||
var stck []*big.Int
|
||||
var stck []uint256.Int
|
||||
if !l.cfg.DisableStack {
|
||||
stck = make([]*big.Int, len(stack.Data()))
|
||||
stck = make([]uint256.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
stck[i] = new(big.Int).Set(item.ToBig())
|
||||
stck[i] = item
|
||||
}
|
||||
}
|
||||
// Copy a snapshot of the current storage to a new container
|
||||
var storage Storage
|
||||
if !l.cfg.DisableStorage {
|
||||
if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) {
|
||||
// initialise new changed values storage container for this contract
|
||||
// if not present.
|
||||
if l.storage[contract.Address()] == nil {
|
||||
@ -179,17 +187,17 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
value = env.StateDB.GetState(contract.Address(), address)
|
||||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
}
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
} else if op == SSTORE && stack.len() >= 2 {
|
||||
// capture SSTORE opcodes and record the written entry in the local storage.
|
||||
if op == SSTORE && stack.len() >= 2 {
|
||||
var (
|
||||
value = common.Hash(stack.data[stack.len()-2].Bytes32())
|
||||
address = common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
}
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
}
|
||||
}
|
||||
var rdata []byte
|
||||
if !l.cfg.DisableReturnData {
|
||||
rdata = make([]byte, len(rData))
|
||||
@ -238,7 +246,7 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
|
||||
if len(log.Stack) > 0 {
|
||||
fmt.Fprintln(writer, "Stack:")
|
||||
for i := len(log.Stack) - 1; i >= 0; i-- {
|
||||
fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
|
||||
fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex())
|
||||
}
|
||||
}
|
||||
if len(log.Memory) > 0 {
|
||||
@ -314,7 +322,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
||||
// format stack
|
||||
var a []string
|
||||
for _, elem := range stack.data {
|
||||
a = append(a, fmt.Sprintf("%v", elem.String()))
|
||||
a = append(a, elem.Hex())
|
||||
}
|
||||
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||
fmt.Fprintf(t.out, "%10v |", b)
|
||||
|
@ -57,7 +57,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
||||
Gas: gas,
|
||||
GasCost: cost,
|
||||
MemorySize: memory.Len(),
|
||||
Storage: nil,
|
||||
Depth: depth,
|
||||
RefundCounter: env.StateDB.GetRefund(),
|
||||
Err: err,
|
||||
@ -66,12 +65,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
||||
log.Memory = memory.Data()
|
||||
}
|
||||
if !l.cfg.DisableStack {
|
||||
//TODO(@holiman) improve this
|
||||
logstack := make([]*big.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
logstack[i] = item.ToBig()
|
||||
}
|
||||
log.Stack = logstack
|
||||
log.Stack = stack.data
|
||||
}
|
||||
if !l.cfg.DisableReturnData {
|
||||
log.ReturnData = rData
|
||||
|
@ -300,3 +300,81 @@ func jsonEqual(x, y interface{}) bool {
|
||||
}
|
||||
return reflect.DeepEqual(xTrace, yTrace)
|
||||
}
|
||||
|
||||
func BenchmarkTransactionTrace(b *testing.B) {
|
||||
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
from := crypto.PubkeyToAddress(key.PublicKey)
|
||||
gas := uint64(1000000) // 1M gas
|
||||
to := common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
||||
signer := types.LatestSignerForChainID(big.NewInt(1337))
|
||||
tx, err := types.SignNewTx(key, signer,
|
||||
&types.LegacyTx{
|
||||
Nonce: 1,
|
||||
GasPrice: big.NewInt(500),
|
||||
Gas: gas,
|
||||
To: &to,
|
||||
})
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
txContext := vm.TxContext{
|
||||
Origin: from,
|
||||
GasPrice: tx.GasPrice(),
|
||||
}
|
||||
context := vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
Coinbase: common.Address{},
|
||||
BlockNumber: new(big.Int).SetUint64(uint64(5)),
|
||||
Time: new(big.Int).SetUint64(uint64(5)),
|
||||
Difficulty: big.NewInt(0xffffffff),
|
||||
GasLimit: gas,
|
||||
}
|
||||
alloc := core.GenesisAlloc{}
|
||||
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
|
||||
// the address
|
||||
loop := []byte{
|
||||
byte(vm.JUMPDEST), // [ count ]
|
||||
byte(vm.PUSH1), 0, // jumpdestination
|
||||
byte(vm.JUMP),
|
||||
}
|
||||
alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
|
||||
Nonce: 1,
|
||||
Code: loop,
|
||||
Balance: big.NewInt(1),
|
||||
}
|
||||
alloc[from] = core.GenesisAccount{
|
||||
Nonce: 1,
|
||||
Code: []byte{},
|
||||
Balance: big.NewInt(500000000000000),
|
||||
}
|
||||
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
|
||||
// Create the tracer, the EVM environment and run it
|
||||
tracer := vm.NewStructLogger(&vm.LogConfig{
|
||||
Debug: false,
|
||||
//DisableStorage: true,
|
||||
//DisableMemory: true,
|
||||
//DisableReturnData: true,
|
||||
})
|
||||
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
|
||||
msg, err := tx.AsMessage(signer, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
snap := statedb.Snapshot()
|
||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
_, err = st.TransitionDb()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
statedb.RevertToSnapshot(snap)
|
||||
if have, want := len(tracer.StructLogs()), 244752; have != want {
|
||||
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
|
||||
}
|
||||
tracer.Reset()
|
||||
}
|
||||
}
|
||||
|
@ -1117,7 +1117,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
||||
if trace.Stack != nil {
|
||||
stack := make([]string, len(trace.Stack))
|
||||
for i, stackValue := range trace.Stack {
|
||||
stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
|
||||
stack[i] = stackValue.Hex()
|
||||
}
|
||||
formatted[index].Stack = &stack
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user