github.com/MetalBlockchain/subnet-evm@v0.4.9/eth/tracers/native/prestate.go (about)

     1  // (c) 2020-2021, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2022 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package native
    28  
    29  import (
    30  	"encoding/json"
    31  	"math/big"
    32  	"sync/atomic"
    33  	"time"
    34  
    35  	"github.com/MetalBlockchain/subnet-evm/core/vm"
    36  	"github.com/MetalBlockchain/subnet-evm/eth/tracers"
    37  	"github.com/ethereum/go-ethereum/common"
    38  	"github.com/ethereum/go-ethereum/common/hexutil"
    39  	"github.com/ethereum/go-ethereum/crypto"
    40  )
    41  
    42  func init() {
    43  	register("prestateTracer", newPrestateTracer)
    44  }
    45  
    46  type prestate = map[common.Address]*account
    47  type account struct {
    48  	Balance string                      `json:"balance"`
    49  	Nonce   uint64                      `json:"nonce"`
    50  	Code    string                      `json:"code"`
    51  	Storage map[common.Hash]common.Hash `json:"storage"`
    52  }
    53  
    54  type prestateTracer struct {
    55  	env       *vm.EVM
    56  	prestate  prestate
    57  	create    bool
    58  	to        common.Address
    59  	gasLimit  uint64 // Amount of gas bought for the whole tx
    60  	interrupt uint32 // Atomic flag to signal execution interruption
    61  	reason    error  // Textual reason for the interruption
    62  }
    63  
    64  func newPrestateTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
    65  	// First callframe contains tx context info
    66  	// and is populated on start and end.
    67  	return &prestateTracer{prestate: prestate{}}, nil
    68  }
    69  
    70  // CaptureStart implements the EVMLogger interface to initialize the tracing operation.
    71  func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
    72  	t.env = env
    73  	t.create = create
    74  	t.to = to
    75  
    76  	t.lookupAccount(from)
    77  	t.lookupAccount(to)
    78  
    79  	// The recipient balance includes the value transferred.
    80  	toBal := hexutil.MustDecodeBig(t.prestate[to].Balance)
    81  	toBal = new(big.Int).Sub(toBal, value)
    82  	t.prestate[to].Balance = hexutil.EncodeBig(toBal)
    83  
    84  	// The sender balance is after reducing: value and gasLimit.
    85  	// We need to re-add them to get the pre-tx balance.
    86  	fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance)
    87  	gasPrice := env.TxContext.GasPrice
    88  	consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
    89  	fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
    90  	t.prestate[from].Balance = hexutil.EncodeBig(fromBal)
    91  	t.prestate[from].Nonce--
    92  }
    93  
    94  // CaptureEnd is called after the call finishes to finalize the tracing.
    95  func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
    96  	if t.create {
    97  		// Exclude created contract.
    98  		delete(t.prestate, t.to)
    99  	}
   100  }
   101  
   102  // CaptureState implements the EVMLogger interface to trace a single step of VM execution.
   103  func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
   104  	stack := scope.Stack
   105  	stackData := stack.Data()
   106  	stackLen := len(stackData)
   107  	switch {
   108  	case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
   109  		slot := common.Hash(stackData[stackLen-1].Bytes32())
   110  		t.lookupStorage(scope.Contract.Address(), slot)
   111  	case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT):
   112  		addr := common.Address(stackData[stackLen-1].Bytes20())
   113  		t.lookupAccount(addr)
   114  	case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE):
   115  		addr := common.Address(stackData[stackLen-2].Bytes20())
   116  		t.lookupAccount(addr)
   117  	case op == vm.CREATE:
   118  		addr := scope.Contract.Address()
   119  		nonce := t.env.StateDB.GetNonce(addr)
   120  		t.lookupAccount(crypto.CreateAddress(addr, nonce))
   121  	case stackLen >= 4 && op == vm.CREATE2:
   122  		offset := stackData[stackLen-2]
   123  		size := stackData[stackLen-3]
   124  		init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
   125  		inithash := crypto.Keccak256(init)
   126  		salt := stackData[stackLen-4]
   127  		t.lookupAccount(crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), inithash))
   128  	}
   129  }
   130  
   131  // CaptureFault implements the EVMLogger interface to trace an execution fault.
   132  func (t *prestateTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
   133  }
   134  
   135  // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
   136  func (t *prestateTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   137  }
   138  
   139  func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
   140  	t.gasLimit = gasLimit
   141  }
   142  
   143  func (t *prestateTracer) CaptureTxEnd(restGas uint64) {}
   144  
   145  // CaptureExit is called when EVM exits a scope, even if the scope didn't
   146  // execute any code.
   147  func (t *prestateTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
   148  }
   149  
   150  // GetResult returns the json-encoded nested list of call traces, and any
   151  // error arising from the encoding or forceful termination (via `Stop`).
   152  func (t *prestateTracer) GetResult() (json.RawMessage, error) {
   153  	res, err := json.Marshal(t.prestate)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return json.RawMessage(res), t.reason
   158  }
   159  
   160  // Stop terminates execution of the tracer at the first opportune moment.
   161  func (t *prestateTracer) Stop(err error) {
   162  	t.reason = err
   163  	atomic.StoreUint32(&t.interrupt, 1)
   164  }
   165  
   166  // lookupAccount fetches details of an account and adds it to the prestate
   167  // if it doesn't exist there.
   168  func (t *prestateTracer) lookupAccount(addr common.Address) {
   169  	if _, ok := t.prestate[addr]; ok {
   170  		return
   171  	}
   172  	t.prestate[addr] = &account{
   173  		Balance: bigToHex(t.env.StateDB.GetBalance(addr)),
   174  		Nonce:   t.env.StateDB.GetNonce(addr),
   175  		Code:    bytesToHex(t.env.StateDB.GetCode(addr)),
   176  		Storage: make(map[common.Hash]common.Hash),
   177  	}
   178  }
   179  
   180  // lookupStorage fetches the requested storage slot and adds
   181  // it to the prestate of the given contract. It assumes `lookupAccount`
   182  // has been performed on the contract before.
   183  func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) {
   184  	if _, ok := t.prestate[addr].Storage[key]; ok {
   185  		return
   186  	}
   187  	t.prestate[addr].Storage[key] = t.env.StateDB.GetState(addr, key)
   188  }