github.com/MetalBlockchain/subnet-evm@v0.4.9/precompile/contract_native_minter.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  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"math/big"
    11  
    12  	"github.com/MetalBlockchain/subnet-evm/utils"
    13  	"github.com/MetalBlockchain/subnet-evm/vmerrs"
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/common/math"
    16  )
    17  
    18  const (
    19  	mintInputAddressSlot = iota
    20  	mintInputAmountSlot
    21  
    22  	mintInputLen = common.HashLength + common.HashLength
    23  
    24  	MintGasCost = 30_000
    25  )
    26  
    27  var (
    28  	_ StatefulPrecompileConfig = &ContractNativeMinterConfig{}
    29  	// Singleton StatefulPrecompiledContract for minting native assets by permissioned callers.
    30  	ContractNativeMinterPrecompile StatefulPrecompiledContract = createNativeMinterPrecompile(ContractNativeMinterAddress)
    31  
    32  	mintSignature = CalculateFunctionSelector("mintNativeCoin(address,uint256)") // address, amount
    33  	ErrCannotMint = errors.New("non-enabled cannot mint")
    34  )
    35  
    36  // ContractNativeMinterConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig
    37  // interface while adding in the ContractNativeMinter specific precompile address.
    38  type ContractNativeMinterConfig struct {
    39  	AllowListConfig
    40  	UpgradeableConfig
    41  	InitialMint map[common.Address]*math.HexOrDecimal256 `json:"initialMint,omitempty"` // initial mint config to be immediately minted
    42  }
    43  
    44  // NewContractNativeMinterConfig returns a config for a network upgrade at [blockTimestamp] that enables
    45  // ContractNativeMinter with the given [admins] and [enableds] as members of the allowlist. Also mints balances according to [initialMint] when the upgrade activates.
    46  func NewContractNativeMinterConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialMint map[common.Address]*math.HexOrDecimal256) *ContractNativeMinterConfig {
    47  	return &ContractNativeMinterConfig{
    48  		AllowListConfig: AllowListConfig{
    49  			AllowListAdmins:  admins,
    50  			EnabledAddresses: enableds,
    51  		},
    52  		UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp},
    53  		InitialMint:       initialMint,
    54  	}
    55  }
    56  
    57  // NewDisableContractNativeMinterConfig returns config for a network upgrade at [blockTimestamp]
    58  // that disables ContractNativeMinter.
    59  func NewDisableContractNativeMinterConfig(blockTimestamp *big.Int) *ContractNativeMinterConfig {
    60  	return &ContractNativeMinterConfig{
    61  		UpgradeableConfig: UpgradeableConfig{
    62  			BlockTimestamp: blockTimestamp,
    63  			Disable:        true,
    64  		},
    65  	}
    66  }
    67  
    68  // Address returns the address of the native minter contract.
    69  func (c *ContractNativeMinterConfig) Address() common.Address {
    70  	return ContractNativeMinterAddress
    71  }
    72  
    73  // Configure configures [state] with the desired admins based on [c].
    74  func (c *ContractNativeMinterConfig) Configure(_ ChainConfig, state StateDB, _ BlockContext) {
    75  	for to, amount := range c.InitialMint {
    76  		if amount != nil {
    77  			bigIntAmount := (*big.Int)(amount)
    78  			state.AddBalance(to, bigIntAmount)
    79  		}
    80  	}
    81  
    82  	c.AllowListConfig.Configure(state, ContractNativeMinterAddress)
    83  }
    84  
    85  // Contract returns the singleton stateful precompiled contract to be used for the native minter.
    86  func (c *ContractNativeMinterConfig) Contract() StatefulPrecompiledContract {
    87  	return ContractNativeMinterPrecompile
    88  }
    89  
    90  func (c *ContractNativeMinterConfig) Verify() error {
    91  	if err := c.AllowListConfig.Verify(); err != nil {
    92  		return err
    93  	}
    94  	// ensure that all of the initial mint values in the map are non-nil positive values
    95  	for addr, amount := range c.InitialMint {
    96  		if amount == nil {
    97  			return fmt.Errorf("initial mint cannot contain nil amount for address %s", addr)
    98  		}
    99  		bigIntAmount := (*big.Int)(amount)
   100  		if bigIntAmount.Sign() < 1 {
   101  			return fmt.Errorf("initial mint cannot contain invalid amount %v for address %s", bigIntAmount, addr)
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  // Equal returns true if [s] is a [*ContractNativeMinterConfig] and it has been configured identical to [c].
   108  func (c *ContractNativeMinterConfig) Equal(s StatefulPrecompileConfig) bool {
   109  	// typecast before comparison
   110  	other, ok := (s).(*ContractNativeMinterConfig)
   111  	if !ok {
   112  		return false
   113  	}
   114  	eq := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig)
   115  	if !eq {
   116  		return false
   117  	}
   118  
   119  	if len(c.InitialMint) != len(other.InitialMint) {
   120  		return false
   121  	}
   122  
   123  	for address, amount := range c.InitialMint {
   124  		val, ok := other.InitialMint[address]
   125  		if !ok {
   126  			return false
   127  		}
   128  		bigIntAmount := (*big.Int)(amount)
   129  		bigIntVal := (*big.Int)(val)
   130  		if !utils.BigNumEqual(bigIntAmount, bigIntVal) {
   131  			return false
   132  		}
   133  	}
   134  
   135  	return true
   136  }
   137  
   138  // String returns a string representation of the ContractNativeMinterConfig.
   139  func (c *ContractNativeMinterConfig) String() string {
   140  	bytes, _ := json.Marshal(c)
   141  	return string(bytes)
   142  }
   143  
   144  // GetContractNativeMinterStatus returns the role of [address] for the minter list.
   145  func GetContractNativeMinterStatus(stateDB StateDB, address common.Address) AllowListRole {
   146  	return getAllowListStatus(stateDB, ContractNativeMinterAddress, address)
   147  }
   148  
   149  // SetContractNativeMinterStatus sets the permissions of [address] to [role] for the
   150  // minter list. assumes [role] has already been verified as valid.
   151  func SetContractNativeMinterStatus(stateDB StateDB, address common.Address, role AllowListRole) {
   152  	setAllowListRole(stateDB, ContractNativeMinterAddress, address, role)
   153  }
   154  
   155  // PackMintInput packs [address] and [amount] into the appropriate arguments for minting operation.
   156  // Assumes that [amount] can be represented by 32 bytes.
   157  func PackMintInput(address common.Address, amount *big.Int) ([]byte, error) {
   158  	// function selector (4 bytes) + input(hash for address + hash for amount)
   159  	res := make([]byte, selectorLen+mintInputLen)
   160  	packOrderedHashesWithSelector(res, mintSignature, []common.Hash{
   161  		address.Hash(),
   162  		common.BigToHash(amount),
   163  	})
   164  
   165  	return res, nil
   166  }
   167  
   168  // UnpackMintInput attempts to unpack [input] into the arguments to the mint precompile
   169  // assumes that [input] does not include selector (omits first 4 bytes in PackMintInput)
   170  func UnpackMintInput(input []byte) (common.Address, *big.Int, error) {
   171  	if len(input) != mintInputLen {
   172  		return common.Address{}, nil, fmt.Errorf("invalid input length for minting: %d", len(input))
   173  	}
   174  	to := common.BytesToAddress(returnPackedHash(input, mintInputAddressSlot))
   175  	assetAmount := new(big.Int).SetBytes(returnPackedHash(input, mintInputAmountSlot))
   176  	return to, assetAmount, nil
   177  }
   178  
   179  // mintNativeCoin checks if the caller is permissioned for minting operation.
   180  // The execution function parses the [input] into native coin amount and receiver address.
   181  func mintNativeCoin(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
   182  	if remainingGas, err = deductGas(suppliedGas, MintGasCost); err != nil {
   183  		return nil, 0, err
   184  	}
   185  
   186  	if readOnly {
   187  		return nil, remainingGas, vmerrs.ErrWriteProtection
   188  	}
   189  
   190  	to, amount, err := UnpackMintInput(input)
   191  	if err != nil {
   192  		return nil, remainingGas, err
   193  	}
   194  
   195  	stateDB := accessibleState.GetStateDB()
   196  	// Verify that the caller is in the allow list and therefore has the right to modify it
   197  	callerStatus := getAllowListStatus(stateDB, ContractNativeMinterAddress, caller)
   198  	if !callerStatus.IsEnabled() {
   199  		return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotMint, caller)
   200  	}
   201  
   202  	// if there is no address in the state, create one.
   203  	if !stateDB.Exist(to) {
   204  		stateDB.CreateAccount(to)
   205  	}
   206  
   207  	stateDB.AddBalance(to, amount)
   208  	// Return an empty output and the remaining gas
   209  	return []byte{}, remainingGas, nil
   210  }
   211  
   212  // createNativeMinterPrecompile returns a StatefulPrecompiledContract with R/W control of an allow list at [precompileAddr] and a native coin minter.
   213  func createNativeMinterPrecompile(precompileAddr common.Address) StatefulPrecompiledContract {
   214  	enabledFuncs := createAllowListFunctions(precompileAddr)
   215  
   216  	mintFunc := newStatefulPrecompileFunction(mintSignature, mintNativeCoin)
   217  
   218  	enabledFuncs = append(enabledFuncs, mintFunc)
   219  	// Construct the contract with no fallback function.
   220  	contract := newStatefulPrecompileWithFunctionSelectors(nil, enabledFuncs)
   221  	return contract
   222  }