github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/emulator/emulator.go (about)

     1  package emulator
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  
     7  	"github.com/onflow/atree"
     8  	gethCommon "github.com/onflow/go-ethereum/common"
     9  	gethCore "github.com/onflow/go-ethereum/core"
    10  	gethTypes "github.com/onflow/go-ethereum/core/types"
    11  	gethVM "github.com/onflow/go-ethereum/core/vm"
    12  	gethCrypto "github.com/onflow/go-ethereum/crypto"
    13  	gethParams "github.com/onflow/go-ethereum/params"
    14  
    15  	"github.com/onflow/flow-go/fvm/evm/emulator/state"
    16  	"github.com/onflow/flow-go/fvm/evm/types"
    17  	"github.com/onflow/flow-go/model/flow"
    18  )
    19  
    20  // Emulator handles operations against evm runtime
    21  type Emulator struct {
    22  	rootAddr flow.Address
    23  	ledger   atree.Ledger
    24  }
    25  
    26  var _ types.Emulator = &Emulator{}
    27  
    28  // NewEmulator constructs a new EVM Emulator
    29  func NewEmulator(
    30  	ledger atree.Ledger,
    31  	rootAddr flow.Address,
    32  ) *Emulator {
    33  	return &Emulator{
    34  		rootAddr: rootAddr,
    35  		ledger:   ledger,
    36  	}
    37  }
    38  
    39  func newConfig(ctx types.BlockContext) *Config {
    40  	return NewConfig(
    41  		WithChainID(ctx.ChainID),
    42  		WithBlockNumber(new(big.Int).SetUint64(ctx.BlockNumber)),
    43  		WithBlockTime(ctx.BlockTimestamp),
    44  		WithCoinbase(ctx.GasFeeCollector.ToCommon()),
    45  		WithDirectCallBaseGasUsage(ctx.DirectCallBaseGasUsage),
    46  		WithExtraPrecompiles(ctx.ExtraPrecompiles),
    47  		WithGetBlockHashFunction(ctx.GetHashFunc),
    48  		WithRandom(&ctx.Random),
    49  	)
    50  }
    51  
    52  // NewReadOnlyBlockView constructs a new readonly block view
    53  func (em *Emulator) NewReadOnlyBlockView(ctx types.BlockContext) (types.ReadOnlyBlockView, error) {
    54  	execState, err := state.NewStateDB(em.ledger, em.rootAddr)
    55  	return &ReadOnlyBlockView{
    56  		state: execState,
    57  	}, err
    58  }
    59  
    60  // NewBlockView constructs a new block view (mutable)
    61  func (em *Emulator) NewBlockView(ctx types.BlockContext) (types.BlockView, error) {
    62  	cfg := newConfig(ctx)
    63  	return &BlockView{
    64  		config:   cfg,
    65  		rootAddr: em.rootAddr,
    66  		ledger:   em.ledger,
    67  	}, nil
    68  }
    69  
    70  // ReadOnlyBlockView provides a read only view of a block
    71  // could be used multiple times for queries
    72  type ReadOnlyBlockView struct {
    73  	state types.StateDB
    74  }
    75  
    76  // BalanceOf returns the balance of the given address
    77  func (bv *ReadOnlyBlockView) BalanceOf(address types.Address) (*big.Int, error) {
    78  	return bv.state.GetBalance(address.ToCommon()), nil
    79  }
    80  
    81  // NonceOf returns the nonce of the given address
    82  func (bv *ReadOnlyBlockView) NonceOf(address types.Address) (uint64, error) {
    83  	return bv.state.GetNonce(address.ToCommon()), nil
    84  }
    85  
    86  // CodeOf returns the code of the given address
    87  func (bv *ReadOnlyBlockView) CodeOf(address types.Address) (types.Code, error) {
    88  	return bv.state.GetCode(address.ToCommon()), nil
    89  }
    90  
    91  // CodeHashOf returns the code hash of the given address
    92  func (bv *ReadOnlyBlockView) CodeHashOf(address types.Address) ([]byte, error) {
    93  	return bv.state.GetCodeHash(address.ToCommon()).Bytes(), nil
    94  }
    95  
    96  // BlockView allows mutation of the evm state as part of a block
    97  //
    98  // TODO: allow  multiple calls per block view
    99  // TODO: add block level commit (separation of trie commit to storage)
   100  type BlockView struct {
   101  	config   *Config
   102  	rootAddr flow.Address
   103  	ledger   atree.Ledger
   104  }
   105  
   106  // DirectCall executes a direct call
   107  func (bl *BlockView) DirectCall(call *types.DirectCall) (*types.Result, error) {
   108  	proc, err := bl.newProcedure()
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	txHash, err := call.Hash()
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	switch call.SubType {
   117  	case types.DepositCallSubType:
   118  		return proc.mintTo(call, txHash)
   119  	case types.WithdrawCallSubType:
   120  		return proc.withdrawFrom(call, txHash)
   121  	case types.DeployCallSubType:
   122  		if !call.EmptyToField() {
   123  			return proc.deployAt(call.From, call.To, call.Data, call.GasLimit, call.Value, txHash)
   124  		}
   125  		fallthrough
   126  	default:
   127  		// TODO: when we support mutiple calls per block, we need
   128  		// to update the value zero here for tx index
   129  		return proc.runDirect(call.Message(), txHash, 0)
   130  	}
   131  }
   132  
   133  // RunTransaction runs an evm transaction
   134  func (bl *BlockView) RunTransaction(
   135  	tx *gethTypes.Transaction,
   136  ) (*types.Result, error) {
   137  	proc, err := bl.newProcedure()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	msg, err := gethCore.TransactionToMessage(
   143  		tx,
   144  		GetSigner(bl.config),
   145  		proc.config.BlockContext.BaseFee)
   146  	if err != nil {
   147  		// this is not a fatal error (e.g. due to bad signature)
   148  		// not a valid transaction
   149  		return types.NewInvalidResult(tx, err), nil
   150  	}
   151  
   152  	// update tx context origin
   153  	proc.evm.TxContext.Origin = msg.From
   154  	res, err := proc.run(msg, tx.Hash(), 0, tx.Type())
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	// all commmit errors (StateDB errors) has to be returned
   159  	if err := proc.commit(true); err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	return res, nil
   164  }
   165  
   166  func (bl *BlockView) BatchRunTransactions(txs []*gethTypes.Transaction) ([]*types.Result, error) {
   167  	batchResults := make([]*types.Result, len(txs))
   168  
   169  	proc, err := bl.newProcedure()
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	for i, tx := range txs {
   175  		msg, err := gethCore.TransactionToMessage(
   176  			tx,
   177  			GetSigner(bl.config),
   178  			proc.config.BlockContext.BaseFee)
   179  		if err != nil {
   180  			batchResults[i] = types.NewInvalidResult(tx, err)
   181  			continue
   182  		}
   183  
   184  		// update tx context origin
   185  		proc.evm.TxContext.Origin = msg.From
   186  		res, err := proc.run(msg, tx.Hash(), uint(i), tx.Type())
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  		// all commmit errors (StateDB errors) has to be returned
   191  		if err := proc.commit(false); err != nil {
   192  			return nil, err
   193  		}
   194  
   195  		// this clears state for any subsequent transaction runs
   196  		proc.state.Reset()
   197  
   198  		batchResults[i] = res
   199  	}
   200  
   201  	// finalize after all the batch transactions are executed to save resources
   202  	if err := proc.state.Finalize(); err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	return batchResults, nil
   207  }
   208  
   209  // DryRunTransaction run unsigned transaction without persisting the state
   210  func (bl *BlockView) DryRunTransaction(
   211  	tx *gethTypes.Transaction,
   212  	from gethCommon.Address,
   213  ) (*types.Result, error) {
   214  	proc, err := bl.newProcedure()
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	msg, err := gethCore.TransactionToMessage(
   220  		tx,
   221  		GetSigner(bl.config),
   222  		proc.config.BlockContext.BaseFee,
   223  	)
   224  	// we can ignore invalid signature errors since we don't expect signed transctions
   225  	if !errors.Is(err, gethTypes.ErrInvalidSig) {
   226  		return nil, err
   227  	}
   228  
   229  	// use the from as the signer
   230  	proc.evm.TxContext.Origin = from
   231  	msg.From = from
   232  	// we need to skip nonce check for dry run
   233  	msg.SkipAccountChecks = true
   234  
   235  	// return without commiting the state
   236  	return proc.run(msg, tx.Hash(), 0, tx.Type())
   237  }
   238  
   239  func (bl *BlockView) newProcedure() (*procedure, error) {
   240  	execState, err := state.NewStateDB(bl.ledger, bl.rootAddr)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	cfg := bl.config
   245  	return &procedure{
   246  		config: cfg,
   247  		evm: gethVM.NewEVM(
   248  			*cfg.BlockContext,
   249  			*cfg.TxContext,
   250  			execState,
   251  			cfg.ChainConfig,
   252  			cfg.EVMConfig,
   253  		),
   254  		state: execState,
   255  	}, nil
   256  }
   257  
   258  type procedure struct {
   259  	config *Config
   260  	evm    *gethVM.EVM
   261  	state  types.StateDB
   262  }
   263  
   264  // commit commits the changes to the state (with optional finalization)
   265  func (proc *procedure) commit(finalize bool) error {
   266  	err := proc.state.Commit(finalize)
   267  	if err != nil {
   268  		// if known types (state errors) don't do anything and return
   269  		if types.IsAFatalError(err) || types.IsAStateError(err) {
   270  			return err
   271  		}
   272  
   273  		// else is a new fatal error
   274  		return types.NewFatalError(err)
   275  	}
   276  	return nil
   277  }
   278  
   279  func (proc *procedure) mintTo(
   280  	call *types.DirectCall,
   281  	txHash gethCommon.Hash,
   282  ) (*types.Result, error) {
   283  	bridge := call.From.ToCommon()
   284  
   285  	// create bridge account if not exist
   286  	if !proc.state.Exist(bridge) {
   287  		proc.state.CreateAccount(bridge)
   288  	}
   289  
   290  	// add balance to the bridge account before transfer
   291  	proc.state.AddBalance(bridge, call.Value)
   292  
   293  	msg := call.Message()
   294  	proc.evm.TxContext.Origin = msg.From
   295  	// withdraw the amount and move it to the bridge account
   296  	res, err := proc.run(msg, txHash, 0, types.DirectCallTxType)
   297  	if err != nil {
   298  		return res, err
   299  	}
   300  
   301  	// if any error (invalid or vm) on the internal call, revert and don't commit any change
   302  	// this prevents having cases that we add balance to the bridge but the transfer
   303  	// fails due to gas, etc.
   304  	// TODO: in the future we might just return without error and handle everything on higher level
   305  	if res.Invalid() || res.Failed() {
   306  		return res, types.ErrInternalDirectCallFailed
   307  	}
   308  
   309  	// all commmit errors (StateDB errors) has to be returned
   310  	return res, proc.commit(true)
   311  }
   312  
   313  func (proc *procedure) withdrawFrom(
   314  	call *types.DirectCall,
   315  	txHash gethCommon.Hash,
   316  ) (*types.Result, error) {
   317  	bridge := call.To.ToCommon()
   318  
   319  	// create bridge account if not exist
   320  	if !proc.state.Exist(bridge) {
   321  		proc.state.CreateAccount(bridge)
   322  	}
   323  
   324  	// withdraw the amount and move it to the bridge account
   325  	msg := call.Message()
   326  	proc.evm.TxContext.Origin = msg.From
   327  	res, err := proc.run(msg, txHash, 0, types.DirectCallTxType)
   328  	if err != nil {
   329  		return res, err
   330  	}
   331  
   332  	// if any error (invalid or vm) on the internal call, revert and don't commit any change
   333  	// TODO: in the future we might just return without error and handle everything on higher level
   334  	if res.Invalid() || res.Failed() {
   335  		return res, types.ErrInternalDirectCallFailed
   336  	}
   337  
   338  	// now deduct the balance from the bridge
   339  	proc.state.SubBalance(bridge, call.Value)
   340  	// all commmit errors (StateDB errors) has to be returned
   341  	return res, proc.commit(true)
   342  }
   343  
   344  // deployAt deploys a contract at the given target address
   345  // behaviour should be similar to what evm.create internal method does with
   346  // a few differences, don't need to check for previous forks given this
   347  // functionality was not available to anyone, we don't need to
   348  // follow snapshoting, given we do commit/revert style in this code base.
   349  // in the future we might optimize this method accepting deploy-ready byte codes
   350  // and skip interpreter call, gas calculations and many checks.
   351  func (proc *procedure) deployAt(
   352  	caller types.Address,
   353  	to types.Address,
   354  	data types.Code,
   355  	gasLimit uint64,
   356  	value *big.Int,
   357  	txHash gethCommon.Hash,
   358  ) (*types.Result, error) {
   359  	if value.Sign() < 0 {
   360  		return nil, types.ErrInvalidBalance
   361  	}
   362  
   363  	res := &types.Result{
   364  		TxType: types.DirectCallTxType,
   365  		TxHash: txHash,
   366  	}
   367  
   368  	addr := to.ToCommon()
   369  
   370  	// precheck 1 - check balance of the source
   371  	if value.Sign() != 0 &&
   372  		!proc.evm.Context.CanTransfer(proc.state, caller.ToCommon(), value) {
   373  		res.SetValidationError(gethCore.ErrInsufficientFundsForTransfer)
   374  		return res, nil
   375  	}
   376  
   377  	// precheck 2 - ensure there's no existing eoa or contract is deployed at the address
   378  	contractHash := proc.state.GetCodeHash(addr)
   379  	if proc.state.GetNonce(addr) != 0 ||
   380  		(contractHash != (gethCommon.Hash{}) && contractHash != gethTypes.EmptyCodeHash) {
   381  		res.VMError = gethVM.ErrContractAddressCollision
   382  		return res, nil
   383  	}
   384  
   385  	callerCommon := caller.ToCommon()
   386  	// setup caller if doesn't exist
   387  	if !proc.state.Exist(callerCommon) {
   388  		proc.state.CreateAccount(callerCommon)
   389  	}
   390  	// increment the nonce for the caller
   391  	proc.state.SetNonce(callerCommon, proc.state.GetNonce(callerCommon)+1)
   392  
   393  	// setup account
   394  	proc.state.CreateAccount(addr)
   395  	proc.state.SetNonce(addr, 1) // (EIP-158)
   396  	if value.Sign() > 0 {
   397  		proc.evm.Context.Transfer( // transfer value
   398  			proc.state,
   399  			caller.ToCommon(),
   400  			addr,
   401  			value,
   402  		)
   403  	}
   404  
   405  	// run code through interpreter
   406  	// this would check for errors and computes the final bytes to be stored under account
   407  	var err error
   408  	inter := gethVM.NewEVMInterpreter(proc.evm)
   409  	contract := gethVM.NewContract(
   410  		gethVM.AccountRef(caller.ToCommon()),
   411  		gethVM.AccountRef(addr),
   412  		value,
   413  		gasLimit)
   414  
   415  	contract.SetCallCode(&addr, gethCrypto.Keccak256Hash(data), data)
   416  	// update access list (Berlin)
   417  	proc.state.AddAddressToAccessList(addr)
   418  
   419  	ret, err := inter.Run(contract, nil, false)
   420  	gasCost := uint64(len(ret)) * gethParams.CreateDataGas
   421  	res.GasConsumed = gasCost
   422  
   423  	// handle errors
   424  	if err != nil {
   425  		// for all errors except this one consume all the remaining gas (Homestead)
   426  		if err != gethVM.ErrExecutionReverted {
   427  			res.GasConsumed = gasLimit
   428  		}
   429  		res.VMError = err
   430  		return res, nil
   431  	}
   432  
   433  	// update gas usage
   434  	if gasCost > gasLimit {
   435  		// consume all the remaining gas (Homestead)
   436  		res.GasConsumed = gasLimit
   437  		res.VMError = gethVM.ErrCodeStoreOutOfGas
   438  		return res, nil
   439  	}
   440  
   441  	// check max code size (EIP-158)
   442  	if len(ret) > gethParams.MaxCodeSize {
   443  		// consume all the remaining gas (Homestead)
   444  		res.GasConsumed = gasLimit
   445  		res.VMError = gethVM.ErrMaxCodeSizeExceeded
   446  		return res, nil
   447  	}
   448  
   449  	// reject code starting with 0xEF (EIP-3541)
   450  	if len(ret) >= 1 && ret[0] == 0xEF {
   451  		// consume all the remaining gas (Homestead)
   452  		res.GasConsumed = gasLimit
   453  		res.VMError = gethVM.ErrInvalidCode
   454  		return res, nil
   455  	}
   456  
   457  	res.DeployedContractAddress = &to
   458  
   459  	proc.state.SetCode(addr, ret)
   460  	return res, proc.commit(true)
   461  }
   462  
   463  func (proc *procedure) runDirect(
   464  	msg *gethCore.Message,
   465  	txHash gethCommon.Hash,
   466  	txIndex uint,
   467  ) (*types.Result, error) {
   468  	// set the nonce for the message (needed for some opeartions like deployment)
   469  	msg.Nonce = proc.state.GetNonce(msg.From)
   470  	proc.evm.TxContext.Origin = msg.From
   471  	res, err := proc.run(msg, txHash, txIndex, types.DirectCallTxType)
   472  	if err != nil {
   473  		return nil, err
   474  	}
   475  	// all commmit errors (StateDB errors) has to be returned
   476  	return res, proc.commit(true)
   477  }
   478  
   479  // run runs a geth core.message and returns the
   480  // results, any validation or execution errors
   481  // are captured inside the result, the remaining
   482  // return errors are errors requires extra handling
   483  // on upstream (e.g. backend errors).
   484  func (proc *procedure) run(
   485  	msg *gethCore.Message,
   486  	txHash gethCommon.Hash,
   487  	txIndex uint,
   488  	txType uint8,
   489  ) (*types.Result, error) {
   490  	res := types.Result{
   491  		TxType: txType,
   492  		TxHash: txHash,
   493  	}
   494  
   495  	gasPool := (*gethCore.GasPool)(&proc.config.BlockContext.GasLimit)
   496  	execResult, err := gethCore.NewStateTransition(
   497  		proc.evm,
   498  		msg,
   499  		gasPool,
   500  	).TransitionDb()
   501  	if err != nil {
   502  		// if the error is a fatal error or a non-fatal state error or a backend err return it
   503  		// this condition should never happen given all StateDB errors are withheld for the commit time.
   504  		if types.IsAFatalError(err) || types.IsAStateError(err) || types.IsABackendError(err) {
   505  			return nil, err
   506  		}
   507  		// otherwise is a validation error (pre-check failure)
   508  		// no state change, wrap the error and return
   509  		res.SetValidationError(err)
   510  		return &res, nil
   511  	}
   512  
   513  	// if prechecks are passed, the exec result won't be nil
   514  	if execResult != nil {
   515  		res.GasConsumed = execResult.UsedGas
   516  		res.Index = uint16(txIndex)
   517  
   518  		if !execResult.Failed() { // collect vm errors
   519  			res.ReturnedValue = execResult.ReturnData
   520  			// If the transaction created a contract, store the creation address in the receipt,
   521  			if msg.To == nil {
   522  				deployedAddress := types.NewAddress(gethCrypto.CreateAddress(msg.From, msg.Nonce))
   523  				res.DeployedContractAddress = &deployedAddress
   524  			}
   525  			// replace tx index and tx hash
   526  			res.Logs = proc.state.Logs(
   527  				proc.config.BlockContext.BlockNumber.Uint64(),
   528  				txHash,
   529  				txIndex,
   530  			)
   531  		} else {
   532  			// execResult.Err is VM errors (we don't return it as error)
   533  			res.VMError = execResult.Err
   534  		}
   535  	}
   536  	return &res, nil
   537  }