github.com/theQRL/go-zond@v0.1.1/zond/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/theQRL/go-zond/common"
    26  	"github.com/theQRL/go-zond/common/hexutil"
    27  	"github.com/theQRL/go-zond/core/vm"
    28  	"github.com/theQRL/go-zond/crypto"
    29  	"github.com/theQRL/go-zond/log"
    30  	"github.com/theQRL/go-zond/zond/tracers"
    31  )
    32  
    33  //go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
    34  
    35  func init() {
    36  	tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
    37  }
    38  
    39  type state = map[common.Address]*account
    40  
    41  type account struct {
    42  	Balance *big.Int                    `json:"balance,omitempty"`
    43  	Code    []byte                      `json:"code,omitempty"`
    44  	Nonce   uint64                      `json:"nonce,omitempty"`
    45  	Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
    46  }
    47  
    48  func (a *account) exists() bool {
    49  	return a.Nonce > 0 || len(a.Code) > 0 || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0)
    50  }
    51  
    52  type accountMarshaling struct {
    53  	Balance *hexutil.Big
    54  	Code    hexutil.Bytes
    55  }
    56  
    57  type prestateTracer struct {
    58  	noopTracer
    59  	env       *vm.EVM
    60  	pre       state
    61  	post      state
    62  	create    bool
    63  	to        common.Address
    64  	gasLimit  uint64 // Amount of gas bought for the whole tx
    65  	config    prestateTracerConfig
    66  	interrupt atomic.Bool // Atomic flag to signal execution interruption
    67  	reason    error       // Textual reason for the interruption
    68  	created   map[common.Address]bool
    69  	deleted   map[common.Address]bool
    70  }
    71  
    72  type prestateTracerConfig struct {
    73  	DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
    74  }
    75  
    76  func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
    77  	var config prestateTracerConfig
    78  	if cfg != nil {
    79  		if err := json.Unmarshal(cfg, &config); err != nil {
    80  			return nil, err
    81  		}
    82  	}
    83  	return &prestateTracer{
    84  		pre:     state{},
    85  		post:    state{},
    86  		config:  config,
    87  		created: make(map[common.Address]bool),
    88  		deleted: make(map[common.Address]bool),
    89  	}, nil
    90  }
    91  
    92  // CaptureStart implements the EVMLogger interface to initialize the tracing operation.
    93  func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
    94  	t.env = env
    95  	t.create = create
    96  	t.to = to
    97  
    98  	t.lookupAccount(from)
    99  	t.lookupAccount(to)
   100  	t.lookupAccount(env.Context.Coinbase)
   101  
   102  	// The recipient balance includes the value transferred.
   103  	toBal := new(big.Int).Sub(t.pre[to].Balance, value)
   104  	t.pre[to].Balance = toBal
   105  
   106  	// The sender balance is after reducing: value and gasLimit.
   107  	// We need to re-add them to get the pre-tx balance.
   108  	fromBal := new(big.Int).Set(t.pre[from].Balance)
   109  	gasPrice := env.TxContext.GasPrice
   110  	consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
   111  	fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
   112  	t.pre[from].Balance = fromBal
   113  	t.pre[from].Nonce--
   114  
   115  	if create && t.config.DiffMode {
   116  		t.created[to] = true
   117  	}
   118  }
   119  
   120  // CaptureEnd is called after the call finishes to finalize the tracing.
   121  func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
   122  	if t.config.DiffMode {
   123  		return
   124  	}
   125  
   126  	if t.create {
   127  		// Keep existing account prior to contract creation at that address
   128  		if s := t.pre[t.to]; s != nil && !s.exists() {
   129  			// Exclude newly created contract.
   130  			delete(t.pre, t.to)
   131  		}
   132  	}
   133  }
   134  
   135  // CaptureState implements the EVMLogger interface to trace a single step of VM execution.
   136  func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
   137  	if err != nil {
   138  		return
   139  	}
   140  	// Skip if tracing was interrupted
   141  	if t.interrupt.Load() {
   142  		return
   143  	}
   144  	stack := scope.Stack
   145  	stackData := stack.Data()
   146  	stackLen := len(stackData)
   147  	caller := scope.Contract.Address()
   148  	switch {
   149  	case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
   150  		slot := common.Hash(stackData[stackLen-1].Bytes32())
   151  		t.lookupStorage(caller, slot)
   152  	case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT):
   153  		addr := common.Address(stackData[stackLen-1].Bytes20())
   154  		t.lookupAccount(addr)
   155  		if op == vm.SELFDESTRUCT {
   156  			t.deleted[caller] = true
   157  		}
   158  	case stackLen >= 5 && (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE):
   159  		addr := common.Address(stackData[stackLen-2].Bytes20())
   160  		t.lookupAccount(addr)
   161  	case op == vm.CREATE:
   162  		nonce := t.env.StateDB.GetNonce(caller)
   163  		addr := crypto.CreateAddress(caller, nonce)
   164  		t.lookupAccount(addr)
   165  		t.created[addr] = true
   166  	case stackLen >= 4 && op == vm.CREATE2:
   167  		offset := stackData[stackLen-2]
   168  		size := stackData[stackLen-3]
   169  		init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
   170  		if err != nil {
   171  			log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
   172  			return
   173  		}
   174  		inithash := crypto.Keccak256(init)
   175  		salt := stackData[stackLen-4]
   176  		addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
   177  		t.lookupAccount(addr)
   178  		t.created[addr] = true
   179  	}
   180  }
   181  
   182  func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
   183  	t.gasLimit = gasLimit
   184  }
   185  
   186  func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
   187  	if !t.config.DiffMode {
   188  		return
   189  	}
   190  
   191  	for addr, state := range t.pre {
   192  		// The deleted account's state is pruned from `post` but kept in `pre`
   193  		if _, ok := t.deleted[addr]; ok {
   194  			continue
   195  		}
   196  		modified := false
   197  		postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
   198  		newBalance := t.env.StateDB.GetBalance(addr)
   199  		newNonce := t.env.StateDB.GetNonce(addr)
   200  		newCode := t.env.StateDB.GetCode(addr)
   201  
   202  		if newBalance.Cmp(t.pre[addr].Balance) != 0 {
   203  			modified = true
   204  			postAccount.Balance = newBalance
   205  		}
   206  		if newNonce != t.pre[addr].Nonce {
   207  			modified = true
   208  			postAccount.Nonce = newNonce
   209  		}
   210  		if !bytes.Equal(newCode, t.pre[addr].Code) {
   211  			modified = true
   212  			postAccount.Code = newCode
   213  		}
   214  
   215  		for key, val := range state.Storage {
   216  			// don't include the empty slot
   217  			if val == (common.Hash{}) {
   218  				delete(t.pre[addr].Storage, key)
   219  			}
   220  
   221  			newVal := t.env.StateDB.GetState(addr, key)
   222  			if val == newVal {
   223  				// Omit unchanged slots
   224  				delete(t.pre[addr].Storage, key)
   225  			} else {
   226  				modified = true
   227  				if newVal != (common.Hash{}) {
   228  					postAccount.Storage[key] = newVal
   229  				}
   230  			}
   231  		}
   232  
   233  		if modified {
   234  			t.post[addr] = postAccount
   235  		} else {
   236  			// if state is not modified, then no need to include into the pre state
   237  			delete(t.pre, addr)
   238  		}
   239  	}
   240  	// the new created contracts' prestate were empty, so delete them
   241  	for a := range t.created {
   242  		// the created contract maybe exists in statedb before the creating tx
   243  		if s := t.pre[a]; s != nil && !s.exists() {
   244  			delete(t.pre, a)
   245  		}
   246  	}
   247  }
   248  
   249  // GetResult returns the json-encoded nested list of call traces, and any
   250  // error arising from the encoding or forceful termination (via `Stop`).
   251  func (t *prestateTracer) GetResult() (json.RawMessage, error) {
   252  	var res []byte
   253  	var err error
   254  	if t.config.DiffMode {
   255  		res, err = json.Marshal(struct {
   256  			Post state `json:"post"`
   257  			Pre  state `json:"pre"`
   258  		}{t.post, t.pre})
   259  	} else {
   260  		res, err = json.Marshal(t.pre)
   261  	}
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	return json.RawMessage(res), t.reason
   266  }
   267  
   268  // Stop terminates execution of the tracer at the first opportune moment.
   269  func (t *prestateTracer) Stop(err error) {
   270  	t.reason = err
   271  	t.interrupt.Store(true)
   272  }
   273  
   274  // lookupAccount fetches details of an account and adds it to the prestate
   275  // if it doesn't exist there.
   276  func (t *prestateTracer) lookupAccount(addr common.Address) {
   277  	if _, ok := t.pre[addr]; ok {
   278  		return
   279  	}
   280  
   281  	t.pre[addr] = &account{
   282  		Balance: t.env.StateDB.GetBalance(addr),
   283  		Nonce:   t.env.StateDB.GetNonce(addr),
   284  		Code:    t.env.StateDB.GetCode(addr),
   285  		Storage: make(map[common.Hash]common.Hash),
   286  	}
   287  }
   288  
   289  // lookupStorage fetches the requested storage slot and adds
   290  // it to the prestate of the given contract. It assumes `lookupAccount`
   291  // has been performed on the contract before.
   292  func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) {
   293  	if _, ok := t.pre[addr].Storage[key]; ok {
   294  		return
   295  	}
   296  	t.pre[addr].Storage[key] = t.env.StateDB.GetState(addr, key)
   297  }