gitlab.com/flarenetwork/coreth@v0.1.1/core/vm/contracts_stateful.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package vm 5 6 import ( 7 "fmt" 8 "math/big" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/holiman/uint256" 12 "gitlab.com/flarenetwork/coreth/params" 13 ) 14 15 // PrecompiledContractsApricot contains the default set of pre-compiled Ethereum 16 // contracts used in the Istanbul release and the stateful precompiled contracts 17 // added for the Avalanche Apricot release. 18 // Apricot is incompatible with the YoloV3 Release since it does not include the 19 // BLS12-381 Curve Operations added to the set of precompiled contracts 20 21 var ( 22 genesisContractAddr = common.HexToAddress("0x0100000000000000000000000000000000000000") 23 nativeAssetBalanceAddr = common.HexToAddress("0x0100000000000000000000000000000000000001") 24 nativeAssetCallAddr = common.HexToAddress("0x0100000000000000000000000000000000000002") 25 ) 26 27 // StatefulPrecompiledContract is the interface for executing a precompiled contract 28 // This wraps the PrecompiledContracts native to Ethereum and allows adding in stateful 29 // precompiled contracts to support native Avalanche asset transfers. 30 type StatefulPrecompiledContract interface { 31 // Run executes a precompiled contract in the current state 32 // assumes that it has already been verified that [caller] can 33 // transfer [value]. 34 Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) 35 } 36 37 // wrappedPrecompiledContract implements StatefulPrecompiledContract by wrapping stateless native precompiled contracts 38 // in Ethereum. 39 type wrappedPrecompiledContract struct { 40 p PrecompiledContract 41 } 42 43 func newWrappedPrecompiledContract(p PrecompiledContract) StatefulPrecompiledContract { 44 return &wrappedPrecompiledContract{p: p} 45 } 46 47 // Run implements the StatefulPrecompiledContract interface 48 func (w *wrappedPrecompiledContract) Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 49 return RunPrecompiledContract(w.p, input, suppliedGas) 50 } 51 52 // nativeAssetBalance is a precompiled contract used to retrieve the native asset balance 53 type nativeAssetBalance struct { 54 gasCost uint64 55 } 56 57 func PackNativeAssetBalanceInput(address common.Address, assetID common.Hash) []byte { 58 input := make([]byte, 52) 59 copy(input, address.Bytes()) 60 copy(input[20:], assetID.Bytes()) 61 return input 62 } 63 64 func UnpackNativeAssetBalanceInput(input []byte) (common.Address, common.Hash, error) { 65 if len(input) != 52 { 66 return common.Address{}, common.Hash{}, fmt.Errorf("native asset balance input had unexpcted length %d", len(input)) 67 } 68 address := common.BytesToAddress(input[:20]) 69 assetID := common.Hash{} 70 assetID.SetBytes(input[20:52]) 71 return address, assetID, nil 72 } 73 74 // Run implements StatefulPrecompiledContract 75 func (b *nativeAssetBalance) Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 76 // input: encodePacked(address 20 bytes, assetID 32 bytes) 77 if suppliedGas < b.gasCost { 78 return nil, 0, ErrOutOfGas 79 } 80 remainingGas = suppliedGas - b.gasCost 81 82 address, assetID, err := UnpackNativeAssetBalanceInput(input) 83 if err != nil { 84 return nil, remainingGas, ErrExecutionReverted 85 } 86 87 res, overflow := uint256.FromBig(evm.StateDB.GetBalanceMultiCoin(address, assetID)) 88 if overflow { 89 return nil, remainingGas, ErrExecutionReverted 90 } 91 return common.LeftPadBytes(res.Bytes(), 32), remainingGas, nil 92 } 93 94 // nativeAssetCall atomically transfers a native asset to a recipient address as well as calling that 95 // address 96 type nativeAssetCall struct { 97 gasCost uint64 98 } 99 100 func PackNativeAssetCallInput(address common.Address, assetID common.Hash, assetAmount *big.Int, callData []byte) []byte { 101 input := make([]byte, 84+len(callData)) 102 copy(input[0:20], address.Bytes()) 103 copy(input[20:52], assetID.Bytes()) 104 assetAmount.FillBytes(input[52:84]) 105 copy(input[84:], callData) 106 return input 107 } 108 109 func UnpackNativeAssetCallInput(input []byte) (common.Address, *common.Hash, *big.Int, []byte, error) { 110 if len(input) < 84 { 111 return common.Address{}, nil, nil, nil, fmt.Errorf("native asset call input had unexpcted length %d", len(input)) 112 } 113 to := common.BytesToAddress(input[:20]) 114 assetID := new(common.Hash) 115 assetID.SetBytes(input[20:52]) 116 assetAmount := new(big.Int).SetBytes(input[52:84]) 117 callData := input[84:] 118 return to, assetID, assetAmount, callData, nil 119 } 120 121 // Run implements StatefulPrecompiledContract 122 func (c *nativeAssetCall) Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 123 // input: encodePacked(address 20 bytes, assetID 32 bytes, assetAmount 32 bytes, callData variable length bytes) 124 if suppliedGas < c.gasCost { 125 return nil, 0, ErrOutOfGas 126 } 127 remainingGas = suppliedGas - c.gasCost 128 129 if readOnly { 130 return nil, remainingGas, ErrExecutionReverted 131 } 132 133 to, assetID, assetAmount, callData, err := UnpackNativeAssetCallInput(input) 134 if err != nil { 135 return nil, remainingGas, ErrExecutionReverted 136 } 137 138 if assetAmount.Sign() != 0 && !evm.Context.CanTransferMC(evm.StateDB, caller.Address(), to, assetID, assetAmount) { 139 return nil, remainingGas, ErrInsufficientBalance 140 } 141 142 snapshot := evm.StateDB.Snapshot() 143 144 if !evm.StateDB.Exist(to) { 145 if remainingGas < params.CallNewAccountGas { 146 return nil, 0, ErrOutOfGas 147 } 148 remainingGas -= params.CallNewAccountGas 149 evm.StateDB.CreateAccount(to) 150 } 151 152 // Increment the call depth which is restricted to 1024 153 evm.depth++ 154 defer func() { evm.depth-- }() 155 156 // Send [assetAmount] of [assetID] to [to] address 157 evm.Context.TransferMultiCoin(evm.StateDB, caller.Address(), to, assetID, assetAmount) 158 ret, remainingGas, err = evm.Call(caller, to, callData, remainingGas, big.NewInt(0)) 159 160 // When an error was returned by the EVM or when setting the creation code 161 // above we revert to the snapshot and consume any gas remaining. Additionally 162 // when we're in homestead this also counts for code storage gas errors. 163 if err != nil { 164 evm.StateDB.RevertToSnapshot(snapshot) 165 if err != ErrExecutionReverted { 166 remainingGas = 0 167 } 168 // TODO: consider clearing up unused snapshots: 169 //} else { 170 // evm.StateDB.DiscardSnapshot(snapshot) 171 } 172 return ret, remainingGas, err 173 } 174 175 type deprecatedContract struct{} 176 177 func (_ *deprecatedContract) Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 178 return nil, suppliedGas, ErrExecutionReverted 179 }