github.com/MetalBlockchain/subnet-evm@v0.4.9/precompile/fee_config_manager.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/commontype" 13 "github.com/MetalBlockchain/subnet-evm/vmerrs" 14 "github.com/ethereum/go-ethereum/common" 15 ) 16 17 const ( 18 minFeeConfigFieldKey = iota + 1 19 // add new fields below this 20 // must preserve order of these fields 21 gasLimitKey = iota 22 targetBlockRateKey 23 minBaseFeeKey 24 targetGasKey 25 baseFeeChangeDenominatorKey 26 minBlockGasCostKey 27 maxBlockGasCostKey 28 blockGasCostStepKey 29 // add new fields above this 30 numFeeConfigField = iota - 1 31 32 // [numFeeConfigField] fields in FeeConfig struct 33 feeConfigInputLen = common.HashLength * numFeeConfigField 34 35 SetFeeConfigGasCost = writeGasCostPerSlot * (numFeeConfigField + 1) // plus one for setting last changed at 36 GetFeeConfigGasCost = readGasCostPerSlot * numFeeConfigField 37 GetLastChangedAtGasCost = readGasCostPerSlot 38 ) 39 40 var ( 41 _ StatefulPrecompileConfig = &FeeConfigManagerConfig{} 42 43 // Singleton StatefulPrecompiledContract for setting fee configs by permissioned callers. 44 FeeConfigManagerPrecompile StatefulPrecompiledContract = createFeeConfigManagerPrecompile(FeeConfigManagerAddress) 45 46 setFeeConfigSignature = CalculateFunctionSelector("setFeeConfig(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)") 47 getFeeConfigSignature = CalculateFunctionSelector("getFeeConfig()") 48 getFeeConfigLastChangedAtSignature = CalculateFunctionSelector("getFeeConfigLastChangedAt()") 49 50 feeConfigLastChangedAtKey = common.Hash{'l', 'c', 'a'} 51 52 ErrCannotChangeFee = errors.New("non-enabled cannot change fee config") 53 ) 54 55 // FeeConfigManagerConfig wraps [AllowListConfig] and uses it to implement the StatefulPrecompileConfig 56 // interface while adding in the FeeConfigManager specific precompile address. 57 type FeeConfigManagerConfig struct { 58 AllowListConfig // Config for the fee config manager allow list 59 UpgradeableConfig 60 InitialFeeConfig *commontype.FeeConfig `json:"initialFeeConfig,omitempty"` // initial fee config to be immediately activated 61 } 62 63 // NewFeeManagerConfig returns a config for a network upgrade at [blockTimestamp] that enables 64 // FeeConfigManager with the given [admins] and [enableds] as members of the allowlist with [initialConfig] as initial fee config if specified. 65 func NewFeeManagerConfig(blockTimestamp *big.Int, admins []common.Address, enableds []common.Address, initialConfig *commontype.FeeConfig) *FeeConfigManagerConfig { 66 return &FeeConfigManagerConfig{ 67 AllowListConfig: AllowListConfig{ 68 AllowListAdmins: admins, 69 EnabledAddresses: enableds, 70 }, 71 UpgradeableConfig: UpgradeableConfig{BlockTimestamp: blockTimestamp}, 72 InitialFeeConfig: initialConfig, 73 } 74 } 75 76 // NewDisableFeeManagerConfig returns config for a network upgrade at [blockTimestamp] 77 // that disables FeeConfigManager. 78 func NewDisableFeeManagerConfig(blockTimestamp *big.Int) *FeeConfigManagerConfig { 79 return &FeeConfigManagerConfig{ 80 UpgradeableConfig: UpgradeableConfig{ 81 BlockTimestamp: blockTimestamp, 82 Disable: true, 83 }, 84 } 85 } 86 87 // Address returns the address of the fee config manager contract. 88 func (c *FeeConfigManagerConfig) Address() common.Address { 89 return FeeConfigManagerAddress 90 } 91 92 // Equal returns true if [s] is a [*FeeConfigManagerConfig] and it has been configured identical to [c]. 93 func (c *FeeConfigManagerConfig) Equal(s StatefulPrecompileConfig) bool { 94 // typecast before comparison 95 other, ok := (s).(*FeeConfigManagerConfig) 96 if !ok { 97 return false 98 } 99 eq := c.UpgradeableConfig.Equal(&other.UpgradeableConfig) && c.AllowListConfig.Equal(&other.AllowListConfig) 100 if !eq { 101 return false 102 } 103 104 if c.InitialFeeConfig == nil { 105 return other.InitialFeeConfig == nil 106 } 107 108 return c.InitialFeeConfig.Equal(other.InitialFeeConfig) 109 } 110 111 // Configure configures [state] with the desired admins based on [c]. 112 func (c *FeeConfigManagerConfig) Configure(chainConfig ChainConfig, state StateDB, blockContext BlockContext) { 113 // Store the initial fee config into the state when the fee config manager activates. 114 if c.InitialFeeConfig != nil { 115 if err := StoreFeeConfig(state, *c.InitialFeeConfig, blockContext); err != nil { 116 // This should not happen since we already checked this config with Verify() 117 panic(fmt.Sprintf("invalid feeConfig provided: %s", err)) 118 } 119 } else { 120 if err := StoreFeeConfig(state, chainConfig.GetFeeConfig(), blockContext); err != nil { 121 // This should not happen since we already checked the chain config in the genesis creation. 122 panic(fmt.Sprintf("fee config should have been verified in genesis: %s", err)) 123 } 124 } 125 c.AllowListConfig.Configure(state, FeeConfigManagerAddress) 126 } 127 128 // Contract returns the singleton stateful precompiled contract to be used for the fee manager. 129 func (c *FeeConfigManagerConfig) Contract() StatefulPrecompiledContract { 130 return FeeConfigManagerPrecompile 131 } 132 133 func (c *FeeConfigManagerConfig) Verify() error { 134 if err := c.AllowListConfig.Verify(); err != nil { 135 return err 136 } 137 if c.InitialFeeConfig == nil { 138 return nil 139 } 140 141 return c.InitialFeeConfig.Verify() 142 } 143 144 // String returns a string representation of the FeeConfigManagerConfig. 145 func (c *FeeConfigManagerConfig) String() string { 146 bytes, _ := json.Marshal(c) 147 return string(bytes) 148 } 149 150 // GetFeeConfigManagerStatus returns the role of [address] for the fee config manager list. 151 func GetFeeConfigManagerStatus(stateDB StateDB, address common.Address) AllowListRole { 152 return getAllowListStatus(stateDB, FeeConfigManagerAddress, address) 153 } 154 155 // SetFeeConfigManagerStatus sets the permissions of [address] to [role] for the 156 // fee config manager list. assumes [role] has already been verified as valid. 157 func SetFeeConfigManagerStatus(stateDB StateDB, address common.Address, role AllowListRole) { 158 setAllowListRole(stateDB, FeeConfigManagerAddress, address, role) 159 } 160 161 // PackGetFeeConfigInput packs the getFeeConfig signature 162 func PackGetFeeConfigInput() []byte { 163 return getFeeConfigSignature 164 } 165 166 // PackGetLastChangedAtInput packs the getFeeConfigLastChangedAt signature 167 func PackGetLastChangedAtInput() []byte { 168 return getFeeConfigLastChangedAtSignature 169 } 170 171 // PackFeeConfig packs [feeConfig] without the selector into the appropriate arguments for fee config operations. 172 func PackFeeConfig(feeConfig commontype.FeeConfig) ([]byte, error) { 173 // input(feeConfig) 174 return packFeeConfigHelper(feeConfig, false), nil 175 } 176 177 // PackSetFeeConfig packs [feeConfig] with the selector into the appropriate arguments for setting fee config operations. 178 func PackSetFeeConfig(feeConfig commontype.FeeConfig) ([]byte, error) { 179 // function selector (4 bytes) + input(feeConfig) 180 return packFeeConfigHelper(feeConfig, true), nil 181 } 182 183 func packFeeConfigHelper(feeConfig commontype.FeeConfig, useSelector bool) []byte { 184 hashes := []common.Hash{ 185 common.BigToHash(feeConfig.GasLimit), 186 common.BigToHash(new(big.Int).SetUint64(feeConfig.TargetBlockRate)), 187 common.BigToHash(feeConfig.MinBaseFee), 188 common.BigToHash(feeConfig.TargetGas), 189 common.BigToHash(feeConfig.BaseFeeChangeDenominator), 190 common.BigToHash(feeConfig.MinBlockGasCost), 191 common.BigToHash(feeConfig.MaxBlockGasCost), 192 common.BigToHash(feeConfig.BlockGasCostStep), 193 } 194 195 if useSelector { 196 res := make([]byte, len(setFeeConfigSignature)+feeConfigInputLen) 197 packOrderedHashesWithSelector(res, setFeeConfigSignature, hashes) 198 return res 199 } 200 201 res := make([]byte, len(hashes)*common.HashLength) 202 packOrderedHashes(res, hashes) 203 return res 204 } 205 206 // UnpackFeeConfigInput attempts to unpack [input] into the arguments to the fee config precompile 207 // assumes that [input] does not include selector (omits first 4 bytes in PackSetFeeConfigInput) 208 func UnpackFeeConfigInput(input []byte) (commontype.FeeConfig, error) { 209 if len(input) != feeConfigInputLen { 210 return commontype.FeeConfig{}, fmt.Errorf("invalid input length for fee config input: %d", len(input)) 211 } 212 feeConfig := commontype.FeeConfig{} 213 for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { 214 listIndex := i - 1 215 packedElement := returnPackedHash(input, listIndex) 216 switch i { 217 case gasLimitKey: 218 feeConfig.GasLimit = new(big.Int).SetBytes(packedElement) 219 case targetBlockRateKey: 220 feeConfig.TargetBlockRate = new(big.Int).SetBytes(packedElement).Uint64() 221 case minBaseFeeKey: 222 feeConfig.MinBaseFee = new(big.Int).SetBytes(packedElement) 223 case targetGasKey: 224 feeConfig.TargetGas = new(big.Int).SetBytes(packedElement) 225 case baseFeeChangeDenominatorKey: 226 feeConfig.BaseFeeChangeDenominator = new(big.Int).SetBytes(packedElement) 227 case minBlockGasCostKey: 228 feeConfig.MinBlockGasCost = new(big.Int).SetBytes(packedElement) 229 case maxBlockGasCostKey: 230 feeConfig.MaxBlockGasCost = new(big.Int).SetBytes(packedElement) 231 case blockGasCostStepKey: 232 feeConfig.BlockGasCostStep = new(big.Int).SetBytes(packedElement) 233 default: 234 panic(fmt.Sprintf("unknown fee config key: %d", i)) 235 } 236 } 237 return feeConfig, nil 238 } 239 240 // GetStoredFeeConfig returns fee config from contract storage in given state 241 func GetStoredFeeConfig(stateDB StateDB) commontype.FeeConfig { 242 feeConfig := commontype.FeeConfig{} 243 for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { 244 val := stateDB.GetState(FeeConfigManagerAddress, common.Hash{byte(i)}) 245 switch i { 246 case gasLimitKey: 247 feeConfig.GasLimit = new(big.Int).Set(val.Big()) 248 case targetBlockRateKey: 249 feeConfig.TargetBlockRate = val.Big().Uint64() 250 case minBaseFeeKey: 251 feeConfig.MinBaseFee = new(big.Int).Set(val.Big()) 252 case targetGasKey: 253 feeConfig.TargetGas = new(big.Int).Set(val.Big()) 254 case baseFeeChangeDenominatorKey: 255 feeConfig.BaseFeeChangeDenominator = new(big.Int).Set(val.Big()) 256 case minBlockGasCostKey: 257 feeConfig.MinBlockGasCost = new(big.Int).Set(val.Big()) 258 case maxBlockGasCostKey: 259 feeConfig.MaxBlockGasCost = new(big.Int).Set(val.Big()) 260 case blockGasCostStepKey: 261 feeConfig.BlockGasCostStep = new(big.Int).Set(val.Big()) 262 default: 263 panic(fmt.Sprintf("unknown fee config key: %d", i)) 264 } 265 } 266 return feeConfig 267 } 268 269 func GetFeeConfigLastChangedAt(stateDB StateDB) *big.Int { 270 val := stateDB.GetState(FeeConfigManagerAddress, feeConfigLastChangedAtKey) 271 return val.Big() 272 } 273 274 // StoreFeeConfig stores given [feeConfig] and block number in the [blockContext] to the [stateDB]. 275 // A validation on [feeConfig] is done before storing. 276 func StoreFeeConfig(stateDB StateDB, feeConfig commontype.FeeConfig, blockContext BlockContext) error { 277 if err := feeConfig.Verify(); err != nil { 278 return err 279 } 280 281 for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { 282 var input common.Hash 283 switch i { 284 case gasLimitKey: 285 input = common.BigToHash(feeConfig.GasLimit) 286 case targetBlockRateKey: 287 input = common.BigToHash(new(big.Int).SetUint64(feeConfig.TargetBlockRate)) 288 case minBaseFeeKey: 289 input = common.BigToHash(feeConfig.MinBaseFee) 290 case targetGasKey: 291 input = common.BigToHash(feeConfig.TargetGas) 292 case baseFeeChangeDenominatorKey: 293 input = common.BigToHash(feeConfig.BaseFeeChangeDenominator) 294 case minBlockGasCostKey: 295 input = common.BigToHash(feeConfig.MinBlockGasCost) 296 case maxBlockGasCostKey: 297 input = common.BigToHash(feeConfig.MaxBlockGasCost) 298 case blockGasCostStepKey: 299 input = common.BigToHash(feeConfig.BlockGasCostStep) 300 default: 301 panic(fmt.Sprintf("unknown fee config key: %d", i)) 302 } 303 stateDB.SetState(FeeConfigManagerAddress, common.Hash{byte(i)}, input) 304 } 305 306 blockNumber := blockContext.Number() 307 if blockNumber == nil { 308 return fmt.Errorf("blockNumber cannot be nil") 309 } 310 stateDB.SetState(FeeConfigManagerAddress, feeConfigLastChangedAtKey, common.BigToHash(blockNumber)) 311 312 return nil 313 } 314 315 // setFeeConfig checks if the caller has permissions to set the fee config. 316 // The execution function parses [input] into FeeConfig structure and sets contract storage accordingly. 317 func setFeeConfig(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 318 if remainingGas, err = deductGas(suppliedGas, SetFeeConfigGasCost); err != nil { 319 return nil, 0, err 320 } 321 322 if readOnly { 323 return nil, remainingGas, vmerrs.ErrWriteProtection 324 } 325 326 feeConfig, err := UnpackFeeConfigInput(input) 327 if err != nil { 328 return nil, remainingGas, err 329 } 330 331 stateDB := accessibleState.GetStateDB() 332 // Verify that the caller is in the allow list and therefore has the right to modify it 333 callerStatus := getAllowListStatus(stateDB, FeeConfigManagerAddress, caller) 334 if !callerStatus.IsEnabled() { 335 return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotChangeFee, caller) 336 } 337 338 if err := StoreFeeConfig(stateDB, feeConfig, accessibleState.GetBlockContext()); err != nil { 339 return nil, remainingGas, err 340 } 341 342 // Return an empty output and the remaining gas 343 return []byte{}, remainingGas, nil 344 } 345 346 // getFeeConfig returns the stored fee config as an output. 347 // The execution function reads the contract state for the stored fee config and returns the output. 348 func getFeeConfig(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 349 if remainingGas, err = deductGas(suppliedGas, GetFeeConfigGasCost); err != nil { 350 return nil, 0, err 351 } 352 353 feeConfig := GetStoredFeeConfig(accessibleState.GetStateDB()) 354 355 output, err := PackFeeConfig(feeConfig) 356 if err != nil { 357 return nil, remainingGas, err 358 } 359 360 // Return the fee config as output and the remaining gas 361 return output, remainingGas, err 362 } 363 364 // getFeeConfigLastChangedAt returns the block number that fee config was last changed in. 365 // The execution function reads the contract state for the stored block number and returns the output. 366 func getFeeConfigLastChangedAt(accessibleState PrecompileAccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { 367 if remainingGas, err = deductGas(suppliedGas, GetLastChangedAtGasCost); err != nil { 368 return nil, 0, err 369 } 370 371 lastChangedAt := GetFeeConfigLastChangedAt(accessibleState.GetStateDB()) 372 373 // Return an empty output and the remaining gas 374 return common.BigToHash(lastChangedAt).Bytes(), remainingGas, err 375 } 376 377 // createFeeConfigManagerPrecompile returns a StatefulPrecompiledContract 378 // with getters and setters for the chain's fee config. Access to the getters/setters 379 // is controlled by an allow list for [precompileAddr]. 380 func createFeeConfigManagerPrecompile(precompileAddr common.Address) StatefulPrecompiledContract { 381 feeConfigManagerFunctions := createAllowListFunctions(precompileAddr) 382 383 setFeeConfigFunc := newStatefulPrecompileFunction(setFeeConfigSignature, setFeeConfig) 384 getFeeConfigFunc := newStatefulPrecompileFunction(getFeeConfigSignature, getFeeConfig) 385 getFeeConfigLastChangedAtFunc := newStatefulPrecompileFunction(getFeeConfigLastChangedAtSignature, getFeeConfigLastChangedAt) 386 387 feeConfigManagerFunctions = append(feeConfigManagerFunctions, setFeeConfigFunc, getFeeConfigFunc, getFeeConfigLastChangedAtFunc) 388 // Construct the contract with no fallback function. 389 contract := newStatefulPrecompileWithFunctionSelectors(nil, feeConfigManagerFunctions) 390 return contract 391 }