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  }