mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-03 17:44:29 +00:00
c293883ec0
after removal of tevm experiment - we left interfaces everywhere removing it for performance and for geth compatibility
200 lines
5.9 KiB
Go
200 lines
5.9 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"sort"
|
|
|
|
"github.com/holiman/uint256"
|
|
jsoniter "github.com/json-iterator/go"
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
|
|
|
"github.com/ledgerwatch/erigon/common"
|
|
"github.com/ledgerwatch/erigon/core/vm"
|
|
)
|
|
|
|
// JsonStreamLogger is an EVM state logger and implements Tracer.
|
|
//
|
|
// JsonStreamLogger can capture state based on the given Log configuration and also keeps
|
|
// a track record of modified storage which is used in reporting snapshots of the
|
|
// contract their storage.
|
|
type JsonStreamLogger struct {
|
|
ctx context.Context
|
|
cfg LogConfig
|
|
stream *jsoniter.Stream
|
|
hexEncodeBuf [128]byte
|
|
firstCapture bool
|
|
|
|
locations common.Hashes // For sorting
|
|
storage map[libcommon.Address]Storage
|
|
logs []StructLog
|
|
output []byte //nolint
|
|
err error //nolint
|
|
env *vm.EVM
|
|
}
|
|
|
|
// NewStructLogger returns a new logger
|
|
func NewJsonStreamLogger(cfg *LogConfig, ctx context.Context, stream *jsoniter.Stream) *JsonStreamLogger {
|
|
logger := &JsonStreamLogger{
|
|
ctx: ctx,
|
|
stream: stream,
|
|
storage: make(map[libcommon.Address]Storage),
|
|
firstCapture: true,
|
|
}
|
|
if cfg != nil {
|
|
logger.cfg = *cfg
|
|
}
|
|
return logger
|
|
}
|
|
|
|
func (l *JsonStreamLogger) CaptureTxStart(gasLimit uint64) {}
|
|
|
|
func (l *JsonStreamLogger) CaptureTxEnd(restGas uint64) {}
|
|
|
|
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
|
func (l *JsonStreamLogger) CaptureStart(env *vm.EVM, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
|
|
l.env = env
|
|
}
|
|
|
|
func (l *JsonStreamLogger) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
|
|
}
|
|
|
|
// CaptureState logs a new structured log message and pushes it out to the environment
|
|
//
|
|
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
|
func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
|
contract := scope.Contract
|
|
memory := scope.Memory
|
|
stack := scope.Stack
|
|
|
|
select {
|
|
case <-l.ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
// check if already accumulated the specified number of logs
|
|
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
|
return
|
|
}
|
|
if !l.firstCapture {
|
|
l.stream.WriteMore()
|
|
} else {
|
|
l.firstCapture = false
|
|
}
|
|
var outputStorage bool
|
|
if !l.cfg.DisableStorage {
|
|
// initialise new changed values storage container for this contract
|
|
// if not present.
|
|
if l.storage[contract.Address()] == nil {
|
|
l.storage[contract.Address()] = make(Storage)
|
|
}
|
|
// capture SLOAD opcodes and record the read entry in the local storage
|
|
if op == vm.SLOAD && stack.Len() >= 1 {
|
|
var (
|
|
address = libcommon.Hash(stack.Data[stack.Len()-1].Bytes32())
|
|
value uint256.Int
|
|
)
|
|
l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
|
|
l.storage[contract.Address()][address] = value.Bytes32()
|
|
outputStorage = true
|
|
}
|
|
// capture SSTORE opcodes and record the written entry in the local storage.
|
|
if op == vm.SSTORE && stack.Len() >= 2 {
|
|
var (
|
|
value = libcommon.Hash(stack.Data[stack.Len()-2].Bytes32())
|
|
address = libcommon.Hash(stack.Data[stack.Len()-1].Bytes32())
|
|
)
|
|
l.storage[contract.Address()][address] = value
|
|
outputStorage = true
|
|
}
|
|
}
|
|
// create a new snapshot of the EVM.
|
|
l.stream.WriteObjectStart()
|
|
l.stream.WriteObjectField("pc")
|
|
l.stream.WriteUint64(pc)
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("op")
|
|
l.stream.WriteString(op.String())
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("gas")
|
|
l.stream.WriteUint64(gas)
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("gasCost")
|
|
l.stream.WriteUint64(cost)
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("depth")
|
|
l.stream.WriteInt(depth)
|
|
if err != nil {
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("error")
|
|
l.stream.WriteObjectStart()
|
|
l.stream.WriteObjectEnd()
|
|
//l.stream.WriteString(err.Error())
|
|
}
|
|
if !l.cfg.DisableStack {
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("stack")
|
|
l.stream.WriteArrayStart()
|
|
for i, stackValue := range stack.Data {
|
|
if i > 0 {
|
|
l.stream.WriteMore()
|
|
}
|
|
l.stream.WriteString(stackValue.String())
|
|
}
|
|
l.stream.WriteArrayEnd()
|
|
}
|
|
if !l.cfg.DisableMemory {
|
|
memData := memory.Data()
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("memory")
|
|
l.stream.WriteArrayStart()
|
|
for i := 0; i+32 <= len(memData); i += 32 {
|
|
if i > 0 {
|
|
l.stream.WriteMore()
|
|
}
|
|
l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], memData[i:i+32])]))
|
|
}
|
|
l.stream.WriteArrayEnd()
|
|
}
|
|
if outputStorage {
|
|
l.stream.WriteMore()
|
|
l.stream.WriteObjectField("storage")
|
|
l.stream.WriteObjectStart()
|
|
first := true
|
|
// Sort storage by locations for easier comparison with geth
|
|
if l.locations != nil {
|
|
l.locations = l.locations[:0]
|
|
}
|
|
s := l.storage[contract.Address()]
|
|
for loc := range s {
|
|
l.locations = append(l.locations, loc)
|
|
}
|
|
sort.Sort(l.locations)
|
|
for _, loc := range l.locations {
|
|
value := s[loc]
|
|
if first {
|
|
first = false
|
|
} else {
|
|
l.stream.WriteMore()
|
|
}
|
|
l.stream.WriteObjectField(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], loc[:])]))
|
|
l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], value[:])]))
|
|
}
|
|
l.stream.WriteObjectEnd()
|
|
}
|
|
l.stream.WriteObjectEnd()
|
|
_ = l.stream.Flush()
|
|
}
|
|
|
|
// CaptureFault implements the Tracer interface to trace an execution fault
|
|
// while running an opcode.
|
|
func (l *JsonStreamLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
|
}
|
|
|
|
// CaptureEnd is called after the call finishes to finalize the tracing.
|
|
func (l *JsonStreamLogger) CaptureEnd(output []byte, usedGas uint64, err error) {
|
|
}
|
|
|
|
func (l *JsonStreamLogger) CaptureExit(output []byte, usedGas uint64, err error) {
|
|
}
|