github.com/dim4egster/coreth@v0.10.2/precompile/contract.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package precompile 5 6 import ( 7 "fmt" 8 "math/big" 9 10 "github.com/ethereum/go-ethereum/common" 11 ) 12 13 const ( 14 selectorLen = 4 15 ) 16 17 type RunStatefulPrecompileFunc func(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) 18 19 // PrecompileAccessibleState defines the interface exposed to stateful precompile contracts 20 type PrecompileAccessibleState interface { 21 GetStateDB() StateDB 22 GetBlockContext() BlockContext 23 NativeAssetCall(caller common.Address, input []byte, suppliedGas uint64, gasGost uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) 24 } 25 26 // BlockContext defines an interface that provides information to a stateful precompile 27 // about the block that activates the upgrade. The precompile can access this information 28 // to initialize its state. 29 type BlockContext interface { 30 Number() *big.Int 31 Timestamp() *big.Int 32 } 33 34 // ChainContext defines an interface that provides information to a stateful precompile 35 // about the chain configuration. The precompile can access this information to initialize 36 // its state. 37 type ChainConfig interface { 38 // Note: None of the existing stateful precompiles currently access chain config information 39 // in Configure so this interface is empty. 40 } 41 42 // StateDB is the interface for accessing EVM state 43 type StateDB interface { 44 GetState(common.Address, common.Hash) common.Hash 45 SetState(common.Address, common.Hash, common.Hash) 46 47 SetCode(common.Address, []byte) 48 49 SetNonce(common.Address, uint64) 50 GetNonce(common.Address) uint64 51 52 GetBalance(common.Address) *big.Int 53 AddBalance(common.Address, *big.Int) 54 SubBalance(common.Address, *big.Int) 55 56 SubBalanceMultiCoin(common.Address, common.Hash, *big.Int) 57 AddBalanceMultiCoin(common.Address, common.Hash, *big.Int) 58 GetBalanceMultiCoin(common.Address, common.Hash) *big.Int 59 60 CreateAccount(common.Address) 61 Exist(common.Address) bool 62 } 63 64 // StatefulPrecompiledContract is the interface for executing a precompiled contract 65 type StatefulPrecompiledContract interface { 66 // Run executes the precompiled contract. 67 Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) 68 } 69 70 // statefulPrecompileFunction defines a function implemented by a stateful precompile 71 type statefulPrecompileFunction struct { 72 // selector is the 4 byte function selector for this function 73 // This should be calculated from the function signature using CalculateFunctionSelector 74 selector []byte 75 // execute is performed when this function is selected 76 execute RunStatefulPrecompileFunc 77 } 78 79 // newStatefulPrecompileFunction creates a stateful precompile function with the given arguments 80 //nolint:unused,deadcode 81 func newStatefulPrecompileFunction(selector []byte, execute RunStatefulPrecompileFunc) *statefulPrecompileFunction { 82 return &statefulPrecompileFunction{ 83 selector: selector, 84 execute: execute, 85 } 86 } 87 88 // statefulPrecompileWithFunctionSelectors implements StatefulPrecompiledContract by using 4 byte function selectors to pass 89 // off responsibilities to internal execution functions. 90 // Note: because we only ever read from [functions] there no lock is required to make it thread-safe. 91 type statefulPrecompileWithFunctionSelectors struct { 92 fallback *statefulPrecompileFunction 93 functions map[string]*statefulPrecompileFunction 94 } 95 96 // newStatefulPrecompileWithFunctionSelectors generates new StatefulPrecompile using [functions] as the available functions and [fallback] 97 // as an optional fallback if there is no input data. Note: the selector of [fallback] will be ignored, so it is required to be left empty. 98 //nolint:unused,deadcode 99 func newStatefulPrecompileWithFunctionSelectors(fallback *statefulPrecompileFunction, functions []*statefulPrecompileFunction) StatefulPrecompiledContract { 100 // Ensure that if a fallback is present, it does not have a mistakenly populated function selector. 101 if fallback != nil && len(fallback.selector) != 0 { 102 panic(fmt.Errorf("fallback function cannot specify non-zero length function selector")) 103 } 104 105 // Construct the contract and populate [functions]. 106 contract := &statefulPrecompileWithFunctionSelectors{ 107 fallback: fallback, 108 functions: make(map[string]*statefulPrecompileFunction), 109 } 110 for _, function := range functions { 111 _, exists := contract.functions[string(function.selector)] 112 if exists { 113 panic(fmt.Errorf("cannot create stateful precompile with duplicated function selector: %q", function.selector)) 114 } 115 contract.functions[string(function.selector)] = function 116 } 117 118 return contract 119 } 120 121 // Run selects the function using the 4 byte function selector at the start of the input and executes the underlying function on the 122 // given arguments. 123 func (s *statefulPrecompileWithFunctionSelectors) Run(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 124 // If there is no input data present, call the fallback function if present. 125 if len(input) == 0 && s.fallback != nil { 126 return s.fallback.execute(accessibleState, caller, addr, nil, suppliedGas, readOnly) 127 } 128 129 // Otherwise, an unexpected input size will result in an error. 130 if len(input) < selectorLen { 131 return nil, suppliedGas, fmt.Errorf("missing function selector to precompile - input length (%d)", len(input)) 132 } 133 134 // Use the function selector to grab the correct function 135 selector := input[:selectorLen] 136 functionInput := input[selectorLen:] 137 function, ok := s.functions[string(selector)] 138 if !ok { 139 return nil, suppliedGas, fmt.Errorf("invalid function selector %#x", selector) 140 } 141 142 return function.execute(accessibleState, caller, addr, functionInput, suppliedGas, readOnly) 143 }