Skip to content

Commit

Permalink
Merge pull request #105 from OffchainLabs/js-capture-arbitrum-transfers
Browse files Browse the repository at this point in the history
Native and Js tracing impls for `CaptureArbitrumTransfer`
  • Loading branch information
PlasmaPower authored Jun 17, 2022
2 parents e2e32a7 + 1fb931e commit 93c8207
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 30 deletions.
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 @@ -366,8 +373,21 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
st.state.AddBalance(st.evm.Context.Coinbase, 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 @@ -394,6 +414,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

0 comments on commit 93c8207

Please sign in to comment.