Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native and Js tracing impls for CaptureArbitrumTransfer #105

Merged
merged 12 commits into from
Jun 17, 2022
14 changes: 14 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,20 @@ func (s *StateDB) GetUnexpectedBalanceDelta() *big.Int {
return new(big.Int).Set(s.unexpectedBalanceDelta)
}

func (s *StateDB) GetSuicides() []common.Address {
suicides := []common.Address{}
for addr := range s.journal.dirties {
obj, exist := s.stateObjects[addr]
if !exist {
continue
}
if obj.suicided {
suicides = append(suicides, addr)
}
}
return suicides
}

// Finalise finalises the state by removing the s destructed objects and clears
// the journal as well as the refunds. Finalise, however, will not push any updates
// into the tries just yet. Only IntermediateRoot or Commit will do that.
Expand Down
26 changes: 26 additions & 0 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ func (st *StateTransition) buyGas() error {

st.initialGas = st.msg.Gas()
st.state.SubBalance(st.msg.From(), mgval)

// Arbitrum: record fee payment
if st.evm.Config.Debug {
from := st.msg.From()
st.evm.Config.Tracer.CaptureArbitrumTransfer(st.evm, &from, nil, mgval, true, "feePayment")
}

return nil
}

Expand Down Expand Up @@ -370,8 +377,21 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
st.state.AddBalance(*tipRecipient, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))

// Arbitrum: record the tip if nonzero (this should never happen in L2)
if st.evm.Config.Debug && effectiveTip.Sign() != 0 {
st.evm.Config.Tracer.CaptureArbitrumTransfer(st.evm, nil, tipRecipient, effectiveTip, false, "tip")
}

st.evm.ProcessingHook.EndTxHook(st.gas, vmerr == nil)

// Arbitrum: record self destructs
if st.evm.Config.Debug {
for _, address := range st.evm.StateDB.GetSuicides() {
balance := st.evm.StateDB.GetBalance(address)
st.evm.Config.Tracer.CaptureArbitrumTransfer(st.evm, &address, nil, balance, false, "selfDestruct")
}
}

return &ExecutionResult{
UsedGas: st.gasUsed(),
Err: vmerr,
Expand All @@ -398,6 +418,12 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
st.state.AddBalance(st.msg.From(), remaining)

// Arbitrum: record the gas refund
if st.evm.Config.Debug {
from := st.msg.From()
st.evm.Config.Tracer.CaptureArbitrumTransfer(st.evm, nil, &from, remaining, false, "gasRefund")
}

// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
st.gp.AddGas(st.gas)
Expand Down
2 changes: 1 addition & 1 deletion core/vm/evm_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (evm *EVM) IncrementDepth() {
}

func (evm *EVM) DecrementDepth() {
evm.depth += 1
evm.depth -= 1
}

type TxProcessingHook interface {
Expand Down
1 change: 1 addition & 0 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type StateDB interface {

Suicide(common.Address) bool
HasSuicided(common.Address) bool
GetSuicides() []common.Address

// Exist reports whether the given account exists in state.
// Notably this should also return true for suicided accounts.
Expand Down
2 changes: 1 addition & 1 deletion core/vm/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type EVMLogger interface {
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)

// Arbitrum: capture a transfer, mint, or burn that happens outside of EVM exectuion
CaptureArbitrumTransfer(env *EVM, from, to *common.Address, amount *big.Int, before bool)
CaptureArbitrumTransfer(env *EVM, from, to *common.Address, value *big.Int, before bool, purpose string)
CaptureArbitrumStorageGet(key common.Hash, depth int, before bool)
CaptureArbitrumStorageSet(key, value common.Hash, depth int, before bool)
}
2 changes: 2 additions & 0 deletions eth/tracers/js/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,8 @@ func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {

func pushValue(ctx *duktape.Context, val interface{}) {
switch val := val.(type) {
case bool:
ctx.PushBoolean(val)
case uint64:
ctx.PushUint(uint(val))
case string:
Expand Down
38 changes: 36 additions & 2 deletions eth/tracers/js/tracer_arbitrum.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017 The go-ethereum Authors
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
Expand All @@ -23,8 +23,42 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
)

func (*jsTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
func (jst *jsTracer) CaptureArbitrumTransfer(
env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string,
) {
traceTransfers := jst.vm.GetPropString(jst.tracerObject, "captureArbitrumTransfer")
jst.vm.Pop()
if !traceTransfers {
return
}

obj := jst.vm.PushObject()
if from != nil {
jst.addToObj(obj, "from", from.String())
} else {
jst.addNull(obj, "from")
}
if to != nil {
jst.addToObj(obj, "to", to.String())
} else {
jst.addNull(obj, "to")
}

jst.addToObj(obj, "value", value)
jst.addToObj(obj, "before", before)
jst.addToObj(obj, "purpose", purpose)
jst.vm.PutPropString(jst.stateObject, "transfer")

if _, err := jst.call(true, "captureArbitrumTransfer", "transfer"); err != nil {
jst.err = wrapError("captureArbitrumTransfer", err)
}
}

func (*jsTracer) CaptureArbitrumStorageGet(key common.Hash, depth int, before bool) {}
func (*jsTracer) CaptureArbitrumStorageSet(key, value common.Hash, depth int, before bool) {}

// addNull pushes a null field to a JS object.
func (jst *jsTracer) addNull(obj int, key string) {
jst.vm.PushNull()
jst.vm.PutPropString(obj, key)
}
17 changes: 0 additions & 17 deletions eth/tracers/logger/access_list_tracer_arbitrum.go

This file was deleted.

6 changes: 3 additions & 3 deletions eth/tracers/logger/logger_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
)

func (*AccessListTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
func (*AccessListTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string) {
}
func (*JSONLogger) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
func (*JSONLogger) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string) {
}
func (*StructLogger) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
func (*StructLogger) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string) {
}

func (*AccessListTracer) CaptureArbitrumStorageGet(key common.Hash, depth int, before bool) {}
Expand Down
22 changes: 20 additions & 2 deletions eth/tracers/native/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func init() {
}

type callFrame struct {
// Arbitrum: we add these here due to the tracer returning the top frame
BeforeEVMTransfers *[]arbitrumTransfer `json:"beforeEVMTransfers,omitempty"`
AfterEVMTransfers *[]arbitrumTransfer `json:"afterEVMTransfers,omitempty"`

Type string `json:"type"`
From string `json:"from"`
To string `json:"to,omitempty"`
Expand All @@ -48,6 +52,10 @@ type callFrame struct {
}

type callTracer struct {
// Arbitrum: capture transfers occuring outside of evm execution
beforeEVMTransfers []arbitrumTransfer
afterEVMTransfers []arbitrumTransfer

env *vm.EVM
callstack []callFrame
interrupt uint32 // Atomic flag to signal execution interruption
Expand All @@ -59,7 +67,11 @@ type callTracer struct {
func newCallTracer() tracers.Tracer {
// First callframe contains tx context info
// and is populated on start and end.
return &callTracer{callstack: make([]callFrame, 1)}
return &callTracer{
callstack: make([]callFrame, 1),
beforeEVMTransfers: []arbitrumTransfer{},
afterEVMTransfers: []arbitrumTransfer{},
}
}

// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
Expand Down Expand Up @@ -148,7 +160,13 @@ func (t *callTracer) GetResult() (json.RawMessage, error) {
if len(t.callstack) != 1 {
return nil, errors.New("incorrect number of top-level calls")
}
res, err := json.Marshal(t.callstack[0])

// Arbitrum: populate the top-level call with additional info
call := t.callstack[0]
call.BeforeEVMTransfers = &t.beforeEVMTransfers
call.AfterEVMTransfers = &t.afterEVMTransfers

res, err := json.Marshal(call)
if err != nil {
return nil, err
}
Expand Down
35 changes: 31 additions & 4 deletions eth/tracers/native/tracer_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,40 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
)

func (*callTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
type arbitrumTransfer struct {
Purpose string `json:"purpose"`
From *string `json:"from"`
To *string `json:"to"`
Value string `json:"value"`
}
func (*fourByteTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {

func (t *callTracer) CaptureArbitrumTransfer(
env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string,
) {
transfer := arbitrumTransfer{
Purpose: purpose,
Value: bigToHex(value),
}
if from != nil {
from := from.String()
transfer.From = &from
}
if to != nil {
to := to.String()
transfer.To = &to
}
if before {
t.beforeEVMTransfers = append(t.beforeEVMTransfers, transfer)
} else {
t.afterEVMTransfers = append(t.afterEVMTransfers, transfer)
}
}

func (*fourByteTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string) {
}
func (*noopTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
func (*noopTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string) {
}
func (*prestateTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, amount *big.Int, before bool) {
func (*prestateTracer) CaptureArbitrumTransfer(env *vm.EVM, from, to *common.Address, value *big.Int, before bool, purpose string) {
}

func (*callTracer) CaptureArbitrumStorageGet(key common.Hash, depth int, before bool) {}
Expand Down