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 }