Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/evm/testdata/evmrun/8.out.2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@
{"pc":2597,"op":97,"gas":"0xae7b2","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
{"pc":2600,"op":96,"gas":"0xae7af","gasCost":"0x3","memSize":2240,"stack":["0x8a6"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":2602,"op":96,"gas":"0xae7ac","gasCost":"0x3","memSize":2240,"stack":["0x8a6","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}
{"pc":2604,"op":240,"gas":"0xae7a9","gasCost":"0x7d00","memSize":2240,"stack":["0x8a6","0x0","0x0"],"depth":1,"refund":0,"opName":"CREATE"}
{"pc":2604,"op":240,"gas":"0xae7a9","gasCost":"0x7d00","memSize":2240,"stack":["0x8a6","0x0","0x0"],"createAddr":"0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","depth":1,"refund":0,"opName":"CREATE"}
{"pc":0,"op":96,"gas":"0xa40ff","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"}
{"pc":2,"op":84,"gas":"0xa40fc","gasCost":"0x834","memSize":0,"stack":["0x2"],"depth":2,"refund":0,"opName":"SLOAD"}
{"pc":3,"op":80,"gas":"0xa38c8","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"POP"}
Expand Down Expand Up @@ -703,7 +703,7 @@
{"pc":1765,"op":97,"gas":"0x47e06","gasCost":"0x3","memSize":1472,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"}
{"pc":1768,"op":96,"gas":"0x47e03","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b2"],"depth":2,"refund":0,"opName":"PUSH1"}
{"pc":1770,"op":96,"gas":"0x47e00","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b2","0x0"],"depth":2,"refund":0,"opName":"PUSH1"}
{"pc":1772,"op":245,"gas":"0x47dfd","gasCost":"0x7e14","memSize":1472,"stack":["0x0","0x5b2","0x0","0x0"],"depth":2,"refund":0,"opName":"CREATE2"}
{"pc":1772,"op":245,"gas":"0x47dfd","gasCost":"0x7e14","memSize":1472,"stack":["0x0","0x5b2","0x0","0x0"],"createAddr":"0x94a843a7335fc63be036fbdecc40b1365f3c5f26","depth":2,"refund":0,"opName":"CREATE2"}
{"pc":0,"op":96,"gas":"0x3efea","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"}
{"pc":2,"op":84,"gas":"0x3efe7","gasCost":"0x834","memSize":0,"stack":["0x8"],"depth":3,"refund":0,"opName":"SLOAD"}
{"pc":3,"op":80,"gas":"0x3e7b3","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"POP"}
Expand Down
6 changes: 6 additions & 0 deletions eth/tracers/logger/gen_structlog.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 57 additions & 1 deletion eth/tracers/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/internal"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
Expand Down Expand Up @@ -78,6 +81,7 @@ type StructLog struct {
Stack []uint256.Int `json:"stack"`
ReturnData []byte `json:"returnData,omitempty"`
Storage map[common.Hash]common.Hash `json:"-"`
CreateAddr *common.Address `json:"createAddr,omitempty"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
Expand Down Expand Up @@ -113,6 +117,9 @@ func (s *StructLog) Write(writer io.Writer) {
if s.Err != nil {
fmt.Fprintf(writer, " ERROR: %v", s.Err)
}
if s.CreateAddr != nil {
fmt.Fprintf(writer, " createAddr=%v", s.CreateAddr)
}
fmt.Fprintln(writer)

if len(s.Stack) > 0 {
Expand Down Expand Up @@ -166,6 +173,7 @@ type structLogLegacy struct {
ReturnData string `json:"returnData,omitempty"`
Memory *[]string `json:"memory,omitempty"`
Storage *map[string]string `json:"storage,omitempty"`
CreateAddr *common.Address `json:"createAddr,omitempty"`
RefundCounter uint64 `json:"refund,omitempty"`
}

Expand All @@ -187,6 +195,7 @@ func (s *StructLog) toLegacyJSON() json.RawMessage {
GasCost: s.GasCost,
Depth: s.Depth,
Error: s.ErrorString(),
CreateAddr: s.CreateAddr,
RefundCounter: s.RefundCounter,
}
if s.Stack != nil {
Expand Down Expand Up @@ -221,6 +230,43 @@ func (s *StructLog) toLegacyJSON() json.RawMessage {
return element
}

func projectedCreateAddress(op vm.OpCode, scope tracing.OpContext, state tracing.StateDB) *common.Address {
stack := scope.StackData()
switch op {
case vm.CREATE:
addr := crypto.CreateAddress(scope.Address(), state.GetNonce(scope.Address()))
return &addr
case vm.CREATE2:
if len(stack) < 4 {
return nil
}
size, sizeOverflow := stack[len(stack)-3].Uint64WithOverflow()
const maxInt64 = uint64(1<<63 - 1)
if sizeOverflow || size > maxInt64 {
log.Warn("failed to copy CREATE2 input", "err", "offset or size overflow", "offset", stack[len(stack)-2], "size", stack[len(stack)-3])
return nil
}
var input []byte
if size > 0 {
offset, overflow := stack[len(stack)-2].Uint64WithOverflow()
if overflow || offset > maxInt64-size {
log.Warn("failed to copy CREATE2 input", "err", "offset or size overflow", "offset", stack[len(stack)-2], "size", size)
return nil
}
var err error
input, err = internal.GetMemoryCopyPadded(scope.MemoryData(), int64(offset), int64(size))
if err != nil {
log.Warn("failed to copy CREATE2 input", "err", err, "offset", offset, "size", size)
return nil
}
}
addr := crypto.CreateAddress2(scope.Address(), stack[len(stack)-4].Bytes32(), crypto.Keccak256(input))
return &addr
default:
return nil
}
}

// StructLogger is an EVM state logger and implements EVMLogger.
//
// StructLogger can capture state based on the given Log configuration and also keeps
Expand Down Expand Up @@ -300,7 +346,17 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
stack = scope.StackData()
stackLen = len(stack)
)
log := StructLog{pc, op, gas, cost, nil, len(memory), nil, nil, nil, depth, l.env.StateDB.GetRefund(), err}
log := StructLog{
Pc: pc,
Op: op,
Gas: gas,
GasCost: cost,
MemorySize: len(memory),
CreateAddr: projectedCreateAddress(op, scope, l.env.StateDB),
Depth: depth,
RefundCounter: l.env.StateDB.GetRefund(),
Err: err,
}
if l.cfg.EnableMemory {
log.Memory = memory
}
Expand Down
1 change: 1 addition & 0 deletions eth/tracers/logger/logger_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracin
Gas: gas,
GasCost: cost,
MemorySize: len(memory),
CreateAddr: projectedCreateAddress(vm.OpCode(op), scope, l.env.StateDB),
Depth: depth,
RefundCounter: l.env.StateDB.GetRefund(),
Err: err,
Expand Down
107 changes: 107 additions & 0 deletions eth/tracers/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,28 @@
package logger

import (
"bytes"
"encoding/json"
"errors"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)

type dummyStatedb struct {
state.StateDB
nonce uint64
}

func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (db *dummyStatedb) GetNonce(common.Address) uint64 { return db.nonce }
func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} }
func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) common.Hash {
return common.Hash{}
Expand All @@ -43,6 +48,108 @@ func (*dummyStatedb) GetStateAndCommittedState(common.Address, common.Hash) (com
return common.Hash{}, common.Hash{}
}

type testOpContext struct {
memory []byte
stack []uint256.Int
address common.Address
}

func (c testOpContext) MemoryData() []byte { return c.memory }
func (c testOpContext) StackData() []uint256.Int { return c.stack }
func (c testOpContext) Caller() common.Address { return common.Address{} }
func (c testOpContext) Address() common.Address { return c.address }
func (c testOpContext) CallValue() *uint256.Int { return new(uint256.Int) }
func (c testOpContext) CallInput() []byte { return nil }
func (c testOpContext) ContractCode() []byte { return nil }

func testStack(values ...uint64) []uint256.Int {
stack := make([]uint256.Int, len(values))
for i, value := range values {
stack[i].SetUint64(value)
}
return stack
}

func TestStructLoggerCapturesCreateAddress(t *testing.T) {
var (
caller = common.HexToAddress("0x1234")
db = &dummyStatedb{nonce: 7}
logger = NewStructLogger(nil)
)
logger.OnTxStart(&tracing.VMContext{StateDB: db}, nil, common.Address{})
logger.OnOpcode(0, byte(vm.CREATE), 0, 0, testOpContext{
address: caller,
stack: testStack(0, 0, 0),
}, nil, 0, nil)

var result struct {
CreateAddr *common.Address `json:"createAddr"`
}
if err := json.Unmarshal(logger.logs[0], &result); err != nil {
t.Fatal(err)
}
want := crypto.CreateAddress(caller, db.nonce)
if result.CreateAddr == nil || *result.CreateAddr != want {
t.Fatalf("unexpected create address: have %v want %v", result.CreateAddr, want)
}
}

func TestJSONLoggerCapturesCreate2Address(t *testing.T) {
var (
caller = common.HexToAddress("0x1234")
memory = []byte{0xaa, 0xbb}
salt = uint64(42)
output bytes.Buffer
db = new(dummyStatedb)
hooks = NewJSONLogger(nil, &output)
)
hooks.OnTxStart(&tracing.VMContext{StateDB: db}, nil, common.Address{})
hooks.OnOpcode(0, byte(vm.CREATE2), 0, 0, testOpContext{
address: caller,
memory: memory,
stack: testStack(salt, uint64(len(memory)), 0, 0),
}, nil, 0, nil)

var result struct {
CreateAddr *common.Address `json:"createAddr"`
}
if err := json.Unmarshal(output.Bytes(), &result); err != nil {
t.Fatal(err)
}
want := crypto.CreateAddress2(caller, testStack(salt)[0].Bytes32(), crypto.Keccak256(memory))
if result.CreateAddr == nil || *result.CreateAddr != want {
t.Fatalf("unexpected create2 address: have %v want %v", result.CreateAddr, want)
}
}

func TestJSONLoggerCapturesCreate2AddressWithZeroSize(t *testing.T) {
var (
caller = common.HexToAddress("0x1234")
salt = uint64(42)
output bytes.Buffer
db = new(dummyStatedb)
hooks = NewJSONLogger(nil, &output)
stack = testStack(salt, 0, 0, 0)
)
stack[2].SetBytes(bytes.Repeat([]byte{0xff}, 32))
hooks.OnTxStart(&tracing.VMContext{StateDB: db}, nil, common.Address{})
hooks.OnOpcode(0, byte(vm.CREATE2), 0, 0, testOpContext{
address: caller,
stack: stack,
}, nil, 0, nil)

var result struct {
CreateAddr *common.Address `json:"createAddr"`
}
if err := json.Unmarshal(output.Bytes(), &result); err != nil {
t.Fatal(err)
}
want := crypto.CreateAddress2(caller, stack[0].Bytes32(), crypto.Keccak256(nil))
if result.CreateAddr == nil || *result.CreateAddr != want {
t.Fatalf("unexpected create2 address: have %v want %v", result.CreateAddr, want)
}
}

func TestStoreCapture(t *testing.T) {
var (
logger = NewStructLogger(nil)
Expand Down