github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/tracers/native/prestate.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package native
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"math/big"
    23  	"sync/atomic"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/hexutil"
    27  	"github.com/ethereum/go-ethereum/core/tracing"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/core/vm"
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/eth/tracers"
    32  	"github.com/ethereum/go-ethereum/eth/tracers/internal"
    33  	"github.com/ethereum/go-ethereum/log"
    34  )
    35  
    36  //go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
    37  
    38  func init() {
    39  	tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
    40  }
    41  
    42  type stateMap = map[common.Address]*account
    43  
    44  type account struct {
    45  	Balance *big.Int                    `json:"balance,omitempty"`
    46  	Code    []byte                      `json:"code,omitempty"`
    47  	Nonce   uint64                      `json:"nonce,omitempty"`
    48  	Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
    49  	empty   bool
    50  }
    51  
    52  func (a *account) exists() bool {
    53  	return a.Nonce > 0 || len(a.Code) > 0 || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0)
    54  }
    55  
    56  type accountMarshaling struct {
    57  	Balance *hexutil.Big
    58  	Code    hexutil.Bytes
    59  }
    60  
    61  type prestateTracer struct {
    62  	env       *tracing.VMContext
    63  	pre       stateMap
    64  	post      stateMap
    65  	to        common.Address
    66  	config    prestateTracerConfig
    67  	interrupt atomic.Bool // Atomic flag to signal execution interruption
    68  	reason    error       // Textual reason for the interruption
    69  	created   map[common.Address]bool
    70  	deleted   map[common.Address]bool
    71  }
    72  
    73  type prestateTracerConfig struct {
    74  	DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
    75  }
    76  
    77  func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
    78  	var config prestateTracerConfig
    79  	if cfg != nil {
    80  		if err := json.Unmarshal(cfg, &config); err != nil {
    81  			return nil, err
    82  		}
    83  	}
    84  	t := &prestateTracer{
    85  		pre:     stateMap{},
    86  		post:    stateMap{},
    87  		config:  config,
    88  		created: make(map[common.Address]bool),
    89  		deleted: make(map[common.Address]bool),
    90  	}
    91  	return &tracers.Tracer{
    92  		Hooks: &tracing.Hooks{
    93  			OnTxStart: t.OnTxStart,
    94  			OnTxEnd:   t.OnTxEnd,
    95  			OnOpcode:  t.OnOpcode,
    96  		},
    97  		GetResult: t.GetResult,
    98  		Stop:      t.Stop,
    99  	}, nil
   100  }
   101  
   102  // OnOpcode implements the EVMLogger interface to trace a single step of VM execution.
   103  func (t *prestateTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
   104  	if err != nil {
   105  		return
   106  	}
   107  	// Skip if tracing was interrupted
   108  	if t.interrupt.Load() {
   109  		return
   110  	}
   111  	op := vm.OpCode(opcode)
   112  	stackData := scope.StackData()
   113  	stackLen := len(stackData)
   114  	caller := scope.Address()
   115  	switch {
   116  	case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
   117  		slot := common.Hash(stackData[stackLen-1].Bytes32())
   118  		t.lookupStorage(caller, slot)
   119  	case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT):
   120  		addr := common.Address(stackData[stackLen-1].Bytes20())
   121  		t.lookupAccount(addr)
   122  		if op == vm.SELFDESTRUCT {
   123  			t.deleted[caller] = true
   124  		}
   125  	case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE):
   126  		addr := common.Address(stackData[stackLen-2].Bytes20())
   127  		t.lookupAccount(addr)
   128  	case op == vm.CREATE:
   129  		nonce := t.env.StateDB.GetNonce(caller)
   130  		addr := crypto.CreateAddress(caller, nonce)
   131  		t.lookupAccount(addr)
   132  		t.created[addr] = true
   133  	case stackLen >= 4 && op == vm.CREATE2:
   134  		offset := stackData[stackLen-2]
   135  		size := stackData[stackLen-3]
   136  		init, err := internal.GetMemoryCopyPadded(scope.MemoryData(), int64(offset.Uint64()), int64(size.Uint64()))
   137  		if err != nil {
   138  			log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
   139  			return
   140  		}
   141  		inithash := crypto.Keccak256(init)
   142  		salt := stackData[stackLen-4]
   143  		addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
   144  		t.lookupAccount(addr)
   145  		t.created[addr] = true
   146  	}
   147  }
   148  
   149  func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
   150  	t.env = env
   151  	if tx.To() == nil {
   152  		t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from))
   153  		t.created[t.to] = true
   154  	} else {
   155  		t.to = *tx.To()
   156  	}
   157  
   158  	t.lookupAccount(from)
   159  	t.lookupAccount(t.to)
   160  	t.lookupAccount(env.Coinbase)
   161  }
   162  
   163  func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) {
   164  	if err != nil {
   165  		return
   166  	}
   167  	if t.config.DiffMode {
   168  		t.processDiffState()
   169  	}
   170  	// the new created contracts' prestate were empty, so delete them
   171  	for a := range t.created {
   172  		// the created contract maybe exists in statedb before the creating tx
   173  		if s := t.pre[a]; s != nil && s.empty {
   174  			delete(t.pre, a)
   175  		}
   176  	}
   177  }
   178  
   179  // GetResult returns the json-encoded nested list of call traces, and any
   180  // error arising from the encoding or forceful termination (via `Stop`).
   181  func (t *prestateTracer) GetResult() (json.RawMessage, error) {
   182  	var res []byte
   183  	var err error
   184  	if t.config.DiffMode {
   185  		res, err = json.Marshal(struct {
   186  			Post stateMap `json:"post"`
   187  			Pre  stateMap `json:"pre"`
   188  		}{t.post, t.pre})
   189  	} else {
   190  		res, err = json.Marshal(t.pre)
   191  	}
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	return json.RawMessage(res), t.reason
   196  }
   197  
   198  // Stop terminates execution of the tracer at the first opportune moment.
   199  func (t *prestateTracer) Stop(err error) {
   200  	t.reason = err
   201  	t.interrupt.Store(true)
   202  }
   203  
   204  func (t *prestateTracer) processDiffState() {
   205  	for addr, state := range t.pre {
   206  		// The deleted account's state is pruned from `post` but kept in `pre`
   207  		if _, ok := t.deleted[addr]; ok {
   208  			continue
   209  		}
   210  		modified := false
   211  		postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
   212  		newBalance := t.env.StateDB.GetBalance(addr).ToBig()
   213  		newNonce := t.env.StateDB.GetNonce(addr)
   214  		newCode := t.env.StateDB.GetCode(addr)
   215  
   216  		if newBalance.Cmp(t.pre[addr].Balance) != 0 {
   217  			modified = true
   218  			postAccount.Balance = newBalance
   219  		}
   220  		if newNonce != t.pre[addr].Nonce {
   221  			modified = true
   222  			postAccount.Nonce = newNonce
   223  		}
   224  		if !bytes.Equal(newCode, t.pre[addr].Code) {
   225  			modified = true
   226  			postAccount.Code = newCode
   227  		}
   228  
   229  		for key, val := range state.Storage {
   230  			// don't include the empty slot
   231  			if val == (common.Hash{}) {
   232  				delete(t.pre[addr].Storage, key)
   233  			}
   234  
   235  			newVal := t.env.StateDB.GetState(addr, key)
   236  			if val == newVal {
   237  				// Omit unchanged slots
   238  				delete(t.pre[addr].Storage, key)
   239  			} else {
   240  				modified = true
   241  				if newVal != (common.Hash{}) {
   242  					postAccount.Storage[key] = newVal
   243  				}
   244  			}
   245  		}
   246  
   247  		if modified {
   248  			t.post[addr] = postAccount
   249  		} else {
   250  			// if state is not modified, then no need to include into the pre state
   251  			delete(t.pre, addr)
   252  		}
   253  	}
   254  }
   255  
   256  // lookupAccount fetches details of an account and adds it to the prestate
   257  // if it doesn't exist there.
   258  func (t *prestateTracer) lookupAccount(addr common.Address) {
   259  	if _, ok := t.pre[addr]; ok {
   260  		return
   261  	}
   262  
   263  	acc := &account{
   264  		Balance: t.env.StateDB.GetBalance(addr).ToBig(),
   265  		Nonce:   t.env.StateDB.GetNonce(addr),
   266  		Code:    t.env.StateDB.GetCode(addr),
   267  		Storage: make(map[common.Hash]common.Hash),
   268  	}
   269  	if !acc.exists() {
   270  		acc.empty = true
   271  	}
   272  	t.pre[addr] = acc
   273  }
   274  
   275  // lookupStorage fetches the requested storage slot and adds
   276  // it to the prestate of the given contract. It assumes `lookupAccount`
   277  // has been performed on the contract before.
   278  func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) {
   279  	if _, ok := t.pre[addr].Storage[key]; ok {
   280  		return
   281  	}
   282  	t.pre[addr].Storage[key] = t.env.StateDB.GetState(addr, key)
   283  }