github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/evm/evm.go (about)

     1  // Copyright Monax Industries Limited
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package evm
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/hyperledger/burrow/acm"
    10  	"github.com/hyperledger/burrow/acm/acmstate"
    11  	"github.com/hyperledger/burrow/execution/defaults"
    12  	"github.com/hyperledger/burrow/execution/engine"
    13  	"github.com/hyperledger/burrow/execution/exec"
    14  	"github.com/hyperledger/burrow/execution/native"
    15  	"github.com/hyperledger/burrow/logging"
    16  )
    17  
    18  const (
    19  	DataStackInitialCapacity    = 1024
    20  	MaximumAllowedBlockLookBack = 256
    21  	uint64Length                = 8
    22  )
    23  
    24  type EVM struct {
    25  	options  engine.Options
    26  	sequence uint64
    27  	// Provide any foreign dispatchers to allow calls between VMs
    28  	engine.Externals
    29  	externalDispatcher engine.Dispatcher
    30  	// User dispatcher.CallableProvider to get access to other VMs
    31  	logger *logging.Logger
    32  }
    33  
    34  func New(options engine.Options) *EVM {
    35  	options = defaults.CompleteOptions(options)
    36  	vm := &EVM{
    37  		options: options,
    38  	}
    39  	vm.logger = options.Logger.WithScope("NewVM").With("evm_nonce", options.Nonce)
    40  	vm.externalDispatcher = engine.Dispatchers{&vm.Externals, options.Natives, vm}
    41  	return vm
    42  }
    43  
    44  func Default() *EVM {
    45  	return New(engine.Options{})
    46  }
    47  
    48  // Initiate an EVM call against the provided state pushing events to eventSink. code should contain the EVM bytecode,
    49  // input the CallData (readable by CALLDATALOAD), value the amount of native token to transfer with the call
    50  // an quantity metering the number of computational steps available to the execution according to the gas schedule.
    51  func (vm *EVM) Execute(st acmstate.ReaderWriter, blockchain engine.Blockchain, eventSink exec.EventSink,
    52  	params engine.CallParams, code []byte) ([]byte, error) {
    53  
    54  	// Make it appear as if natives are stored in state
    55  	st = native.NewState(vm.options.Natives, st)
    56  
    57  	state := engine.State{
    58  		CallFrame:  engine.NewCallFrame(st).WithMaxCallStackDepth(vm.options.CallStackMaxDepth),
    59  		Blockchain: blockchain,
    60  		EventSink:  eventSink,
    61  	}
    62  
    63  	output, err := vm.Contract(code).Call(state, params)
    64  	if err == nil {
    65  		// Only sync back when there was no exception
    66  		err = state.CallFrame.Sync()
    67  	}
    68  	// Always return output - we may have a reverted exception for which the return is meaningful
    69  	return output, err
    70  }
    71  
    72  // Sets a new nonce and resets the sequence number. Nonces should only be used once!
    73  // A global counter or sufficient randomness will work.
    74  func (vm *EVM) SetNonce(nonce []byte) {
    75  	vm.options.Nonce = nonce
    76  	vm.sequence = 0
    77  }
    78  
    79  func (vm *EVM) SetLogger(logger *logging.Logger) {
    80  	vm.logger = logger
    81  }
    82  
    83  func (vm *EVM) Dispatch(acc *acm.Account) engine.Callable {
    84  	// Let the EVM handle code-less (e.g. those created by a call) contracts (so only return nil if there is _other_ non-EVM code)
    85  	if len(acc.EVMCode) == 0 && len(acc.Code()) != 0 {
    86  		return nil
    87  	}
    88  	return vm.Contract(acc.EVMCode)
    89  }
    90  
    91  func (vm *EVM) Contract(code []byte) *Contract {
    92  	return &Contract{
    93  		EVM:  vm,
    94  		Code: NewCode(code),
    95  	}
    96  }
    97  
    98  func (vm *EVM) debugf(format string, a ...interface{}) {
    99  	if vm.options.DebugOpcodes {
   100  		fmt.Printf(format, a...)
   101  	}
   102  }