diff --git a/IACP224FeeManager.abi b/IACP224FeeManager.abi new file mode 100644 index 0000000000..c605eb142a --- /dev/null +++ b/IACP224FeeManager.abi @@ -0,0 +1,256 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "oldFeeConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "newFeeConfig", + "type": "tuple" + } + ], + "name": "FeeConfigUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "role", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldRole", + "type": "uint256" + } + ], + "name": "RoleSet", + "type": "event" + }, + { + "inputs": [], + "name": "getFeeConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeConfigLastChangedAt", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "readAllowList", + "outputs": [ + { + "internalType": "uint256", + "name": "role", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "setFeeConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setNone", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 44d5f62e58..be4a031a20 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -230,9 +230,12 @@ func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error { // Override the default min base fee if it's set in the env feeConfig.MinBaseFee = env.MinBaseFee } + acp176Config, err := params.DefaultACP224FeeConfig.ToACP176Config() + if err != nil { + return NewError(ErrorConfig, fmt.Errorf("failed converting ACP224 fee config to ACP176 config: %v", err)) + } configExtra := params.GetExtra(chainConfig) - var err error - env.BaseFee, err = customheader.BaseFee(configExtra, feeConfig, parent, env.Timestamp) + env.BaseFee, err = customheader.BaseFee(configExtra, feeConfig, acp176Config, parent, env.Timestamp) if err != nil { return NewError(ErrorConfig, fmt.Errorf("failed calculating base fee: %v", err)) } diff --git a/commontype/fee_config.go b/commontype/fee_config.go index 75c0558635..732e602829 100644 --- a/commontype/fee_config.go +++ b/commontype/fee_config.go @@ -8,11 +8,96 @@ import ( "fmt" "math/big" + "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/subnet-evm/utils" ) +type ACP224FeeConfig struct { + TargetGas *big.Int `json:"targetGas,omitempty"` + MinGasPrice *big.Int `json:"minGasPrice,omitempty"` + TimeToFillCapacity *big.Int `json:"timeToFillCapacity,omitempty"` + TimeToDouble *big.Int `json:"timeToDouble,omitempty"` +} + +// represents an empty ACP-224 fee config without any field +var EmptyACP224FeeConfig = ACP224FeeConfig{} + +func (f *ACP224FeeConfig) Verify() error { + switch { + case f.TargetGas == nil: + return errors.New("targetGas cannot be nil") + case f.MinGasPrice == nil: + return errors.New("minGasPrice cannot be nil") + case f.TimeToFillCapacity == nil: + return errors.New("timeToFillCapacity cannot be nil") + case f.TimeToDouble == nil: + return errors.New("timeToDouble cannot be nil") + } + + switch { + case f.TargetGas.Cmp(common.Big0) != 1: + return fmt.Errorf("targetGas = %d cannot be less than or equal to 0", f.TargetGas) + case f.MinGasPrice.Cmp(common.Big0) != 1: + return fmt.Errorf("minGasPrice = %d cannot be less than or equal to 0", f.MinGasPrice) + case f.TimeToFillCapacity.Cmp(common.Big0) == -1: + return fmt.Errorf("timeToFillCapacity = %d cannot be less than 0", f.TimeToFillCapacity) + case f.TimeToDouble.Cmp(common.Big0) == -1: + return fmt.Errorf("timeToDouble = %d cannot be less than 0", f.TimeToDouble) + } + + switch { + case f.TimeToFillCapacity.Cmp(big.NewInt(acp176.MaxTimeToFillCapacity)) == 1: + return fmt.Errorf("timeToFillCapacity = %d cannot be greater than %d", f.TimeToFillCapacity, acp176.MaxTimeToFillCapacity) + case f.TimeToDouble.Cmp(big.NewInt(acp176.MaxTimeToDouble)) == 1: + return fmt.Errorf("timeToDouble = %d cannot be greater than %d", f.TimeToDouble, acp176.MaxTimeToDouble) + } + return f.checkByteLens() +} + +func (f *ACP224FeeConfig) Equal(other *ACP224FeeConfig) bool { + if other == nil { + return false + } + + return utils.BigNumEqual(f.TargetGas, other.TargetGas) && + utils.BigNumEqual(f.MinGasPrice, other.MinGasPrice) && + utils.BigNumEqual(f.TimeToFillCapacity, other.TimeToFillCapacity) && + utils.BigNumEqual(f.TimeToDouble, other.TimeToDouble) +} + +func (f *ACP224FeeConfig) ToACP176Config() (acp176.Config, error) { + if err := f.Verify(); err != nil { + return acp176.Config{}, err + } + + config := acp176.Config{ + MinGasPrice: gas.Price(f.MinGasPrice.Uint64()), + TimeToFillCapacity: gas.Gas(f.TimeToFillCapacity.Uint64()), + TimeToDouble: f.TimeToDouble.Uint64(), + } + return config, config.Verify() +} + +// checkByteLens checks byte lengths against common.HashLen (32 bytes) and returns error +func (f *ACP224FeeConfig) checkByteLens() error { + if isBiggerThanHashLen(f.TargetGas) { + return fmt.Errorf("targetGas exceeds %d bytes", common.HashLength) + } + if isBiggerThanHashLen(f.MinGasPrice) { + return fmt.Errorf("minGasPrice exceeds %d bytes", common.HashLength) + } + if isBiggerThanHashLen(f.TimeToFillCapacity) { + return fmt.Errorf("timeToFillCapacity exceeds %d bytes", common.HashLength) + } + if isBiggerThanHashLen(f.TimeToDouble) { + return fmt.Errorf("timeToDouble exceeds %d bytes", common.HashLength) + } + return nil +} + // FeeConfig specifies the parameters for the dynamic fee algorithm, which determines the gas limit, base fee, and block gas cost of blocks // on the network. // diff --git a/commontype/test_fee_config.go b/commontype/test_fee_config.go index 22c3b38280..4a7b65affd 100644 --- a/commontype/test_fee_config.go +++ b/commontype/test_fee_config.go @@ -17,3 +17,10 @@ var ValidTestFeeConfig = FeeConfig{ MaxBlockGasCost: big.NewInt(1_000_000), BlockGasCostStep: big.NewInt(200_000), } + +var ValidTestACP224FeeConfig = ACP224FeeConfig{ + TargetGas: big.NewInt(15_000_000), + MinGasPrice: big.NewInt(1), + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), +} diff --git a/consensus/consensus.go b/consensus/consensus.go index bb9f70e4a4..10e1ca90e6 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -59,6 +59,9 @@ type ChainHeaderReader interface { // GetFeeConfigAt retrieves the fee config and last changed block number at block header. GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error) + // GetACP224FeeConfigAt retrieves the ACP224 fee config and last changed block number at block header. + GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) + // GetCoinbaseAt retrieves the configured coinbase address at [parent]. // If fee recipients are allowed, returns true in the second return value and a predefined address in the first value. GetCoinbaseAt(parent *types.Header) (common.Address, bool, error) diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go index c593d27b4c..851433b478 100644 --- a/consensus/dummy/consensus.go +++ b/consensus/dummy/consensus.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/components/gas" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/types" @@ -43,18 +44,21 @@ type Mode struct { type ( DummyEngine struct { - clock *mockable.Clock - consensusMode Mode + clock *mockable.Clock + consensusMode Mode + desiredTargetExcess *gas.Gas } ) func NewDummyEngine( mode Mode, clock *mockable.Clock, + desiredTargetExcess *gas.Gas, ) *DummyEngine { return &DummyEngine{ - clock: clock, - consensusMode: mode, + clock: clock, + consensusMode: mode, + desiredTargetExcess: desiredTargetExcess, } } @@ -128,9 +132,14 @@ func (eng *DummyEngine) verifyCoinbase(header *types.Header, parent *types.Heade return nil } -func verifyHeaderGasFields(config *extras.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error { - // We verify the current block by checking the parent fee config - // this is because the current block cannot set the fee config for itself +func verifyHeaderGasFields( + config *extras.ChainConfig, + header *types.Header, + parent *types.Header, + chain consensus.ChainHeaderReader, +) error { + // We verify the current block by checking the parent fee configs + // because the current block cannot set the fee config for itself. // Fee config might depend on the state when precompile is activated // but we don't know the final state while forming the block. // See worker package for more details. @@ -138,18 +147,29 @@ func verifyHeaderGasFields(config *extras.ChainConfig, header *types.Header, par if err != nil { return err } - if err := customheader.VerifyGasUsed(config, feeConfig, parent, header); err != nil { + + acp224FeeConfig, _, err := chain.GetACP224FeeConfigAt(parent) + if err != nil { + return err + } + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + return err + } + + if err := customheader.VerifyGasUsed(config, feeConfig, acp176Config, parent, header); err != nil { return err } - if err := customheader.VerifyGasLimit(config, feeConfig, parent, header); err != nil { + if err := customheader.VerifyGasLimit(config, feeConfig, acp176Config, parent, header); err != nil { return err } - if err := customheader.VerifyExtraPrefix(config, parent, header); err != nil { + + if err := customheader.VerifyExtraPrefix(config, acp224FeeConfig, parent, header); err != nil { return err } // Verify header.BaseFee matches the expected value. - expectedBaseFee, err := customheader.BaseFee(config, feeConfig, parent, header.Time) + expectedBaseFee, err := customheader.BaseFee(config, feeConfig, acp176Config, parent, header.Time) if err != nil { return fmt.Errorf("failed to calculate base fee: %w", err) } @@ -395,8 +415,14 @@ func (eng *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, h } } + // Get the ACP224 fee config at the parent since the current block has not been finalized yet. + acp224FeeConfig, _, err := chain.GetACP224FeeConfigAt(parent) + if err != nil { + return nil, err + } + // finalize the header.Extra - extraPrefix, err := customheader.ExtraPrefix(config, parent, header) + extraPrefix, err := customheader.ExtraPrefix(config, acp224FeeConfig, parent, header, eng.desiredTargetExcess) if err != nil { return nil, fmt.Errorf("failed to calculate new header.Extra: %w", err) } diff --git a/contracts/contracts/interfaces/IACP224FeeManager.sol b/contracts/contracts/interfaces/IACP224FeeManager.sol new file mode 100644 index 0000000000..bd3a122fe2 --- /dev/null +++ b/contracts/contracts/interfaces/IACP224FeeManager.sol @@ -0,0 +1,35 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "./IAllowList.sol"; + +/// @title ACP-224 Fee Manager Interface +/// @notice Interface for managing dynamic gas limit and fee parameters +/// @dev Inherits from IAllowList for access control +interface IACP224FeeManager is IAllowList { + /// @notice Configuration parameters for the dynamic fee mechanism + struct FeeConfig { + uint256 targetGas; // Target gas consumption per second + uint256 minGasPrice; // Minimum gas price in wei + uint256 timeToFillCapacity; // Maximum capacity factor (C = factor * T) + uint256 timeToDouble; // Time in seconds for gas price to double at max capacity + } + + /// @notice Emitted when fee configuration is updated + /// @param sender Address that triggered the update + /// @param oldFeeConfig Previous configuration + /// @param newFeeConfig New configuration + event FeeConfigUpdated(address indexed sender, FeeConfig oldFeeConfig, FeeConfig newFeeConfig); + + /// @notice Set the fee configuration + /// @param config New fee configuration parameters + function setFeeConfig(FeeConfig calldata config) external; + + /// @notice Get the current fee configuration + /// @return config Current fee configuration + function getFeeConfig() external view returns (FeeConfig memory config); + + /// @notice Get the block number when fee config was last changed + /// @return blockNumber Block number of last configuration change + function getFeeConfigLastChangedAt() external view returns (uint256 blockNumber); +} diff --git a/core/blockchain.go b/core/blockchain.go index d6fcb00650..ff96d60f2a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -185,6 +185,13 @@ type cacheableFeeConfig struct { lastChangedAt *big.Int } +// cacheableACP224FeeConfig encapsulates ACP-224 fee configuration itself and the block number that it has changed at, +// in order to cache them together. +type cacheableACP224FeeConfig struct { + acp224FeeConfig commontype.ACP224FeeConfig + lastChangedAt *big.Int +} + // cacheableCoinbaseConfig encapsulates coinbase address itself and allowFeeRecipient flag, // in order to cache them together. type cacheableCoinbaseConfig struct { @@ -332,13 +339,14 @@ type BlockChain struct { currentBlock atomic.Pointer[types.Header] // Current head of the block chain - bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies - receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent receipts per block - blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks - txLookupCache *lru.Cache[common.Hash, txLookup] // Cache for the most recent transaction lookup data. - badBlocks *lru.Cache[common.Hash, *badBlock] // Cache for bad blocks - feeConfigCache *lru.Cache[common.Hash, *cacheableFeeConfig] // Cache for the most recent feeConfig lookup data. - coinbaseConfigCache *lru.Cache[common.Hash, *cacheableCoinbaseConfig] // Cache for the most recent coinbaseConfig lookup data. + bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies + receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent receipts per block + blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks + txLookupCache *lru.Cache[common.Hash, txLookup] // Cache for the most recent transaction lookup data. + badBlocks *lru.Cache[common.Hash, *badBlock] // Cache for bad blocks + feeConfigCache *lru.Cache[common.Hash, *cacheableFeeConfig] // Cache for the most recent feeConfig lookup data. + acp224FeeConfigCache *lru.Cache[common.Hash, *cacheableACP224FeeConfig] // Cache for the most recent ACP-224 feeConfig lookup data. + coinbaseConfigCache *lru.Cache[common.Hash, *cacheableCoinbaseConfig] // Cache for the most recent coinbaseConfig lookup data. stopping atomic.Bool // false if chain is running, true when stopped @@ -427,23 +435,24 @@ func NewBlockChain( log.Info("") bc := &BlockChain{ - chainConfig: chainConfig, - cacheConfig: cacheConfig, - db: db, - triedb: triedb, - bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), - receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit), - blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), - txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit), - badBlocks: lru.NewCache[common.Hash, *badBlock](badBlockLimit), - feeConfigCache: lru.NewCache[common.Hash, *cacheableFeeConfig](feeConfigCacheLimit), - coinbaseConfigCache: lru.NewCache[common.Hash, *cacheableCoinbaseConfig](coinbaseConfigCacheLimit), - engine: engine, - vmConfig: vmConfig, - senderCacher: NewTxSenderCacher(runtime.NumCPU()), - acceptorQueue: make(chan *types.Block, cacheConfig.AcceptorQueueLimit), - quit: make(chan struct{}), - acceptedLogsCache: NewFIFOCache[common.Hash, [][]*types.Log](cacheConfig.AcceptedCacheSize), + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triedb: triedb, + bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit), + receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit), + blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), + txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit), + badBlocks: lru.NewCache[common.Hash, *badBlock](badBlockLimit), + feeConfigCache: lru.NewCache[common.Hash, *cacheableFeeConfig](feeConfigCacheLimit), + acp224FeeConfigCache: lru.NewCache[common.Hash, *cacheableACP224FeeConfig](feeConfigCacheLimit), + coinbaseConfigCache: lru.NewCache[common.Hash, *cacheableCoinbaseConfig](coinbaseConfigCacheLimit), + engine: engine, + vmConfig: vmConfig, + senderCacher: NewTxSenderCacher(runtime.NumCPU()), + acceptorQueue: make(chan *types.Block, cacheConfig.AcceptorQueueLimit), + quit: make(chan struct{}), + acceptedLogsCache: NewFIFOCache[common.Hash, [][]*types.Log](cacheConfig.AcceptedCacheSize), } bc.stateCache = extstate.NewDatabaseWithNodeDB(bc.db, bc.triedb) bc.validator = NewBlockValidator(chainConfig, bc, engine) diff --git a/core/blockchain_ext_test.go b/core/blockchain_ext_test.go index 33ddb4b97f..984a32d3e5 100644 --- a/core/blockchain_ext_test.go +++ b/core/blockchain_ext_test.go @@ -1479,7 +1479,7 @@ func InsertChainValidBlockFee(t *testing.T, create createFunc) { // Ensure that key1 has some funds in the genesis block. genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether)) gspec := &Genesis{ - Config: params.TestChainConfig, + Config: params.TestEtnaChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}}, } @@ -1488,13 +1488,13 @@ func InsertChainValidBlockFee(t *testing.T, create createFunc) { t.Cleanup(blockchain.Stop) // This call generates a chain of 3 blocks. - signer := types.LatestSigner(params.TestChainConfig) + signer := types.LatestSigner(params.TestEtnaChainConfig) tip := big.NewInt(50000 * params.GWei) transfer := big.NewInt(10000) - _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, extras.TestChainConfig.FeeConfig.TargetBlockRate-1, func(_ int, gen *BlockGen) { + _, chain, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 3, extras.TestEtnaChainConfig.FeeConfig.TargetBlockRate-1, func(_ int, gen *BlockGen) { feeCap := new(big.Int).Add(gen.BaseFee(), tip) tx := types.NewTx(&types.DynamicFeeTx{ - ChainID: params.TestChainConfig.ChainID, + ChainID: params.TestEtnaChainConfig.ChainID, Nonce: gen.TxNonce(addr1), To: &addr2, Gas: ethparams.TxGas, @@ -1561,7 +1561,7 @@ func StatefulPrecompiles(t *testing.T, create createFunc) { // Ensure that key1 has sufficient funds in the genesis block for all of the tests. genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether)) - config := params.Copy(params.TestChainConfig) + config := params.Copy(params.TestEtnaChainConfig) // Set all of the required config parameters params.GetExtra(&config).GenesisPrecompiles = extras.Precompiles{ deployerallowlist.ConfigKey: deployerallowlist.NewConfig(utils.NewUint64(0), []common.Address{addr1}, nil, nil), diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index e2d3717d66..273c319a6e 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -42,6 +42,7 @@ import ( "github.com/ava-labs/subnet-evm/constants" "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/precompile/contracts/acp224feemanager" "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" "github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager" ) @@ -400,6 +401,45 @@ func (bc *BlockChain) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig return storedFeeConfig, lastChangedAt, nil } +// GetACP224FeeConfigAt returns the ACP-224 fee configuration and the last changed block number at [parent]. +// If Fortuna is not activated, returns default fee config and nil block number. +// If ACP224FeeManager is activated at [parent], returns the fee config in the precompile contract state. +// Otherwise returns the fee config in the chain config. +// Assumes that a valid configuration is stored when the precompile is activated. +func (bc *BlockChain) GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) { + config := params.GetExtra(bc.Config()) + if !config.IsFortuna(parent.Time) { + return params.DefaultACP224FeeConfig, nil, nil + } + if !config.IsPrecompileEnabled(acp224feemanager.ContractAddress, parent.Time) { + return config.ACP224FeeConfig, common.Big0, nil + } + + // try to return it from the cache + if cached, hit := bc.acp224FeeConfigCache.Get(parent.Root); hit { + return cached.acp224FeeConfig, cached.lastChangedAt, nil + } + + stateDB, err := bc.StateAt(parent.Root) + if err != nil { + return commontype.EmptyACP224FeeConfig, nil, err + } + + storedFeeConfig := acp224feemanager.GetStoredFeeConfig(stateDB, acp224feemanager.ContractAddress) + // this should not return an invalid fee config since it's assumed that + // StoreFeeConfig returns an error when an invalid fee config is attempted to be stored. + // However an external stateDB call can modify the contract state. + // This check is added to add a defense in-depth. + if err := storedFeeConfig.Verify(); err != nil { + return commontype.EmptyACP224FeeConfig, nil, err + } + lastChangedAt := acp224feemanager.GetFeeConfigLastChangedAt(stateDB, acp224feemanager.ContractAddress) + cacheable := &cacheableACP224FeeConfig{acp224FeeConfig: storedFeeConfig, lastChangedAt: lastChangedAt} + // add it to the cache + bc.acp224FeeConfigCache.Add(parent.Root, cacheable) + return storedFeeConfig, lastChangedAt, nil +} + // GetCoinbaseAt returns the configured coinbase address at [parent]. // If RewardManager is activated at [parent], returns the reward manager config in the precompile contract state. // If fee recipients are allowed, returns true in the second return value. diff --git a/core/chain_makers.go b/core/chain_makers.go index 130218ca61..f66f228bbe 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -31,6 +31,7 @@ import ( "fmt" "math/big" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/state" @@ -110,8 +111,9 @@ func (b *BlockGen) Difficulty() *big.Int { // block. func (b *BlockGen) SetParentBeaconRoot(root common.Hash) { b.header.ParentBeaconRoot = &root + rulesExtra := params.GetRulesExtra(b.cm.config.Rules(b.header.Number, params.IsMergeTODO, b.header.Time)) var ( - blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) + blockContext = NewEVMBlockContext(rulesExtra.AvalancheRules, b.header, b.cm, &b.header.Coinbase) vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{}) ) ProcessBeaconBlockRoot(root, vmenv, b.statedb) @@ -129,7 +131,8 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti b.SetCoinbase(common.Address{}) } b.statedb.SetTxContext(tx.Hash(), len(b.txs)) - blockContext := NewEVMBlockContext(b.header, bc, &b.header.Coinbase) + rulesExtra := params.GetRulesExtra(b.cm.config.Rules(b.header.Number, params.IsMergeTODO, b.header.Time)) + blockContext := NewEVMBlockContext(rulesExtra.AvalancheRules, b.header, bc, &b.header.Coinbase) receipt, err := ApplyTransaction(b.cm.config, bc, blockContext, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig) if err != nil { panic(err) @@ -382,11 +385,22 @@ func (cm *chainMaker) makeHeader(parent *types.Block, gap uint64, state *state.S panic(err) } config := params.GetExtra(cm.config) - gasLimit, err := header.GasLimit(config, feeConfig, parent.Header(), time) + var acp176Config acp176.Config + if config.IsFortuna(time) { + acp224FeeConfig, _, err := cm.GetACP224FeeConfigAt(parent.Header()) + if err != nil { + panic(err) + } + acp176Config, err = acp224FeeConfig.ToACP176Config() + if err != nil { + panic(err) + } + } + gasLimit, err := header.GasLimit(config, feeConfig, acp176Config, parent.Header(), time) if err != nil { panic(err) } - baseFee, err := header.BaseFee(config, feeConfig, parent.Header(), time) + baseFee, err := header.BaseFee(config, feeConfig, acp176Config, parent.Header(), time) if err != nil { panic(err) } @@ -503,6 +517,10 @@ func (cm *chainMaker) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig return params.GetExtra(cm.config).FeeConfig, nil, nil } +func (cm *chainMaker) GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) { + return params.GetExtra(cm.config).ACP224FeeConfig, nil, nil +} + func (cm *chainMaker) GetCoinbaseAt(parent *types.Header) (common.Address, bool, error) { return constants.BlackholeAddr, params.GetExtra(cm.config).AllowFeeRecipients, nil } diff --git a/core/evm.go b/core/evm.go index ee8ac8be01..2667a18457 100644 --- a/core/evm.go +++ b/core/evm.go @@ -40,6 +40,7 @@ import ( "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" "github.com/ava-labs/subnet-evm/core/extstate" "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/params/extras" "github.com/ava-labs/avalanchego/vms/evm/predicate" customheader "github.com/ava-labs/subnet-evm/plugin/evm/header" @@ -93,8 +94,8 @@ type ChainContext interface { } // NewEVMBlockContext creates a new context for use in the EVM. -func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext { - predicateBytes := customheader.PredicateBytesFromExtra(header.Extra) +func NewEVMBlockContext(rules extras.AvalancheRules, header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext { + predicateBytes := customheader.PredicateBytesFromExtra(rules, header.Extra) if len(predicateBytes) == 0 { return newEVMBlockContext(header, chain, author, nil) } @@ -116,8 +117,8 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common // in header.Extra. // This function is used to create a BlockContext when the header Extra data is not fully formed yet and it's more efficient to pass in predicateResults // directly rather than re-encode the latest results when executing each individual transaction. -func NewEVMBlockContextWithPredicateResults(header *types.Header, chain ChainContext, author *common.Address, predicateBytes []byte) vm.BlockContext { - blockCtx := NewEVMBlockContext(header, chain, author) +func NewEVMBlockContextWithPredicateResults(rules extras.AvalancheRules, header *types.Header, chain ChainContext, author *common.Address, predicateBytes []byte) vm.BlockContext { + blockCtx := NewEVMBlockContext(rules, header, chain, author) // Note this only sets the block context, which is the hand-off point for // the EVM. The actual header is not modified. blockCtx.Header.Extra = customheader.SetPredicateBytesInExtra( diff --git a/core/state_processor.go b/core/state_processor.go index 88c8d5b594..347a609f4f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -87,8 +87,9 @@ func (p *StateProcessor) Process(block *types.Block, parent *types.Header, state return nil, nil, 0, err } + rulesExtra := params.GetRulesExtra(p.config.Rules(blockNumber, params.IsMergeTODO, block.Time())) var ( - context = NewEVMBlockContext(header, p.bc, nil) + context = NewEVMBlockContext(rulesExtra.AvalancheRules, header, p.bc, nil) vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) signer = types.MakeSigner(p.config, header.Number, header.Time) ) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index e201e327e3..d93f63f2a9 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -33,6 +33,7 @@ import ( "testing" "github.com/ava-labs/avalanchego/upgrade" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" @@ -40,6 +41,7 @@ import ( "github.com/ava-labs/libevm/crypto" ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/libevm/trie" + "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/consensus" "github.com/ava-labs/subnet-evm/consensus/dummy" "github.com/ava-labs/subnet-evm/consensus/misc/eip4844" @@ -52,6 +54,12 @@ import ( "golang.org/x/crypto/sha3" ) +var testACP176Config = acp176.Config{ + MinGasPrice: 1, + TimeToFillCapacity: 5, + TimeToDouble: 60, +} + func u64(val uint64) *uint64 { return &val } // TestStateProcessorErrors tests the output from the 'core' errors @@ -154,9 +162,10 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), 15000001, big.NewInt(225000000000), nil), + // This test was modified to account for ACP-176 gas limits + makeTx(key1, 0, common.Address{}, big.NewInt(0), uint64(testACP176Config.MinMaxCapacity()+1), big.NewInt(int64(testACP176Config.MinGasPrice)), nil), }, - want: "could not apply tx 0 [0x1354370681d2ab68247073d889736f8be4a8d87e35956f0c02658d3670803a66]: gas limit reached", + want: "could not apply tx 0 [0xb709565c056a68a4b4dc7714ae901a0f03663bb98f9fa58a10b88ec614362caf]: gas limit reached", }, { // ErrInsufficientFundsForTransfer txs: []*types.Transaction{ @@ -182,15 +191,16 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas*762, big.NewInt(225000000000), nil), + // This test was modified to account for ACP-176 gas limits + makeTx(key1, 0, common.Address{}, big.NewInt(0), ethparams.TxGas*953, big.NewInt(int64(testACP176Config.MinGasPrice)), nil), }, - want: "could not apply tx 0 [0x76c07cc2b32007eb1a9c3fa066d579a3d77ec4ecb79bbc266624a601d7b08e46]: gas limit reached", + want: "could not apply tx 0 [0xcd46718c1af6fd074deb6b036d34c1cb499517bf90d93df74dcfaba25fdf34af]: gas limit reached", }, { // ErrFeeCapTooLow txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, ethparams.TxGas, big.NewInt(0), big.NewInt(0)), }, - want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0, baseFee: 225000000000", + want: "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0, baseFee: 1", }, { // ErrTipVeryHigh txs: []*types.Transaction{ @@ -243,7 +253,7 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkBlobTx(0, common.Address{}, ethparams.TxGas, big.NewInt(1), big.NewInt(1), big.NewInt(0), []common.Hash{(common.Hash{1})}), }, - want: "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 1, baseFee: 225000000000", + want: "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per blob gas less than block blob gas fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7 blobGasFeeCap: 0, blobBaseFee: 1", }, } { block := GenerateBadBlock(gspec.ToBlock(), dummy.NewCoinbaseFaker(), tt.txs, gspec.Config) @@ -363,8 +373,20 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr panic(err) } configExtra := params.GetExtra(config) - gasLimit, _ := customheader.GasLimit(configExtra, feeConfig, parent.Header(), time) - baseFee, _ := customheader.BaseFee(configExtra, feeConfig, parent.Header(), time) + var acp224FeeConfig commontype.ACP224FeeConfig + var acp176Config acp176.Config + if configExtra.IsFortuna(time) { + acp224FeeConfig, _, err = fakeChainReader.GetACP224FeeConfigAt(parent.Header()) + if err != nil { + panic(err) + } + acp176Config, err = acp224FeeConfig.ToACP176Config() + if err != nil { + panic(err) + } + } + gasLimit, _ := customheader.GasLimit(configExtra, feeConfig, acp176Config, parent.Header(), time) + baseFee, _ := customheader.BaseFee(configExtra, feeConfig, acp176Config, parent.Header(), time) header := &types.Header{ ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), @@ -402,7 +424,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr cumulativeGas += tx.Gas() nBlobs += len(tx.BlobHashes()) } - header.Extra, _ = customheader.ExtraPrefix(configExtra, parent.Header(), header) + header.Extra, _ = customheader.ExtraPrefix(configExtra, acp224FeeConfig, parent.Header(), header, nil) header.Root = common.BytesToHash(hasher.Sum(nil)) if config.IsCancun(header.Number, header.Time) { var pExcess, pUsed = uint64(0), uint64(0) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index bc76282455..4b300d2b4b 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -417,9 +417,20 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres p.Close() return err } + acp224FeeConfig, _, err := p.chain.GetACP224FeeConfigAt(p.head) + if err != nil { + p.Close() + return err + } + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + p.Close() + return err + } baseFee, err := header.EstimateNextBaseFee( params.GetExtra(p.chain.Config()), feeConfig, + acp176Config, p.head, uint64(time.Now().Unix()), ) @@ -853,9 +864,20 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) { log.Error("Failed to get fee config to reset blobpool fees", "err", err) return } + acp224FeeConfig, _, err := p.chain.GetACP224FeeConfigAt(p.head) + if err != nil { + log.Error("Failed to get ACP224 fee config to reset blobpool fees", "err", err) + return + } + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + log.Error("Failed to convert ACP224 fee config to ACP176 config to reset blobpool fees", "err", err) + return + } baseFeeBig, err := header.EstimateNextBaseFee( params.GetExtra(p.chain.Config()), feeConfig, + acp176Config, p.head, uint64(time.Now().Unix()), ) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 310120bacb..1d9e90f9be 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -121,8 +121,12 @@ func (bc *testBlockChain) CurrentBlock() *types.Header { Extra: make([]byte, subnetevm.WindowSize), } config := params.GetExtra(bc.config) + acp176Config, err := config.ACP224FeeConfig.ToACP176Config() + if err != nil { + panic(err) + } baseFee, err := header.BaseFee( - config, config.FeeConfig, parent, blockTime, + config, config.FeeConfig, acp176Config, parent, blockTime, ) if err != nil { panic(err) @@ -180,6 +184,10 @@ func (bc *testBlockChain) GetFeeConfigAt(header *types.Header) (commontype.FeeCo return params.GetExtra(bc.config).FeeConfig, nil, nil } +func (bc *testBlockChain) GetACP224FeeConfigAt(header *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) { + return params.GetExtra(bc.config).ACP224FeeConfig, nil, nil +} + // makeAddressReserver is a utility method to sanity check that accounts are // properly reserved by the blobpool (no duplicate reserves or unreserves). func makeAddressReserver() txpool.AddressReserver { diff --git a/core/txpool/blobpool/interface.go b/core/txpool/blobpool/interface.go index 771496cdca..3302dcf08f 100644 --- a/core/txpool/blobpool/interface.go +++ b/core/txpool/blobpool/interface.go @@ -57,4 +57,6 @@ type BlockChain interface { StateAt(root common.Hash) (*state.StateDB, error) GetFeeConfigAt(header *types.Header) (commontype.FeeConfig, *big.Int, error) + + GetACP224FeeConfigAt(header *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index d9580dc77f..1c88bda16e 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -146,6 +146,7 @@ type BlockChain interface { SenderCacher() *core.TxSenderCacher GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error) + GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) } // Config are the configuration parameters of the transaction pool. @@ -1851,8 +1852,16 @@ func (pool *LegacyPool) updateBaseFeeAt(head *types.Header) error { if err != nil { return err } + acp224FeeConfig, _, err := pool.chain.GetACP224FeeConfigAt(head) + if err != nil { + return err + } + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + return err + } chainConfig := params.GetExtra(pool.chainconfig) - baseFeeEstimate, err := header.EstimateNextBaseFee(chainConfig, feeConfig, head, uint64(time.Now().Unix())) + baseFeeEstimate, err := header.EstimateNextBaseFee(chainConfig, feeConfig, acp176Config, head, uint64(time.Now().Unix())) if err != nil { return err } diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 5a79cae2f9..f49faab7d3 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -74,6 +74,13 @@ var ( MaxBlockGasCost: big.NewInt(1_000_000), BlockGasCostStep: big.NewInt(200_000), } + + testACP224FeeConfig = commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(1_000_000), + MinGasPrice: big.NewInt(1), + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + } ) func init() { @@ -133,6 +140,10 @@ func (bc *testBlockChain) GetFeeConfigAt(parent *types.Header) (commontype.FeeCo return testFeeConfig, common.Big0, nil } +func (bc *testBlockChain) GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) { + return testACP224FeeConfig, common.Big0, nil +} + func (bc *testBlockChain) SenderCacher() *core.TxSenderCacher { // Zero threads avoids starting goroutines. return core.NewTxSenderCacher(0) diff --git a/eth/api_backend.go b/eth/api_backend.go index 9bcf716d21..9f47edb9ea 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -127,6 +127,10 @@ func (b *EthAPIBackend) GetFeeConfigAt(parent *types.Header) (commontype.FeeConf return b.eth.blockchain.GetFeeConfigAt(parent) } +func (b *EthAPIBackend) GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) { + return b.eth.blockchain.GetACP224FeeConfigAt(parent) +} + func (b *EthAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { if err := ctx.Err(); err != nil { return nil, err @@ -313,7 +317,8 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st if blockCtx != nil { context = *blockCtx } else { - context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) + rulesExtra := params.GetRulesExtra(b.ChainConfig().Rules(header.Number, params.IsMergeTODO, header.Time)) + context = core.NewEVMBlockContext(rulesExtra.AvalancheRules, header, b.eth.BlockChain(), nil) } return vm.NewEVM(context, txContext, state, b.ChainConfig(), *vmConfig) } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index 9ff4b8e4c6..581eba12f7 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -218,9 +218,10 @@ func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit ui // call invocation. func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) { // Assemble the call and the call context + rulesExtra := params.GetRulesExtra(opts.Config.Rules(opts.Header.Number, params.IsMergeTODO, opts.Header.Time)) var ( msgContext = core.NewEVMTxContext(call) - evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil) + evmContext = core.NewEVMBlockContext(rulesExtra.AvalancheRules, opts.Header, opts.Chain, nil) dirtyState = opts.State.Copy() evm = vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) diff --git a/eth/gasprice/fee_info_provider_test.go b/eth/gasprice/fee_info_provider_test.go index 7d91e41194..72fd813102 100644 --- a/eth/gasprice/fee_info_provider_test.go +++ b/eth/gasprice/fee_info_provider_test.go @@ -16,8 +16,8 @@ import ( "github.com/ava-labs/subnet-evm/params" ) -func TestFeeInfoProvider(t *testing.T) { - backend := newTestBackend(t, params.TestChainConfig, 2, testGenBlock(t, 55, 370)) +func TestEtnaFeeInfoProvider(t *testing.T) { + backend := newTestBackend(t, params.TestEtnaChainConfig, 2, testGenBlock(t, 55, 370)) f, err := newFeeInfoProvider(backend, 1, 2) require.NoError(t, err) diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index f57c8becab..2c97d4f7b6 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -33,6 +33,7 @@ import ( "sync" "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/lru" "github.com/ava-labs/libevm/core/types" @@ -66,9 +67,9 @@ const ( var ( DefaultMaxPrice = big.NewInt(150 * params.GWei) - DefaultMinPrice = big.NewInt(0 * params.GWei) + DefaultMinPrice = big.NewInt(1) DefaultMinBaseFee = big.NewInt(legacy.BaseFee) - DefaultMinGasUsed = big.NewInt(6_000_000) // block gas limit is 8,000,000 + DefaultMinGasUsed = big.NewInt(acp176.MinTargetPerSecond) DefaultMaxLookbackSeconds = uint64(80) ) @@ -103,6 +104,7 @@ type OracleBackend interface { MinRequiredTip(ctx context.Context, header *types.Header) (*big.Int, error) LastAcceptedBlock() *types.Block GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error) + GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) } // Oracle recommends gas prices based on the content of recent @@ -231,6 +233,14 @@ func (oracle *Oracle) estimateNextBaseFee(ctx context.Context) (*big.Int, error) if err != nil { return nil, err } + acp224FeeConfig, _, err := oracle.backend.GetACP224FeeConfigAt(header) + if err != nil { + return nil, err + } + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + return nil, err + } // If the fetched block does not have a base fee, return nil as the base fee if header.BaseFee == nil { return nil, nil @@ -240,7 +250,7 @@ func (oracle *Oracle) estimateNextBaseFee(ctx context.Context) (*big.Int, error) // based on the current time and add it to the tip to estimate the // total gas price estimate. chainConfig := params.GetExtra(oracle.backend.ChainConfig()) - return customheader.EstimateNextBaseFee(chainConfig, feeConfig, header, oracle.clock.Unix()) + return customheader.EstimateNextBaseFee(chainConfig, feeConfig, acp176Config, header, oracle.clock.Unix()) } // SuggestPrice returns an estimated price for legacy transactions. diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 4690c4534b..c98df9ed97 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -47,6 +47,7 @@ import ( "github.com/ava-labs/subnet-evm/params/extras" customheader "github.com/ava-labs/subnet-evm/plugin/evm/header" "github.com/ava-labs/subnet-evm/plugin/evm/upgrade/legacy" + "github.com/ava-labs/subnet-evm/precompile/contracts/acp224feemanager" "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" "github.com/ava-labs/subnet-evm/rpc" "github.com/ava-labs/subnet-evm/utils" @@ -99,6 +100,9 @@ func (b *testBackend) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig return b.chain.GetFeeConfigAt(parent) } +func (b *testBackend) GetACP224FeeConfigAt(parent *types.Header) (commontype.ACP224FeeConfig, *big.Int, error) { + return b.chain.GetACP224FeeConfigAt(parent) +} func (b *testBackend) teardown() { b.chain.Stop() } @@ -140,8 +144,8 @@ func newTestBackend(t *testing.T, config *params.ChainConfig, numBlocks int, gen engine := dummy.NewFaker() // Generate testing blocks - targetBlockRate := params.GetExtra(config).FeeConfig.TargetBlockRate - _, blocks, _, err := core.GenerateChainWithGenesis(gspec, engine, numBlocks, targetBlockRate-1, genBlocks) + blockGap := uint64(1) + _, blocks, _, err := core.GenerateChainWithGenesis(gspec, engine, numBlocks, blockGap, genBlocks) if err != nil { t.Fatal(err) } @@ -267,8 +271,8 @@ func TestSuggestTipCapSimple(t *testing.T) { applyGasPriceTest(t, suggestTipCapTest{ chainConfig: params.TestChainConfig, numBlocks: 3, - genBlock: testGenBlock(t, 55, 370), - expectedTip: big.NewInt(1_287_001_288), + genBlock: testGenBlock(t, 55, 80), + expectedTip: big.NewInt(1), }, defaultOracleConfig()) } @@ -276,8 +280,8 @@ func TestSuggestTipCapSimpleFloor(t *testing.T) { applyGasPriceTest(t, suggestTipCapTest{ chainConfig: params.TestChainConfig, numBlocks: 1, - genBlock: testGenBlock(t, 55, 370), - expectedTip: big.NewInt(643_500_644), + genBlock: testGenBlock(t, 55, 80), + expectedTip: big.NewInt(1), }, defaultOracleConfig()) } @@ -292,7 +296,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) { signer := types.LatestSigner(params.TestChainConfig) baseFee := b.BaseFee() feeCap := new(big.Int).Add(baseFee, tip) - for j := 0; j < 185; j++ { + for j := 0; j < 40; j++ { tx := types.NewTx(&types.DynamicFeeTx{ ChainID: params.TestChainConfig.ChainID, Nonce: b.TxNonce(addr), @@ -308,7 +312,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) { } b.AddTx(tx) tx = types.NewTx(&types.DynamicFeeTx{ - ChainID: params.TestChainConfig.ChainID, + ChainID: params.TestEtnaChainConfig.ChainID, Nonce: b.TxNonce(addr), To: &common.Address{}, Gas: ethparams.TxGas, @@ -321,7 +325,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) { b.AddTx(tx) } }, - expectedTip: big.NewInt(1_287_001_288), + expectedTip: big.NewInt(1), }, defaultOracleConfig()) } @@ -330,7 +334,7 @@ func TestSuggestTipCapMinGas(t *testing.T) { chainConfig: params.TestChainConfig, numBlocks: 3, genBlock: testGenBlock(t, 500, 50), - expectedTip: big.NewInt(0), + expectedTip: big.NewInt(1), }, defaultOracleConfig()) } @@ -372,7 +376,7 @@ func TestSuggestGasPriceSubnetEVM(t *testing.T) { func TestSuggestTipCapMaxBlocksLookback(t *testing.T) { applyGasPriceTest(t, suggestTipCapTest{ - chainConfig: params.TestChainConfig, + chainConfig: params.TestEtnaChainConfig, numBlocks: 20, genBlock: testGenBlock(t, 550, 370), expectedTip: big.NewInt(5_807_226_111), @@ -383,14 +387,14 @@ func TestSuggestTipCapMaxBlocksSecondsLookback(t *testing.T) { applyGasPriceTest(t, suggestTipCapTest{ chainConfig: params.TestChainConfig, numBlocks: 20, - genBlock: testGenBlock(t, 550, 370), - expectedTip: big.NewInt(10_384_877_852), + genBlock: testGenBlock(t, 550, 80), + expectedTip: big.NewInt(1), }, timeCrunchOracleConfig()) } // Regression test to ensure the last estimation of base fee is not used // for the block immediately following a fee configuration update. -func TestSuggestGasPriceAfterFeeConfigUpdate(t *testing.T) { +func TestSuggestGasPriceAfterFeeManagerUpdate(t *testing.T) { require := require.New(t) config := Config{ Blocks: 20, @@ -398,26 +402,26 @@ func TestSuggestGasPriceAfterFeeConfigUpdate(t *testing.T) { } // create a chain config with fee manager enabled at genesis with [addr] as the admin - chainConfig := params.Copy(params.TestChainConfig) + chainConfig := params.Copy(params.TestEtnaChainConfig) chainConfigExtra := params.GetExtra(&chainConfig) chainConfigExtra.GenesisPrecompiles = extras.Precompiles{ feemanager.ConfigKey: feemanager.NewConfig(utils.NewUint64(0), []common.Address{addr}, nil, nil, nil), } // create a fee config with higher MinBaseFee and prepare it for inclusion in a tx - signer := types.LatestSigner(params.TestChainConfig) + signer := types.LatestSigner(params.TestEtnaChainConfig) highFeeConfig := chainConfigExtra.FeeConfig highFeeConfig.MinBaseFee = big.NewInt(28_000_000_000) data, err := feemanager.PackSetFeeConfig(highFeeConfig) require.NoError(err) - // before issuing the block changing the fee into the chain, the fee estimation should + // before issuing the block changing the fee into the chain, the base fee estimation should // follow the fee config in genesis. backend := newTestBackend(t, &chainConfig, 0, func(i int, b *core.BlockGen) {}) defer backend.teardown() oracle, err := NewOracle(backend, config) require.NoError(err) - got, err := oracle.SuggestPrice(context.Background()) + got, err := oracle.EstimateBaseFee(context.Background()) require.NoError(err) require.Equal(chainConfigExtra.FeeConfig.MinBaseFee, got) @@ -447,8 +451,71 @@ func TestSuggestGasPriceAfterFeeConfigUpdate(t *testing.T) { _, err = backend.chain.InsertChain(blocks) require.NoError(err) - // verify the suggested price follows the new fee config. - got, err = oracle.SuggestPrice(context.Background()) + // verify the estimated base fee follows the new fee config. + got, err = oracle.EstimateBaseFee(context.Background()) require.NoError(err) require.Equal(highFeeConfig.MinBaseFee, got) } + +func TestSuggestGasPriceAfterACP224FeeManagerUpdate(t *testing.T) { + require := require.New(t) + config := Config{ + Blocks: 20, + Percentile: 60, + } + + // create a chain config with fee manager enabled at genesis with [addr] as the admin + chainConfig := params.Copy(params.TestChainConfig) + chainConfigExtra := params.GetExtra(&chainConfig) + chainConfigExtra.GenesisPrecompiles = extras.Precompiles{ + acp224feemanager.ConfigKey: acp224feemanager.NewConfig(utils.NewUint64(0), []common.Address{addr}, nil, nil, nil), + } + + // create a fee config with higher MinBaseFee and prepare it for inclusion in a tx + signer := types.LatestSigner(params.TestChainConfig) + highFeeConfig := chainConfigExtra.ACP224FeeConfig + highFeeConfig.MinGasPrice = big.NewInt(13_000_000_000) + data, err := acp224feemanager.PackSetFeeConfig(highFeeConfig) + require.NoError(err) + + // before issuing the block changing the fee into the chain, the base fee estimation should + // follow the fee config in genesis. + backend := newTestBackend(t, &chainConfig, 0, func(i int, b *core.BlockGen) {}) + defer backend.teardown() + oracle, err := NewOracle(backend, config) + require.NoError(err) + got, err := oracle.EstimateBaseFee(context.Background()) + require.NoError(err) + require.Equal(chainConfigExtra.ACP224FeeConfig.MinGasPrice, got) + + // issue the block with tx that changes the fee + genesis := backend.chain.Genesis() + engine := backend.chain.Engine() + db := rawdb.NewDatabase(backend.chain.StateCache().DiskDB()) + blocks, _, err := core.GenerateChain(&chainConfig, genesis, engine, db, 1, chainConfigExtra.FeeConfig.TargetBlockRate, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{1}) + + // admin issues tx to change fee config to higher MinBaseFee + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainConfig.ChainID, + Nonce: b.TxNonce(addr), + To: &acp224feemanager.ContractAddress, + Gas: 500_000, + Value: common.Big0, + GasFeeCap: chainConfigExtra.ACP224FeeConfig.MinGasPrice, // give low fee, it should work since we still haven't applied high fees + GasTipCap: common.Big0, + Data: data, + }) + tx, err = types.SignTx(tx, signer, key) + require.NoError(err, "failed to create tx") + b.AddTx(tx) + }) + require.NoError(err) + _, err = backend.chain.InsertChain(blocks) + require.NoError(err) + + // verify the estimated base fee follows the new fee config. + got, err = oracle.EstimateBaseFee(context.Background()) + require.NoError(err) + require.Equal(highFeeConfig.MinGasPrice, got) +} diff --git a/eth/state_accessor.go b/eth/state_accessor.go index f4c290cf32..5ed69f4408 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -44,6 +44,7 @@ import ( "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/extstate" "github.com/ava-labs/subnet-evm/eth/tracers" + "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/plugin/evm/customrawdb" ) @@ -261,7 +262,8 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, // Assemble the transaction call message and return if the requested offset msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + rulesExtra := params.GetRulesExtra(eth.blockchain.Config().Rules(block.Number(), params.IsMergeTODO, block.Time())) + context := core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), eth.blockchain, nil) if idx == txIndex { return msg, context, statedb, release, nil } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 30e50f769e..01f58faf66 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -298,8 +298,9 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // Fetch and execute the block trace taskCh for task := range taskCh { var ( - signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), task.block.Time()) - blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil) + rulesExtra = params.GetRulesExtra(api.backend.ChainConfig().Rules(task.block.Number(), params.IsMergeTODO, task.block.Time())) + signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), task.block.Time()) + blockCtx = core.NewEVMBlockContext(rulesExtra.AvalancheRules, task.block.Header(), api.chainContext(ctx), nil) ) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { @@ -562,7 +563,8 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config roots []common.Hash signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) chainConfig = api.backend.ChainConfig() - vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + rulesExtra = params.GetRulesExtra(chainConfig.Rules(block.Number(), params.IsMergeTODO, block.Time())) + vmctx = core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), api.chainContext(ctx), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) for i, tx := range block.Transactions() { @@ -645,12 +647,13 @@ func (api *baseAPI) traceBlock(ctx context.Context, block *types.Block, config * } // Native tracers have low overhead var ( - txs = block.Transactions() - blockHash = block.Hash() - is158 = api.backend.ChainConfig().IsEIP158(block.Number()) - blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) - results = make([]*txTraceResult, len(txs)) + txs = block.Transactions() + blockHash = block.Hash() + is158 = api.backend.ChainConfig().IsEIP158(block.Number()) + rulesExtra = params.GetRulesExtra(api.backend.ChainConfig().Rules(block.Number(), params.IsMergeTODO, block.Time())) + blockCtx = core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), api.chainContext(ctx), nil) + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) + results = make([]*txTraceResult, len(txs)) ) for i, tx := range txs { // Generate the next state snapshot fast without tracing @@ -679,12 +682,13 @@ func (api *baseAPI) traceBlock(ctx context.Context, block *types.Block, config * func (api *baseAPI) traceBlockParallel(ctx context.Context, block *types.Block, statedb *state.StateDB, config *TraceConfig) ([]*txTraceResult, error) { // Execute all the transaction contained within the block concurrently var ( - txs = block.Transactions() - blockHash = block.Hash() - blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) - results = make([]*txTraceResult, len(txs)) - pend sync.WaitGroup + txs = block.Transactions() + blockHash = block.Hash() + rulesExtra = params.GetRulesExtra(api.backend.ChainConfig().Rules(block.Number(), params.IsMergeTODO, block.Time())) + blockCtx = core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), api.chainContext(ctx), nil) + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) + results = make([]*txTraceResult, len(txs)) + pend sync.WaitGroup ) threads := runtime.NumCPU() if threads > len(txs) { @@ -793,7 +797,8 @@ func (api *FileTracerAPI) standardTraceBlockToFile(ctx context.Context, block *t dumps []string signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) chainConfig = api.backend.ChainConfig() - vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + rulesExtra = params.GetRulesExtra(chainConfig.Rules(block.Number(), params.IsMergeTODO, block.Time())) + vmctx = core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), api.chainContext(ctx), nil) canon = true ) // Check if there are any overrides: the caller may wish to enable a future @@ -959,7 +964,8 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() - vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + rulesExtra := params.GetRulesExtra(api.backend.ChainConfig().Rules(block.Number(), params.IsMergeTODO, block.Time())) + vmctx := core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), api.chainContext(ctx), nil) // Apply the customization rules if required. if config != nil { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 8ba2333f54..e27de50c71 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -219,7 +219,8 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block for idx, tx := range block.Transactions() { msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), b.chain, nil) + rulesExtra := params.GetRulesExtra(b.ChainConfig().Rules(block.Number(), params.IsMergeTODO, block.Time())) + context := core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), b.chain, nil) if idx == txIndex { return msg, context, statedb, release, nil } diff --git a/ethclient/simulated/options_test.go b/ethclient/simulated/options_test.go index fc5b6cfcee..484e102715 100644 --- a/ethclient/simulated/options_test.go +++ b/ethclient/simulated/options_test.go @@ -37,6 +37,7 @@ import ( "github.com/ava-labs/libevm/core/types" ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/params/extras" ) // Tests that the simulator starts with the initial gas limit in the genesis block, @@ -60,8 +61,15 @@ func TestWithBlockGasLimitOption(t *testing.T) { if err != nil { t.Fatalf("failed to retrieve head block: %v", err) } - if head.GasLimit() != 12_345_678 { - t.Errorf("head gas limit mismatch: have %v, want %v", head.GasLimit(), 12_345_678) + acp176Config, err := extras.DefaultACP224FeeConfig.ToACP176Config() + if err != nil { + t.Fatalf("failed to convert ACP224 fee config to ACP176 config: %v", err) + } + if err != nil { + t.Fatalf("failed to convert ACP224 fee config to ACP176 config: %v", err) + } + if head.GasLimit() != uint64(acp176Config.MinMaxCapacity()) { + t.Errorf("head gas limit mismatch: have %v, want %v", head.GasLimit(), acp176Config.MinMaxCapacity()) } } diff --git a/go.mod b/go.mod index f648899801..c5d4a758d5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.9 require ( github.com/VictoriaMetrics/fastcache v1.12.1 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/avalanchego v1.13.5 + github.com/ava-labs/avalanchego v1.13.6-0.20250912174155-ed70ad638d2c github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 github.com/ava-labs/libevm v1.13.14-0.3.0.rc.6 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 527248ec03..36168b88a9 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ava-labs/avalanchego v1.13.5 h1:uOZDhGOdwITPXA496KwF9RNBheEq3pOH4w7w+QLValo= -github.com/ava-labs/avalanchego v1.13.5/go.mod h1:/eugkYcDQfCt9czHr/Jlw3MW/1DIoI7Cm0maqNkuWMs= +github.com/ava-labs/avalanchego v1.13.6-0.20250912174155-ed70ad638d2c h1:S7l1PEaYYRmT2bs9LXY2z1wk7UAyMU6veavKf4IN9e4= +github.com/ava-labs/avalanchego v1.13.6-0.20250912174155-ed70ad638d2c/go.mod h1:/eugkYcDQfCt9czHr/Jlw3MW/1DIoI7Cm0maqNkuWMs= github.com/ava-labs/coreth v0.15.4-rc.3 h1:v33OOerxpGIKa1MpljXMBB3Yljy23xzsez3E/dn7TzY= github.com/ava-labs/coreth v0.15.4-rc.3/go.mod h1:Esb0FK+KJr6co7rrhtBWsmSMXEL5JWelEsijlqAHdq0= github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 h1:aMcrLbpJ/dyu2kZDf/Di/4JIWsUcYPyTDKymiHpejt0= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index faaaa699e7..d536bfe71a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1095,7 +1095,8 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S defer cancel() // Get a new instance of the EVM. - blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) + rulesExtra := params.GetRulesExtra(b.ChainConfig().Rules(header.Number, params.IsMergeTODO, header.Time)) + blockCtx := core.NewEVMBlockContext(rulesExtra.AvalancheRules, header, NewChainContext(ctx, b), nil) if blockOverrides != nil { blockOverrides.Apply(&blockCtx) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index cc812772ca..fecd851bd8 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -567,7 +567,8 @@ func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state *state vmConfig = b.chain.GetVMConfig() } txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(header, b.chain, nil) + rulesExtra := params.GetRulesExtra(b.ChainConfig().Rules(header.Number, params.IsMergeTODO, header.Time)) + context := core.NewEVMBlockContext(rulesExtra.AvalancheRules, header, b.chain, nil) if blockContext != nil { context = *blockContext } @@ -1029,7 +1030,7 @@ func TestSignTransaction(t *testing.T) { if err != nil { t.Fatal(err) } - expect := `{"type":"0x2","chainId":"0x1","nonce":"0x0","to":"0x703c4b2bd70c169f5717101caee543299fc946c7","gas":"0x5208","gasPrice":null,"maxPriorityFeePerGas":"0x0","maxFeePerGas":"0xba43b7400","value":"0x1","input":"0x","accessList":[],"v":"0x0","r":"0xa7bbf5672b6f78e934bd380aad0b2626d5337e96c12f1e755fa5522ba7a314bd","s":"0x4d661f8c7b850b7dc3ce1c8c7b443a4434a22fe3ad14cc463205e0259546f0c8","yParity":"0x0","hash":"0x0333d97cbdababb6af7cc55a6f64d47711b8e18a93d7343657508a454407a82c"}` + expect := `{"type":"0x2","chainId":"0x1","nonce":"0x0","to":"0x703c4b2bd70c169f5717101caee543299fc946c7","gas":"0x5208","gasPrice":null,"maxPriorityFeePerGas":"0x0","maxFeePerGas":"0x2","value":"0x1","input":"0x","accessList":[],"v":"0x1","r":"0x5a32230e497be0277b58afb995227a167e087462fb770057ed6946f5ef5a2df5","s":"0x431e048124baffbd67bc35df940bb9f5ddf8a36afb2672616d075ac39415e885","yParity":"0x1","hash":"0xf5e941beeca516d3d3dca2707d74c54a58e07365b89efc5de58dd7b6041ef78e"}` if !bytes.Equal(tx, []byte(expect)) { t.Errorf("result mismatch. Have:\n%s\nWant:\n%s\n", tx, expect) } diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json index afb12e778f..149c6ac3d8 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json @@ -1,13 +1,13 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0xb6592ae424eafd61ba0339dffe9b080c5b3f005224186447df26638e6c0bbd3b", + "hash": "0xd2ed27e3bcf7c1c8403ba0b13951b9d036bcdd7fbdae6eb0c1ae149e59337c18", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -17,13 +17,13 @@ "parentHash": "0xba5fb8e40c7c77b70d6977300cea5072e1811622808e41b3488832031e4f6a6d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x2de", - "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", + "size": "0x29b", + "stateRoot": "0x8b63f66c0da7b9ed8e8a6ede137b919fa67fcefaaa70ce8fb455d2d63a517c6c", "timestamp": "0xa", "totalDifficulty": "0x1", "transactions": [ - "0xdf92bc7c4c0341ecbdcd2a3ca7011fe9e21df4b8553bf0c8caabe6cb4a1aee26" + "0x941d1c99ff85ba37ae992f86bc5467bdbaea402974f4703914e432ff82dd5389" ], - "transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5", + "transactionsRoot": "0x2091c9f520d1aee00e5552f3e8da86e1d0668593e8dc189c1c2f429401ab2c23", "uncles": [] } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json index 740079ad66..b288b622e6 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json @@ -1,45 +1,45 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "hash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0xae0813ad53958eb4030c34dd19872f06d9eab41bf6140a4536258a2b4f46f45d", + "parentHash": "0x4488a80805f2911efa27cd150af52898714b001c04b8b5208210ff3e84065b2d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x2de", - "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", + "size": "0x29b", + "stateRoot": "0x6d333799439bcfa2a9901891f412879293ec93d1a01429452fa3cdadfb86ccb9", "timestamp": "0x5a", "totalDifficulty": "0x9", "transactions": [ { - "blockHash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "blockHash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "blockNumber": "0x9", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gas": "0x5208", - "gasPrice": "0x5d21dba00", - "hash": "0x237f95840187a93f8aaf8d6f1515f8a8ac9d9359fcb0c220cdb3d642d6b9a19a", + "gasPrice": "0x1", + "hash": "0x788f1d9b6ab5314ca3157e4e514b602d3d9cf98773402749cd0ea73d4f373330", "input": "0x", "nonce": "0x8", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", "transactionIndex": "0x0", "value": "0x3e8", "type": "0x0", - "v": "0x1c", - "r": "0xd7cdc527490b7ba29c515aae3bbe80c67729cda7f736e6515652cfc40e9da68f", - "s": "0x4d0a4a59bef165b16f910bdadd41efaaad1b73549bacc35eaf6d073eb1fb92b7" + "v": "0x1b", + "r": "0x405203de7391198cd3dd1bfc2b12201053a799be421d74a5d3bc1338e75abfed", + "s": "0x683e95bab8c6c2d757bdd5cd0d75dc2bc9e54d9066023935a5d2b93281bfd0a3" } ], - "transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb", + "transactionsRoot": "0xf3f69c61b639254ec6538351b9edaebce124270a70dd2d7fbc8d197b2a7cd928", "uncles": [] } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json index cd6c2efd55..2771475121 100644 --- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json +++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json @@ -1,29 +1,29 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0b01774210aec0d5e0969a2e2cf0d6ec95ede0e7e9692d2e4be1d0779c9059c1", + "hash": "0x0b1d5db9740143d57114cc750c89ec59d75276798a76aa24255bcaaf9fcbc255", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "parentHash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x2de", - "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", + "size": "0x29b", + "stateRoot": "0xda6a753ddf04cde2621800ed8ce40fa9a09328789e5b309f0e32b4827262ae61", "timestamp": "0x64", "totalDifficulty": "0xa", "transactions": [ - "0x71be223424ab6e3457513a760b196d43b094414c32a70ff929b2b720a16b832d" + "0x2019a52ff9645912a406c958eaf597be403ba9cadb25b981bcbdb2b38711c12d" ], - "transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01", + "transactionsRoot": "0x4022accb18056de5994cfb85f3c2721a86c9b1de72ef02f3b224e9c913a18a10", "uncles": [] } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json index afb12e778f..149c6ac3d8 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json @@ -1,13 +1,13 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0xb6592ae424eafd61ba0339dffe9b080c5b3f005224186447df26638e6c0bbd3b", + "hash": "0xd2ed27e3bcf7c1c8403ba0b13951b9d036bcdd7fbdae6eb0c1ae149e59337c18", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -17,13 +17,13 @@ "parentHash": "0xba5fb8e40c7c77b70d6977300cea5072e1811622808e41b3488832031e4f6a6d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x2de", - "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", + "size": "0x29b", + "stateRoot": "0x8b63f66c0da7b9ed8e8a6ede137b919fa67fcefaaa70ce8fb455d2d63a517c6c", "timestamp": "0xa", "totalDifficulty": "0x1", "transactions": [ - "0xdf92bc7c4c0341ecbdcd2a3ca7011fe9e21df4b8553bf0c8caabe6cb4a1aee26" + "0x941d1c99ff85ba37ae992f86bc5467bdbaea402974f4703914e432ff82dd5389" ], - "transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5", + "transactionsRoot": "0x2091c9f520d1aee00e5552f3e8da86e1d0668593e8dc189c1c2f429401ab2c23", "uncles": [] } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json index 740079ad66..b288b622e6 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json @@ -1,45 +1,45 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "hash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0xae0813ad53958eb4030c34dd19872f06d9eab41bf6140a4536258a2b4f46f45d", + "parentHash": "0x4488a80805f2911efa27cd150af52898714b001c04b8b5208210ff3e84065b2d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x2de", - "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", + "size": "0x29b", + "stateRoot": "0x6d333799439bcfa2a9901891f412879293ec93d1a01429452fa3cdadfb86ccb9", "timestamp": "0x5a", "totalDifficulty": "0x9", "transactions": [ { - "blockHash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "blockHash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "blockNumber": "0x9", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gas": "0x5208", - "gasPrice": "0x5d21dba00", - "hash": "0x237f95840187a93f8aaf8d6f1515f8a8ac9d9359fcb0c220cdb3d642d6b9a19a", + "gasPrice": "0x1", + "hash": "0x788f1d9b6ab5314ca3157e4e514b602d3d9cf98773402749cd0ea73d4f373330", "input": "0x", "nonce": "0x8", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", "transactionIndex": "0x0", "value": "0x3e8", "type": "0x0", - "v": "0x1c", - "r": "0xd7cdc527490b7ba29c515aae3bbe80c67729cda7f736e6515652cfc40e9da68f", - "s": "0x4d0a4a59bef165b16f910bdadd41efaaad1b73549bacc35eaf6d073eb1fb92b7" + "v": "0x1b", + "r": "0x405203de7391198cd3dd1bfc2b12201053a799be421d74a5d3bc1338e75abfed", + "s": "0x683e95bab8c6c2d757bdd5cd0d75dc2bc9e54d9066023935a5d2b93281bfd0a3" } ], - "transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb", + "transactionsRoot": "0xf3f69c61b639254ec6538351b9edaebce124270a70dd2d7fbc8d197b2a7cd928", "uncles": [] } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json index cd6c2efd55..2771475121 100644 --- a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json @@ -1,29 +1,29 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0b01774210aec0d5e0969a2e2cf0d6ec95ede0e7e9692d2e4be1d0779c9059c1", + "hash": "0x0b1d5db9740143d57114cc750c89ec59d75276798a76aa24255bcaaf9fcbc255", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "parentHash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "size": "0x2de", - "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", + "size": "0x29b", + "stateRoot": "0xda6a753ddf04cde2621800ed8ce40fa9a09328789e5b309f0e32b4827262ae61", "timestamp": "0x64", "totalDifficulty": "0xa", "transactions": [ - "0x71be223424ab6e3457513a760b196d43b094414c32a70ff929b2b720a16b832d" + "0x2019a52ff9645912a406c958eaf597be403ba9cadb25b981bcbdb2b38711c12d" ], - "transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01", + "transactionsRoot": "0x4022accb18056de5994cfb85f3c2721a86c9b1de72ef02f3b224e9c913a18a10", "uncles": [] } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json index 24afbebc14..cdc8a65a77 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json @@ -2,18 +2,18 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545", + "blockHash": "0xd74e08700a0b69f81670d23e3ae4095f1bb5a7a3a93e131ca26747b510de8d03", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", - "effectiveGasPrice": "0x5d21dba01", + "effectiveGasPrice": "0x2", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5208", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", - "transactionHash": "0x7e71344129674f4bbfdaa86313d005a96581993d93ae3a30d81b13fa25579eb2", + "transactionHash": "0x77255f1a75d6c0252a56539ad11edf9047df59dc390085946b410f406b9cd254", "transactionIndex": "0x0", "type": "0x3" } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json index 84f48f0ea5..4ea47c0440 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json @@ -1,17 +1,17 @@ [ { - "blockHash": "0xdcba2f7c99ad0f58002737f1393578f1b72aca3270c1722d9d0fbdc2439b0484", + "blockHash": "0x67bc227618d9f281e84b55dceea251bcbdd1061217a469f85f9af7e04c14a1fb", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0xcf50", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": null, - "transactionHash": "0x22aa617165f83a9f8c191c2b7724ae43eeb1249bee06c98c03c7624c21d27dc8", + "transactionHash": "0x3b727d5e08e2176d96ee1bea244f772b592acd2cda1acc3d822c8b7f3de362f9", "transactionIndex": "0x0", "type": "0x0" } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json index 977cbe81df..8d86b5d8b6 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json @@ -1,17 +1,17 @@ [ { - "blockHash": "0xfa29fee1c5195fda47b23d3ce5259e314eb7578d18b76b36068d0e321db024e1", + "blockHash": "0x8cbe87a61fa9bb8a89bac2ff327f75e18d6b53618dabc9788f7f61e4d6c43421", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", - "effectiveGasPrice": "0x5d21dbbf4", + "effectiveGasPrice": "0x1f5", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x538d", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x0", "to": "0x0000000000000000000000000000000000031ec7", - "transactionHash": "0x4e1e9194ca6f9d4e1736e9e441f66104f273548ed6d91b236a5f9c2ea10fa06d", + "transactionHash": "0x8416458a5e36b7b2f33020bd43d121c8d42c787d743b6eaf64ef09d663892b40", "transactionIndex": "0x0", "type": "0x2" } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json index 78bec365a3..e483f9fc7e 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json @@ -1,10 +1,10 @@ [ { - "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", + "blockHash": "0x21e527bcf2f59792ad6e35ba7cf3f92a3f319ed7d74581371a0c3a6eb53d4fd7", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5e28", "logs": [ @@ -17,9 +17,9 @@ ], "data": "0x000000000000000000000000000000000000000000000000000000000000000d", "blockNumber": "0x3", - "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", + "transactionHash": "0x551ede5f333e69dfb59dd42f670dbe3f66bb694fafabe02b689436be7dc55421", "transactionIndex": "0x0", - "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", + "blockHash": "0x21e527bcf2f59792ad6e35ba7cf3f92a3f319ed7d74581371a0c3a6eb53d4fd7", "logIndex": "0x0", "removed": false } @@ -27,7 +27,7 @@ "logsBloom": "0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000800000000000000008000000000000000000000000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000400000000002000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000", "status": "0x1", "to": "0x0000000000000000000000000000000000031ec7", - "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", + "transactionHash": "0x551ede5f333e69dfb59dd42f670dbe3f66bb694fafabe02b689436be7dc55421", "transactionIndex": "0x0", "type": "0x0" } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json index e4c599bf22..61753f96b4 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json @@ -1,17 +1,17 @@ [ { - "blockHash": "0xf9081fe79fcdfd6a743577cc42fa17bec5e6cc1ebf5807b771724bf88b454b71", + "blockHash": "0x713ac10c7315b3ec44de14280f4422852dd151dd48619e9ae3827f230665c0cd", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5208", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", - "transactionHash": "0xdf92bc7c4c0341ecbdcd2a3ca7011fe9e21df4b8553bf0c8caabe6cb4a1aee26", + "transactionHash": "0x941d1c99ff85ba37ae992f86bc5467bdbaea402974f4703914e432ff82dd5389", "transactionIndex": "0x0", "type": "0x0" } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json index 24afbebc14..cdc8a65a77 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json @@ -2,18 +2,18 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545", + "blockHash": "0xd74e08700a0b69f81670d23e3ae4095f1bb5a7a3a93e131ca26747b510de8d03", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", - "effectiveGasPrice": "0x5d21dba01", + "effectiveGasPrice": "0x2", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5208", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", - "transactionHash": "0x7e71344129674f4bbfdaa86313d005a96581993d93ae3a30d81b13fa25579eb2", + "transactionHash": "0x77255f1a75d6c0252a56539ad11edf9047df59dc390085946b410f406b9cd254", "transactionIndex": "0x0", "type": "0x3" } diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json index a37c617ebc..a20cdc228a 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json @@ -1,13 +1,13 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0xb6592ae424eafd61ba0339dffe9b080c5b3f005224186447df26638e6c0bbd3b", + "hash": "0xd2ed27e3bcf7c1c8403ba0b13951b9d036bcdd7fbdae6eb0c1ae149e59337c18", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -17,8 +17,8 @@ "parentHash": "0xba5fb8e40c7c77b70d6977300cea5072e1811622808e41b3488832031e4f6a6d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", + "stateRoot": "0x8b63f66c0da7b9ed8e8a6ede137b919fa67fcefaaa70ce8fb455d2d63a517c6c", "timestamp": "0xa", "totalDifficulty": "0x1", - "transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5" + "transactionsRoot": "0x2091c9f520d1aee00e5552f3e8da86e1d0668593e8dc189c1c2f429401ab2c23" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json index 9e67e3b6ca..b50c7450ec 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json @@ -1,24 +1,24 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "hash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0xae0813ad53958eb4030c34dd19872f06d9eab41bf6140a4536258a2b4f46f45d", + "parentHash": "0x4488a80805f2911efa27cd150af52898714b001c04b8b5208210ff3e84065b2d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", + "stateRoot": "0x6d333799439bcfa2a9901891f412879293ec93d1a01429452fa3cdadfb86ccb9", "timestamp": "0x5a", "totalDifficulty": "0x9", - "transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb" + "transactionsRoot": "0xf3f69c61b639254ec6538351b9edaebce124270a70dd2d7fbc8d197b2a7cd928" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json index 6eb0120fdb..7d13c8657e 100644 --- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json +++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json @@ -1,24 +1,24 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0b01774210aec0d5e0969a2e2cf0d6ec95ede0e7e9692d2e4be1d0779c9059c1", + "hash": "0x0b1d5db9740143d57114cc750c89ec59d75276798a76aa24255bcaaf9fcbc255", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "parentHash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", + "stateRoot": "0xda6a753ddf04cde2621800ed8ce40fa9a09328789e5b309f0e32b4827262ae61", "timestamp": "0x64", "totalDifficulty": "0xa", - "transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01" + "transactionsRoot": "0x4022accb18056de5994cfb85f3c2721a86c9b1de72ef02f3b224e9c913a18a10" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json index a37c617ebc..a20cdc228a 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json @@ -1,13 +1,13 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0xb6592ae424eafd61ba0339dffe9b080c5b3f005224186447df26638e6c0bbd3b", + "hash": "0xd2ed27e3bcf7c1c8403ba0b13951b9d036bcdd7fbdae6eb0c1ae149e59337c18", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -17,8 +17,8 @@ "parentHash": "0xba5fb8e40c7c77b70d6977300cea5072e1811622808e41b3488832031e4f6a6d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d", + "stateRoot": "0x8b63f66c0da7b9ed8e8a6ede137b919fa67fcefaaa70ce8fb455d2d63a517c6c", "timestamp": "0xa", "totalDifficulty": "0x1", - "transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5" + "transactionsRoot": "0x2091c9f520d1aee00e5552f3e8da86e1d0668593e8dc189c1c2f429401ab2c23" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json index 9e67e3b6ca..b50c7450ec 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json @@ -1,24 +1,24 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "hash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0x9", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0xae0813ad53958eb4030c34dd19872f06d9eab41bf6140a4536258a2b4f46f45d", + "parentHash": "0x4488a80805f2911efa27cd150af52898714b001c04b8b5208210ff3e84065b2d", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac", + "stateRoot": "0x6d333799439bcfa2a9901891f412879293ec93d1a01429452fa3cdadfb86ccb9", "timestamp": "0x5a", "totalDifficulty": "0x9", - "transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb" + "transactionsRoot": "0xf3f69c61b639254ec6538351b9edaebce124270a70dd2d7fbc8d197b2a7cd928" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json index 6eb0120fdb..7d13c8657e 100644 --- a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json +++ b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json @@ -1,24 +1,24 @@ { - "baseFeePerGas": "0x5d21dba00", + "baseFeePerGas": "0x1", "blobGasUsed": "0x0", "blockGasCost": "0x0", "difficulty": "0x1", "excessBlobGas": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x7a1200", + "extraData": "0x000000000098447800000000000052080000000000000000", + "gasLimit": "0x989680", "gasUsed": "0x5208", - "hash": "0x0b01774210aec0d5e0969a2e2cf0d6ec95ede0e7e9692d2e4be1d0779c9059c1", + "hash": "0x0b1d5db9740143d57114cc750c89ec59d75276798a76aa24255bcaaf9fcbc255", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "miner": "0x0000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nonce": "0x0000000000000000", "number": "0xa", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0ac6abf1efe35140d080b10789a0c0581a7f4b040558920fcb86325bbb7fc0a5", + "parentHash": "0xef08fe152b50ff9a4220f2ba7c27f07eaa00abb156fd44ce513f6ff67f0c03e9", "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34", + "stateRoot": "0xda6a753ddf04cde2621800ed8ce40fa9a09328789e5b309f0e32b4827262ae61", "timestamp": "0x64", "totalDifficulty": "0xa", - "transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01" + "transactionsRoot": "0x4022accb18056de5994cfb85f3c2721a86c9b1de72ef02f3b224e9c913a18a10" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json index 674b7e4f28..51918e7c31 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json @@ -1,18 +1,18 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545", + "blockHash": "0xd74e08700a0b69f81670d23e3ae4095f1bb5a7a3a93e131ca26747b510de8d03", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", - "effectiveGasPrice": "0x5d21dba01", + "effectiveGasPrice": "0x2", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5208", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", - "transactionHash": "0x7e71344129674f4bbfdaa86313d005a96581993d93ae3a30d81b13fa25579eb2", + "transactionHash": "0x77255f1a75d6c0252a56539ad11edf9047df59dc390085946b410f406b9cd254", "transactionIndex": "0x0", "type": "0x3" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json index 0345b77cef..919881e842 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json @@ -1,16 +1,16 @@ { - "blockHash": "0xdcba2f7c99ad0f58002737f1393578f1b72aca3270c1722d9d0fbdc2439b0484", + "blockHash": "0x67bc227618d9f281e84b55dceea251bcbdd1061217a469f85f9af7e04c14a1fb", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0xcf50", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": null, - "transactionHash": "0x22aa617165f83a9f8c191c2b7724ae43eeb1249bee06c98c03c7624c21d27dc8", + "transactionHash": "0x3b727d5e08e2176d96ee1bea244f772b592acd2cda1acc3d822c8b7f3de362f9", "transactionIndex": "0x0", "type": "0x0" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json index 2d68a68bd9..ef38729ad9 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json @@ -1,16 +1,16 @@ { - "blockHash": "0xea28a367715debefc3b6d9f5ed5ab5b8d3a13b956e87f05f148852d3e1e522b5", + "blockHash": "0x46e967065beb0fd473a0e74420803329bac55b2bfadc3e70957945e3b4b6b3da", "blockNumber": "0x5", "contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18", "cumulativeGasUsed": "0xe01c", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0xe01c", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": null, - "transactionHash": "0x8afe030574f663fe5096371d6f58a6287bfb3e0c73a5050220f5775a08e7abc9", + "transactionHash": "0x9a012322bddd6075c93dda3f5ad1c782fdd3552510fb7bf66ca87f6112550f46", "transactionIndex": "0x0", "type": "0x1" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json index 6b558601b2..0e9e807937 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json @@ -1,16 +1,16 @@ { - "blockHash": "0xfa29fee1c5195fda47b23d3ce5259e314eb7578d18b76b36068d0e321db024e1", + "blockHash": "0x8cbe87a61fa9bb8a89bac2ff327f75e18d6b53618dabc9788f7f61e4d6c43421", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", - "effectiveGasPrice": "0x5d21dbbf4", + "effectiveGasPrice": "0x1f5", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x538d", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x0", "to": "0x0000000000000000000000000000000000031ec7", - "transactionHash": "0x4e1e9194ca6f9d4e1736e9e441f66104f273548ed6d91b236a5f9c2ea10fa06d", + "transactionHash": "0x8416458a5e36b7b2f33020bd43d121c8d42c787d743b6eaf64ef09d663892b40", "transactionIndex": "0x0", "type": "0x2" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json index 8411f0b255..713a248cfa 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json @@ -1,16 +1,16 @@ { - "blockHash": "0xf9081fe79fcdfd6a743577cc42fa17bec5e6cc1ebf5807b771724bf88b454b71", + "blockHash": "0x713ac10c7315b3ec44de14280f4422852dd151dd48619e9ae3827f230665c0cd", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5208", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e", - "transactionHash": "0xdf92bc7c4c0341ecbdcd2a3ca7011fe9e21df4b8553bf0c8caabe6cb4a1aee26", + "transactionHash": "0x941d1c99ff85ba37ae992f86bc5467bdbaea402974f4703914e432ff82dd5389", "transactionIndex": "0x0", "type": "0x0" } \ No newline at end of file diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json index 6aebc6190b..fb572fe733 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json @@ -1,9 +1,9 @@ { - "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", + "blockHash": "0x21e527bcf2f59792ad6e35ba7cf3f92a3f319ed7d74581371a0c3a6eb53d4fd7", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", - "effectiveGasPrice": "0x5d21dba00", + "effectiveGasPrice": "0x1", "from": "0x703c4b2bd70c169f5717101caee543299fc946c7", "gasUsed": "0x5e28", "logs": [ @@ -16,9 +16,9 @@ ], "data": "0x000000000000000000000000000000000000000000000000000000000000000d", "blockNumber": "0x3", - "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", + "transactionHash": "0x551ede5f333e69dfb59dd42f670dbe3f66bb694fafabe02b689436be7dc55421", "transactionIndex": "0x0", - "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64", + "blockHash": "0x21e527bcf2f59792ad6e35ba7cf3f92a3f319ed7d74581371a0c3a6eb53d4fd7", "logIndex": "0x0", "removed": false } @@ -26,7 +26,7 @@ "logsBloom": "0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000800000000000000008000000000000000000000000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000400000000002000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000", "status": "0x1", "to": "0x0000000000000000000000000000000000031ec7", - "transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf", + "transactionHash": "0x551ede5f333e69dfb59dd42f670dbe3f66bb694fafabe02b689436be7dc55421", "transactionIndex": "0x0", "type": "0x0" } \ No newline at end of file diff --git a/miner/worker.go b/miner/worker.go index e5a2890b24..edd2d3d81f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -40,6 +40,7 @@ import ( "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/evm/predicate" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/state" "github.com/ava-labs/libevm/core/types" @@ -154,12 +155,20 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte if err != nil { return nil, err } + acp224FeeConfig, _, err := w.chain.GetACP224FeeConfigAt(parent) + if err != nil { + return nil, err + } + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + return nil, err + } chainConfig := params.GetExtra(w.chainConfig) - gasLimit, err := customheader.GasLimit(chainConfig, feeConfig, parent, timestamp) + gasLimit, err := customheader.GasLimit(chainConfig, feeConfig, acp176Config, parent, timestamp) if err != nil { return nil, fmt.Errorf("calculating new gas limit: %w", err) } - baseFee, err := customheader.BaseFee(chainConfig, feeConfig, parent, timestamp) + baseFee, err := customheader.BaseFee(chainConfig, feeConfig, acp176Config, parent, timestamp) if err != nil { return nil, fmt.Errorf("failed to calculate new base fee: %w", err) } @@ -208,12 +217,13 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte return nil, fmt.Errorf("failed to prepare header for mining: %w", err) } - env, err := w.createCurrentEnvironment(predicateContext, parent, header, feeConfig, tstart) + env, err := w.createCurrentEnvironment(predicateContext, parent, header, feeConfig, acp176Config, tstart) if err != nil { return nil, fmt.Errorf("failed to create new current environment: %w", err) } if header.ParentBeaconRoot != nil { - context := core.NewEVMBlockContext(header, w.chain, nil) + rulesExtra := params.GetRulesExtra(env.rules) + context := core.NewEVMBlockContext(rulesExtra.AvalancheRules, header, w.chain, nil) vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) } @@ -278,13 +288,20 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte return w.commit(env) } -func (w *worker) createCurrentEnvironment(predicateContext *precompileconfig.PredicateContext, parent *types.Header, header *types.Header, feeConfig commontype.FeeConfig, tstart time.Time) (*environment, error) { +func (w *worker) createCurrentEnvironment( + predicateContext *precompileconfig.PredicateContext, + parent *types.Header, + header *types.Header, + feeConfig commontype.FeeConfig, + acp176Config acp176.Config, + tstart time.Time, +) (*environment, error) { currentState, err := w.chain.StateAt(parent.Root) if err != nil { return nil, err } chainConfig := params.GetExtra(w.chainConfig) - capacity, err := customheader.GasCapacity(chainConfig, feeConfig, parent, header.Time) + capacity, err := customheader.GasCapacity(chainConfig, feeConfig, acp176Config, parent, header.Time) if err != nil { return nil, fmt.Errorf("calculating gas capacity: %w", err) } @@ -350,7 +367,8 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, coinb blockContext vm.BlockContext ) - if params.GetRulesExtra(env.rules).IsDurango { + rulesExtra := params.GetRulesExtra(env.rules) + if rulesExtra.IsDurango { results, err := core.CheckPredicates(env.rules, env.predicateContext, tx) if err != nil { log.Debug("Transaction predicate failed verification in miner", "tx", tx.Hash(), "err", err) @@ -362,9 +380,9 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, coinb if err != nil { return nil, fmt.Errorf("failed to marshal predicate results: %w", err) } - blockContext = core.NewEVMBlockContextWithPredicateResults(env.header, w.chain, &coinbase, predicateResultsBytes) + blockContext = core.NewEVMBlockContextWithPredicateResults(rulesExtra.AvalancheRules, env.header, w.chain, &coinbase, predicateResultsBytes) } else { - blockContext = core.NewEVMBlockContext(env.header, w.chain, &coinbase) + blockContext = core.NewEVMBlockContext(rulesExtra.AvalancheRules, env.header, w.chain, &coinbase) } receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, blockContext, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) diff --git a/params/config_extra.go b/params/config_extra.go index 626ac05433..de2a02f9da 100644 --- a/params/config_extra.go +++ b/params/config_extra.go @@ -23,8 +23,9 @@ const ( ) var ( - DefaultChainID = big.NewInt(43214) - DefaultFeeConfig = extras.DefaultFeeConfig + DefaultChainID = big.NewInt(43214) + DefaultFeeConfig = extras.DefaultFeeConfig + DefaultACP224FeeConfig = extras.DefaultACP224FeeConfig initiallyActive = uint64(upgrade.InitiallyActiveTime.Unix()) unscheduledActivation = uint64(upgrade.UnscheduledActivationTime.Unix()) diff --git a/params/config_test.go b/params/config_test.go index b6e017bb1f..ba6e9533eb 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -283,6 +283,7 @@ func TestChainConfigMarshalWithUpgrades(t *testing.T) { }, &extras.ChainConfig{ FeeConfig: DefaultFeeConfig, + ACP224FeeConfig: DefaultACP224FeeConfig, AllowFeeRecipients: false, NetworkUpgrades: extras.NetworkUpgrades{ SubnetEVMTimestamp: utils.NewUint64(0), @@ -302,6 +303,12 @@ func TestChainConfigMarshalWithUpgrades(t *testing.T) { result, err := json.Marshal(&config) require.NoError(t, err) expectedJSON := `{ + "acp224FeeConfig": { + "targetGas": 20000000, + "minGasPrice": 1, + "timeToFillCapacity": 5, + "timeToDouble": 60 + }, "chainId": 1, "feeConfig": { "gasLimit": 8000000, diff --git a/params/extras/config.go b/params/extras/config.go index 7bc9156945..889d1d36ff 100644 --- a/params/extras/config.go +++ b/params/extras/config.go @@ -33,6 +33,13 @@ var ( BlockGasCostStep: big.NewInt(200_000), } + DefaultACP224FeeConfig = commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(20_000_000), + MinGasPrice: big.NewInt(1), + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + } + SubnetEVMDefaultChainConfig = &ChainConfig{ FeeConfig: DefaultFeeConfig, NetworkUpgrades: GetNetworkUpgrades(upgrade.GetConfig(constants.MainnetID)), @@ -63,6 +70,7 @@ var ( TestGraniteChainConfig = copyAndSet(TestFortunaChainConfig, func(c *ChainConfig) { c.NetworkUpgrades.GraniteTimestamp = utils.NewUint64(0) + c.ACP224FeeConfig = DefaultACP224FeeConfig }) TestChainConfig = copyConfig(TestGraniteChainConfig) @@ -103,10 +111,11 @@ type ChainConfig struct { AvalancheContext `json:"-"` // Avalanche specific context set during VM initialization. Not serialized. - FeeConfig commontype.FeeConfig `json:"feeConfig"` // Set the configuration for the dynamic fee algorithm - AllowFeeRecipients bool `json:"allowFeeRecipients,omitempty"` // Allows fees to be collected by block builders. - GenesisPrecompiles Precompiles `json:"-"` // Config for enabling precompiles from genesis. JSON encode/decode will be handled by the custom marshaler/unmarshaler. - UpgradeConfig `json:"-"` // Config specified in upgradeBytes (avalanche network upgrades or enable/disabling precompiles). Not serialized. + FeeConfig commontype.FeeConfig `json:"feeConfig"` // Set the configuration for the dynamic fee algorithm + ACP224FeeConfig commontype.ACP224FeeConfig `json:"acp224FeeConfig"` // Set the configuration for the ACP224 fee algorithm + AllowFeeRecipients bool `json:"allowFeeRecipients,omitempty"` // Allows fees to be collected by block builders. + GenesisPrecompiles Precompiles `json:"-"` // Config for enabling precompiles from genesis. JSON encode/decode will be handled by the custom marshaler/unmarshaler. + UpgradeConfig `json:"-"` // Config specified in upgradeBytes (avalanche network upgrades or enable/disabling precompiles). Not serialized. } func (c *ChainConfig) CheckConfigCompatible(newConfig *ethparams.ChainConfig, headNumber *big.Int, headTimestamp uint64) *ethparams.ConfigCompatError { @@ -343,6 +352,12 @@ func (c *ChainConfig) GetFeeConfig() commontype.FeeConfig { return c.FeeConfig } +// GetACP224FeeConfig returns the original ACP224FeeConfig contained in the genesis ChainConfig. +// Implements precompile.ChainConfig interface. +func (c *ChainConfig) GetACP224FeeConfig() commontype.ACP224FeeConfig { + return c.ACP224FeeConfig +} + // AllowedFeeRecipients returns the original AllowedFeeRecipients parameter contained in the genesis ChainConfig. // Implements precompile.ChainConfig interface. func (c *ChainConfig) AllowedFeeRecipients() bool { diff --git a/params/extras/network_upgrades.go b/params/extras/network_upgrades.go index 0187ba1f84..121fc80b23 100644 --- a/params/extras/network_upgrades.go +++ b/params/extras/network_upgrades.go @@ -205,8 +205,12 @@ func GetNetworkUpgrades(agoUpgrade upgrade.Config) NetworkUpgrades { SubnetEVMTimestamp: utils.NewUint64(0), DurangoTimestamp: utils.TimeToNewUint64(agoUpgrade.DurangoTime), EtnaTimestamp: utils.TimeToNewUint64(agoUpgrade.EtnaTime), - FortunaTimestamp: nil, // Fortuna is optional and has no effect on Subnet-EVM - GraniteTimestamp: utils.TimeToNewUint64(agoUpgrade.GraniteTime), + // TODO: Fortuna was initially an optional upgrade upon its release, but it should be required as of the Granite upgrade. + // Chains can still opt to not activate Fortuna, but they will not be able to activate the Granite upgrade. + // Chains can also override this timestamp with a custom upgrade configuration. + // FortunaTimestamp: utils.TimeToNewUint64(agoUpgrade.GraniteTime), + FortunaTimestamp: nil, + GraniteTimestamp: utils.TimeToNewUint64(agoUpgrade.GraniteTime), } } diff --git a/params/hooks_libevm.go b/params/hooks_libevm.go index 7e14598d2a..8f09eafe16 100644 --- a/params/hooks_libevm.go +++ b/params/hooks_libevm.go @@ -79,7 +79,8 @@ func makePrecompile(contract contract.StatefulPrecompiledContract) libevm.Precom panic(err) // Should never happen } var predicateResults predicate.BlockResults - if predicateResultsBytes := customheader.PredicateBytesFromExtra(header.Extra); len(predicateResultsBytes) > 0 { + rules := GetRulesExtra(env.Rules()).AvalancheRules + if predicateResultsBytes := customheader.PredicateBytesFromExtra(rules, header.Extra); len(predicateResultsBytes) > 0 { predicateResults, err = predicate.ParseBlockResults(predicateResultsBytes) if err != nil { panic(err) // Should never happen, as results are already validated in block validation diff --git a/plugin/evm/block.go b/plugin/evm/block.go index 72f2b52400..8212759018 100644 --- a/plugin/evm/block.go +++ b/plugin/evm/block.go @@ -255,7 +255,8 @@ func (b *Block) verifyPredicates(predicateContext *precompileconfig.PredicateCon return fmt.Errorf("failed to marshal predicate results: %w", err) } extraData := b.ethBlock.Extra() - headerPredicateResultsBytes := header.PredicateBytesFromExtra(extraData) + avalancheRules := rulesExtra.AvalancheRules + headerPredicateResultsBytes := header.PredicateBytesFromExtra(avalancheRules, extraData) if !bytes.Equal(headerPredicateResultsBytes, predicateResultsBytes) { return fmt.Errorf("%w (remote: %x local: %x)", errInvalidHeaderPredicateResults, headerPredicateResultsBytes, predicateResultsBytes) } diff --git a/plugin/evm/config/config.go b/plugin/evm/config/config.go index d6c3afb3a4..2dea440959 100644 --- a/plugin/evm/config/config.go +++ b/plugin/evm/config/config.go @@ -9,6 +9,7 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanchego/vms/components/gas" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/spf13/cast" @@ -23,6 +24,11 @@ type ( // Config ... type Config struct { + // GasTarget is the target gas per second that this node will attempt to use + // when creating blocks. If this config is not specified, the node will + // default to use the parent block's target gas per second. + GasTarget *gas.Gas `json:"gas-target,omitempty"` + // Airdrop AirdropFile string `json:"airdrop"` diff --git a/plugin/evm/header/base_fee.go b/plugin/evm/header/base_fee.go index 8fa4198204..2c40d0f133 100644 --- a/plugin/evm/header/base_fee.go +++ b/plugin/evm/header/base_fee.go @@ -5,8 +5,10 @@ package header import ( "errors" + "fmt" "math/big" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/subnet-evm/commontype" @@ -22,10 +24,18 @@ var errEstimateBaseFeeWithoutActivation = errors.New("cannot estimate base fee f func BaseFee( config *extras.ChainConfig, feeConfig commontype.FeeConfig, + acp176Config acp176.Config, parent *types.Header, timestamp uint64, ) (*big.Int, error) { switch { + case config.IsFortuna(timestamp): + state, err := feeStateBeforeBlock(config, acp176Config, parent, timestamp) + if err != nil { + return nil, fmt.Errorf("calculating initial fee state: %w", err) + } + price := state.GasPrice(acp176Config) + return new(big.Int).SetUint64(uint64(price)), nil case config.IsSubnetEVM(timestamp): return baseFeeFromWindow(config, feeConfig, parent, timestamp) default: @@ -45,6 +55,7 @@ func BaseFee( func EstimateNextBaseFee( config *extras.ChainConfig, feeConfig commontype.FeeConfig, + acp176Config acp176.Config, parent *types.Header, timestamp uint64, ) (*big.Int, error) { @@ -53,5 +64,5 @@ func EstimateNextBaseFee( } timestamp = max(timestamp, parent.Time, *config.SubnetEVMTimestamp) - return BaseFee(config, feeConfig, parent, timestamp) + return BaseFee(config, feeConfig, acp176Config, parent, timestamp) } diff --git a/plugin/evm/header/base_fee_test.go b/plugin/evm/header/base_fee_test.go index a47e5fb659..d4ef80b9c7 100644 --- a/plugin/evm/header/base_fee_test.go +++ b/plugin/evm/header/base_fee_test.go @@ -7,6 +7,7 @@ import ( "math/big" "testing" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" @@ -23,14 +24,14 @@ const ( func TestBaseFee(t *testing.T) { t.Run("normal", func(t *testing.T) { - BaseFeeTest(t, testFeeConfig) + BaseFeeTest(t, testFeeConfig, testACP176Config) }) t.Run("double", func(t *testing.T) { - BaseFeeTest(t, testFeeConfigDouble) + BaseFeeTest(t, testFeeConfigDouble, testACP176ConfigDouble) }) } -func BaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) { +func BaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig, acp176Config acp176.Config) { tests := []struct { name string upgrades extras.NetworkUpgrades @@ -179,6 +180,74 @@ func BaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) { timestamp: 2 * subnetevm.WindowLen, want: big.NewInt(feeConfig.MinBaseFee.Int64()), }, + { + name: "fortuna_invalid_timestamp", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + Time: 1, + Extra: (&acp176.State{}).Bytes(), + }, + timestamp: 0, + wantErr: errInvalidTimestamp, + }, + { + name: "fortuna_first_block", + upgrades: extras.NetworkUpgrades{ + FortunaTimestamp: utils.NewUint64(1), + }, + parent: &types.Header{ + Number: big.NewInt(1), + }, + timestamp: 1, + want: big.NewInt(int64(acp176Config.MinGasPrice)), + }, + { + name: "fortuna_genesis_block", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + want: big.NewInt(int64(acp176Config.MinGasPrice)), + }, + { + name: "fortuna_invalid_fee_state", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + Extra: make([]byte, acp176.StateSize-1), + }, + wantErr: acp176.ErrStateInsufficientLength, + }, + // { + // name: "fortuna_current", + // upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + // parent: &types.Header{ + // Number: big.NewInt(1), + // Extra: (&acp176.State{ + // Gas: gas.State{ + // Excess: 2_704_386_192, // 1_500_000 * ln(nAVAX) * [acp176.TargetToPriceUpdateConversion] + // }, + // TargetExcess: 13_605_152, // 2^25 * ln(1.5) + // }).Bytes(), + // }, + // want: big.NewInt(1_000_000_002), // nAVAX + 2 due to rounding + // }, + // { + // name: "fortuna_decrease", + // upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + // parent: &types.Header{ + // Number: big.NewInt(1), + // Extra: (&acp176.State{ + // Gas: gas.State{ + // Excess: 2_704_386_192, // 1_500_000 * ln(nAVAX) * [acp176.TargetToPriceUpdateConversion] + // }, + // TargetExcess: 13_605_152, // 2^25 * ln(1.5) + // }).Bytes(), + // }, + // timestamp: 1, + // want: big.NewInt(988_571_555), // e^((2_704_386_192 - 1_500_000) / 1_500_000 / [acp176.TargetToPriceUpdateConversion]) + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -187,7 +256,7 @@ func BaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - got, err := BaseFee(config, feeConfig, test.parent, test.timestamp) + got, err := BaseFee(config, feeConfig, acp176Config, test.parent, test.timestamp) require.ErrorIs(err, test.wantErr) require.Equal(test.want, got) @@ -199,14 +268,14 @@ func BaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) { func TestEstimateNextBaseFee(t *testing.T) { t.Run("normal", func(t *testing.T) { - EstimateNextBaseFeeTest(t, testFeeConfig) + EstimateNextBaseFeeTest(t, testFeeConfig, testACP176Config) }) t.Run("double", func(t *testing.T) { - EstimateNextBaseFeeTest(t, testFeeConfigDouble) + EstimateNextBaseFeeTest(t, testFeeConfigDouble, testACP176ConfigDouble) }) } -func EstimateNextBaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) { +func EstimateNextBaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig, acp176Config acp176.Config) { testBaseFee := uint64(225 * utils.GWei) nilUpgrade := extras.NetworkUpgrades{} tests := []struct { @@ -253,7 +322,7 @@ func EstimateNextBaseFeeTest(t *testing.T, feeConfig commontype.FeeConfig) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - got, err := EstimateNextBaseFee(config, feeConfig, test.parent, test.timestamp) + got, err := EstimateNextBaseFee(config, feeConfig, acp176Config, test.parent, test.timestamp) require.ErrorIs(err, test.wantErr) require.Equal(test.want, got) }) diff --git a/plugin/evm/header/block_gas_cost_test.go b/plugin/evm/header/block_gas_cost_test.go index 275333312c..fd4a3af7bd 100644 --- a/plugin/evm/header/block_gas_cost_test.go +++ b/plugin/evm/header/block_gas_cost_test.go @@ -7,6 +7,7 @@ import ( "math/big" "testing" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -28,6 +29,17 @@ var ( MinBaseFee: big.NewInt(25 * utils.GWei), GasLimit: big.NewInt(12_000_000), } + testACP224FeeConfig = commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(1_000_000), + MinGasPrice: big.NewInt(1), + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + } + testACP176Config = acp176.Config{ + MinGasPrice: 1, + TimeToFillCapacity: 5, + TimeToDouble: 60, + } testFeeConfigDouble = commontype.FeeConfig{ MinBlockGasCost: big.NewInt(2), @@ -39,6 +51,11 @@ var ( MinBaseFee: big.NewInt(50 * utils.GWei), GasLimit: big.NewInt(24_000_000), } + testACP176ConfigDouble = acp176.Config{ + MinGasPrice: testACP176Config.MinGasPrice * 2, + TimeToFillCapacity: testACP176Config.TimeToFillCapacity * 2, + TimeToDouble: testACP176Config.TimeToDouble * 2, + } ) func TestBlockGasCost(t *testing.T) { diff --git a/plugin/evm/header/dynamic_fee_state.go b/plugin/evm/header/dynamic_fee_state.go new file mode 100644 index 0000000000..8c75cec53c --- /dev/null +++ b/plugin/evm/header/dynamic_fee_state.go @@ -0,0 +1,92 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package header + +import ( + "fmt" + + "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/params/extras" + "github.com/ava-labs/subnet-evm/precompile/contracts/acp224feemanager" +) + +// feeStateBeforeBlock takes the previous header and the timestamp of its child +// block and calculates the fee state before the child block is executed. +func feeStateBeforeBlock( + config *extras.ChainConfig, + acp176Config acp176.Config, + parent *types.Header, + timestamp uint64, +) (acp176.State, error) { + if timestamp < parent.Time { + return acp176.State{}, fmt.Errorf("%w: timestamp %d prior to parent timestamp %d", + errInvalidTimestamp, + timestamp, + parent.Time, + ) + } + + var state acp176.State + if config.IsFortuna(parent.Time) && parent.Number.Cmp(common.Big0) != 0 { + // If the parent block was running with ACP-176, we start with the + // resulting fee state from the parent block. It is assumed that the + // parent has been verified, so the claimed fee state equals the actual + // fee state. + var err error + state, err = acp176.ParseState(parent.Extra) + if err != nil { + return acp176.State{}, fmt.Errorf("parsing parent fee state: %w", err) + } + } + + state.AdvanceTime(timestamp-parent.Time, acp176Config) + return state, nil +} + +// feeStateAfterBlock takes the previous header and returns the fee state after +// the execution of the provided child. +func feeStateAfterBlock( + config *extras.ChainConfig, + acp224FeeConfig commontype.ACP224FeeConfig, + parent *types.Header, + header *types.Header, + desiredTargetExcess *gas.Gas, +) (acp176.State, error) { + acp176Config, err := acp224FeeConfig.ToACP176Config() + if err != nil { + return acp176.State{}, fmt.Errorf("converting ACP224 fee config to ACP176 config: %w", err) + } + + // Calculate the gas state after the parent block + state, err := feeStateBeforeBlock(config, acp176Config, parent, header.Time) + if err != nil { + return acp176.State{}, fmt.Errorf("calculating initial fee state: %w", err) + } + + // Consume the gas used by the block + // There is never any extra gas used in subnet-evm because there are no atomic transactions. + if err := state.ConsumeGas(header.GasUsed, common.Big0); err != nil { + return acp176.State{}, fmt.Errorf("advancing the fee state: %w", err) + } + + // If the ACP224 fee manager precompile is activated, override the target excess with the + // latest value set in the precompile state. + // Otherwise, if the desired target excess is specified, move the target excess as much + // as possible toward that desired value. + if config.IsPrecompileEnabled(acp224feemanager.ContractAddress, header.Time) { + if acp224FeeConfig.TargetGas == nil || acp224FeeConfig.TargetGas.Cmp(common.Big0) == 0 || !acp224FeeConfig.TargetGas.IsUint64() { + return acp176.State{}, fmt.Errorf("invalid target gas: %s", acp224FeeConfig.TargetGas.String()) + } + newTargetExcess := acp176.DesiredTargetExcess(gas.Gas(acp224FeeConfig.TargetGas.Uint64())) + state.UpdateTargetExcessUnbounded(newTargetExcess, acp176Config) + } else if desiredTargetExcess != nil { + state.UpdateTargetExcess(*desiredTargetExcess, acp176Config) + } + return state, nil +} diff --git a/plugin/evm/header/extra.go b/plugin/evm/header/extra.go index 1435158201..a5a847c3e7 100644 --- a/plugin/evm/header/extra.go +++ b/plugin/evm/header/extra.go @@ -8,8 +8,11 @@ import ( "errors" "fmt" + "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/params/extras" "github.com/ava-labs/subnet-evm/plugin/evm/upgrade/subnetevm" ) @@ -20,17 +23,35 @@ const ( var ( errInvalidExtraPrefix = errors.New("invalid header.Extra prefix") + errIncorrectFeeState = errors.New("incorrect fee state") errInvalidExtraLength = errors.New("invalid header.Extra length") ) -// ExtraPrefix takes the previous header and the timestamp of its child -// block and calculates the expected extra prefix for the child block. +// ExtraPrefix returns what the prefix of the header's Extra field should be +// based on the desired target excess. +// +// If the `desiredTargetExcess` is nil, the parent's target excess is used. func ExtraPrefix( config *extras.ChainConfig, + acp224FeeConfig commontype.ACP224FeeConfig, parent *types.Header, header *types.Header, + desiredTargetExcess *gas.Gas, ) ([]byte, error) { switch { + case config.IsFortuna(header.Time): + state, err := feeStateAfterBlock( + config, + acp224FeeConfig, + parent, + header, + desiredTargetExcess, + ) + if err != nil { + return nil, fmt.Errorf("calculating fee state: %w", err) + } + + return state.Bytes(), nil case config.IsSubnetEVM(header.Time): window, err := feeWindow(config, parent, header.Time) if err != nil { @@ -47,10 +68,40 @@ func ExtraPrefix( // formatted. func VerifyExtraPrefix( config *extras.ChainConfig, + acp224FeeConfig commontype.ACP224FeeConfig, parent *types.Header, header *types.Header, ) error { switch { + case config.IsFortuna(header.Time): + remoteState, err := acp176.ParseState(header.Extra) + if err != nil { + return fmt.Errorf("parsing remote fee state: %w", err) + } + + // By passing in the claimed target excess, we ensure that the expected + // target excess is equal to the claimed target excess if it is possible + // to have correctly set it to that value. Otherwise, the resulting + // value will be as close to the claimed value as possible, but would + // not be equal. + expectedState, err := feeStateAfterBlock( + config, + acp224FeeConfig, + parent, + header, + &remoteState.TargetExcess, + ) + if err != nil { + return fmt.Errorf("calculating expected fee state: %w", err) + } + + if remoteState != expectedState { + return fmt.Errorf("%w: expected %+v, found %+v", + errIncorrectFeeState, + expectedState, + remoteState, + ) + } case config.IsSubnetEVM(header.Time): feeWindow, err := feeWindow(config, parent, header.Time) if err != nil { @@ -75,6 +126,15 @@ func VerifyExtraPrefix( func VerifyExtra(rules extras.AvalancheRules, extra []byte) error { extraLen := len(extra) switch { + case rules.IsFortuna: + if extraLen < acp176.StateSize { + return fmt.Errorf( + "%w: expected >= %d but got %d", + errInvalidExtraLength, + acp176.StateSize, + extraLen, + ) + } case rules.IsDurango: if extraLen < subnetevm.WindowSize { return fmt.Errorf( @@ -108,8 +168,11 @@ func VerifyExtra(rules extras.AvalancheRules, extra []byte) error { // PredicateBytesFromExtra returns the predicate result bytes from the header's // extra data. If the extra data is not long enough, an empty slice is returned. -func PredicateBytesFromExtra(extra []byte) []byte { +func PredicateBytesFromExtra(rules extras.AvalancheRules, extra []byte) []byte { offset := subnetevm.WindowSize + if rules.IsFortuna { + offset = acp176.StateSize + } // Prior to Durango, the VM enforces the extra data is smaller than or equal // to `offset`. // After Durango, the VM pre-verifies the extra data past `offset` is valid. diff --git a/plugin/evm/header/extra_test.go b/plugin/evm/header/extra_test.go index 807ae5d40e..6394f9815a 100644 --- a/plugin/evm/header/extra_test.go +++ b/plugin/evm/header/extra_test.go @@ -7,6 +7,8 @@ import ( "math/big" "testing" + "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" @@ -23,12 +25,13 @@ const ( func TestExtraPrefix(t *testing.T) { tests := []struct { - name string - upgrades extras.NetworkUpgrades - parent *types.Header - header *types.Header - want []byte - wantErr error + name string + upgrades extras.NetworkUpgrades + parent *types.Header + header *types.Header + desiredTargetExcess *gas.Gas + want []byte + wantErr error }{ { name: "pre_subnet_evm", @@ -108,6 +111,91 @@ func TestExtraPrefix(t *testing.T) { return window.Bytes() }(), }, + { + name: "fortuna_first_block", + upgrades: extras.NetworkUpgrades{ + FortunaTimestamp: utils.NewUint64(1), + }, + parent: &types.Header{ + Number: big.NewInt(1), + }, + header: &types.Header{ + Time: 1, + GasUsed: 1, + }, + want: (&acp176.State{ + Gas: gas.State{ + Capacity: acp176.MinMaxPerSecond - 1, + Excess: 1, + }, + TargetExcess: 0, + }).Bytes(), + }, + { + name: "fortuna_genesis_block", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + Time: 1, + GasUsed: 2, + }, + desiredTargetExcess: (*gas.Gas)(utils.NewUint64(3)), + want: (&acp176.State{ + Gas: gas.State{ + Capacity: acp176.MinMaxPerSecond - 2, + Excess: 2, + }, + TargetExcess: 3, + }).Bytes(), + }, + { + name: "fortuna_invalid_fee_state", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + }, + header: &types.Header{}, + wantErr: acp176.ErrStateInsufficientLength, + }, + { + name: "fortuna_invalid_gas_used", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + Extra: (&acp176.State{}).Bytes(), + }, + header: &types.Header{ + GasUsed: 1, + }, + wantErr: gas.ErrInsufficientCapacity, + }, + { + name: "fortuna_reduce_capacity", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + Extra: (&acp176.State{ + Gas: gas.State{ + Capacity: 10_019_550, // [acp176.MinMaxCapacity] * e^(2*[acp176.MaxTargetExcessDiff] / [acp176.TargetConversion]) + Excess: 2_000_000_000 - 3, + }, + TargetExcess: 2 * acp176.MaxTargetExcessDiff, + }).Bytes(), + }, + header: &types.Header{ + GasUsed: 2, + }, + desiredTargetExcess: (*gas.Gas)(utils.NewUint64(0)), + want: (&acp176.State{ + Gas: gas.State{ + Capacity: 10_009_770, // [acp176.MinTargetPerSecond] * e^([acp176.MaxTargetExcessDiff] / [acp176.TargetConversion]) + Excess: 1_998_047_815, // 2M * NewTarget / OldTarget + }, + TargetExcess: acp176.MaxTargetExcessDiff, + }).Bytes(), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -116,7 +204,7 @@ func TestExtraPrefix(t *testing.T) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - got, err := ExtraPrefix(config, test.parent, test.header) + got, err := ExtraPrefix(config, testACP224FeeConfig, test.parent, test.header, test.desiredTargetExcess) require.ErrorIs(err, test.wantErr) require.Equal(test.want, got) }) @@ -166,13 +254,69 @@ func TestVerifyExtraPrefix(t *testing.T) { }, wantErr: nil, }, + { + name: "fortuna_invalid_header", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + header: &types.Header{}, + wantErr: acp176.ErrStateInsufficientLength, + }, + { + name: "fortuna_invalid_gas_consumed", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + GasUsed: 1, + Extra: (&acp176.State{}).Bytes(), + }, + wantErr: gas.ErrInsufficientCapacity, + }, + { + name: "fortuna_wrong_fee_state", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + Time: 1, + GasUsed: 1, + Extra: (&acp176.State{ + Gas: gas.State{ + Capacity: acp176.MinMaxPerSecond - 1, + Excess: 1, + }, + TargetExcess: acp176.MaxTargetExcessDiff + 1, // Too much of a diff + }).Bytes(), + }, + wantErr: errIncorrectFeeState, + }, + { + name: "fortuna_valid", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + Time: 1, + GasUsed: 1, + Extra: (&acp176.State{ + Gas: gas.State{ + Capacity: acp176.MinMaxPerSecond - 1, + Excess: 1, + }, + TargetExcess: acp176.MaxTargetExcessDiff, + }).Bytes(), + }, + wantErr: nil, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - err := VerifyExtraPrefix(config, test.parent, test.header) + err := VerifyExtraPrefix(config, testACP224FeeConfig, test.parent, test.header) require.ErrorIs(t, err, test.wantErr) }) } @@ -245,6 +389,30 @@ func TestVerifyExtra(t *testing.T) { extra: make([]byte, subnetevm.WindowSize-1), expected: errInvalidExtraLength, }, + { + name: "fortuna_valid_min", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: make([]byte, acp176.StateSize), + expected: nil, + }, + { + name: "fortuna_valid_extra", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: make([]byte, acp176.StateSize+1), + expected: nil, + }, + { + name: "fortuna_invalid", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: make([]byte, acp176.StateSize-1), + expected: errInvalidExtraLength, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -258,6 +426,7 @@ func TestPredicateBytesFromExtra(t *testing.T) { tests := []struct { name string extra []byte + rules extras.AvalancheRules expected []byte }{ { @@ -282,10 +451,44 @@ func TestPredicateBytesFromExtra(t *testing.T) { }, expected: []byte{5}, }, + { + name: "fortuna_empty_extra", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: nil, + expected: nil, + }, + { + name: "fortuna_too_short", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: make([]byte, acp176.StateSize-1), + expected: nil, + }, + { + name: "fortuna_empty_predicate", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: make([]byte, acp176.StateSize), + expected: nil, + }, + { + name: "fortuna_non_empty_predicate", + rules: extras.AvalancheRules{ + IsFortuna: true, + }, + extra: []byte{ + acp176.StateSize: 5, + }, + expected: []byte{5}, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got := PredicateBytesFromExtra(test.extra) + got := PredicateBytesFromExtra(test.rules, test.extra) require.Equal(t, test.expected, got) }) } @@ -335,6 +538,7 @@ func TestPredicateBytesExtra(t *testing.T) { name string extra []byte predicate []byte + rules extras.AvalancheRules wantExtraWithPredicate []byte wantPredicateBytes []byte }{ @@ -363,7 +567,7 @@ func TestPredicateBytesExtra(t *testing.T) { t.Run(test.name, func(t *testing.T) { gotExtra := SetPredicateBytesInExtra(test.extra, test.predicate) require.Equal(t, test.wantExtraWithPredicate, gotExtra) - gotPredicateBytes := PredicateBytesFromExtra(gotExtra) + gotPredicateBytes := PredicateBytesFromExtra(test.rules, gotExtra) require.Equal(t, test.wantPredicateBytes, gotPredicateBytes) }) } diff --git a/plugin/evm/header/gas_limit.go b/plugin/evm/header/gas_limit.go index 3ff90e991c..a934de68b2 100644 --- a/plugin/evm/header/gas_limit.go +++ b/plugin/evm/header/gas_limit.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/subnet-evm/commontype" @@ -21,17 +22,26 @@ var ( errInvalidGasLimit = errors.New("invalid gas limit") ) -type CalculateGasLimitFunc func(parentGasUsed, parentGasLimit, gasFloor, gasCeil uint64) uint64 - // GasLimit takes the previous header and the timestamp of its child block and // calculates the gas limit for the child block. func GasLimit( config *extras.ChainConfig, feeConfig commontype.FeeConfig, + acp176Config acp176.Config, parent *types.Header, timestamp uint64, ) (uint64, error) { switch { + case config.IsFortuna(timestamp): + state, err := feeStateBeforeBlock(config, acp176Config, parent, timestamp) + if err != nil { + return 0, fmt.Errorf("calculating initial fee state: %w", err) + } + // The gas limit is set to the maximum capacity, rather than the current + // capacity, to minimize the differences with upstream geth. During + // block building and gas usage calculations, the gas limit is checked + // against the current capacity. + return uint64(state.MaxCapacity(acp176Config)), nil case config.IsSubnetEVM(timestamp): return feeConfig.GasLimit.Uint64(), nil default: @@ -48,11 +58,12 @@ func GasLimit( func VerifyGasUsed( config *extras.ChainConfig, feeConfig commontype.FeeConfig, + acp176Config acp176.Config, parent *types.Header, header *types.Header, ) error { gasUsed := header.GasUsed - capacity, err := GasCapacity(config, feeConfig, parent, header.Time) + capacity, err := GasCapacity(config, feeConfig, acp176Config, parent, header.Time) if err != nil { return fmt.Errorf("calculating gas capacity: %w", err) } @@ -70,10 +81,24 @@ func VerifyGasUsed( func VerifyGasLimit( config *extras.ChainConfig, feeConfig commontype.FeeConfig, + acp176Config acp176.Config, parent *types.Header, header *types.Header, ) error { switch { + case config.IsFortuna(header.Time): + state, err := feeStateBeforeBlock(config, acp176Config, parent, header.Time) + if err != nil { + return fmt.Errorf("calculating initial fee state: %w", err) + } + maxCapacity := state.MaxCapacity(acp176Config) + if header.GasLimit != uint64(maxCapacity) { + return fmt.Errorf("%w: have %d, want %d", + errInvalidGasLimit, + header.GasLimit, + maxCapacity, + ) + } case config.IsSubnetEVM(header.Time): expectedGasLimit := feeConfig.GasLimit.Uint64() if header.GasLimit != expectedGasLimit { @@ -113,8 +138,18 @@ func VerifyGasLimit( func GasCapacity( config *extras.ChainConfig, feeConfig commontype.FeeConfig, + acp176Config acp176.Config, parent *types.Header, timestamp uint64, ) (uint64, error) { - return GasLimit(config, feeConfig, parent, timestamp) + // Prior to the F upgrade, the gas capacity is equal to the gas limit. + if !config.IsFortuna(timestamp) { + return GasLimit(config, feeConfig, acp176Config, parent, timestamp) + } + + state, err := feeStateBeforeBlock(config, acp176Config, parent, timestamp) + if err != nil { + return 0, fmt.Errorf("calculating initial fee state: %w", err) + } + return uint64(state.Gas.Capacity), nil } diff --git a/plugin/evm/header/gas_limit_test.go b/plugin/evm/header/gas_limit_test.go index 0c10c27db2..f39dc44a65 100644 --- a/plugin/evm/header/gas_limit_test.go +++ b/plugin/evm/header/gas_limit_test.go @@ -4,8 +4,10 @@ package header import ( + "math/big" "testing" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/libevm/core/types" "github.com/stretchr/testify/require" @@ -17,14 +19,14 @@ import ( func TestGasLimit(t *testing.T) { t.Run("normal", func(t *testing.T) { - GasLimitTest(t, testFeeConfig) + GasLimitTest(t, testFeeConfig, testACP176Config) }) t.Run("double", func(t *testing.T) { - GasLimitTest(t, testFeeConfigDouble) + GasLimitTest(t, testFeeConfigDouble, testACP176ConfigDouble) }) } -func GasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) { +func GasLimitTest(t *testing.T, feeConfig commontype.FeeConfig, acp176Config acp176.Config) { tests := []struct { name string upgrades extras.NetworkUpgrades @@ -38,6 +40,22 @@ func GasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) { upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades, want: feeConfig.GasLimit.Uint64(), }, + { + name: "fortuna_invalid_parent_header", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + }, + wantErr: acp176.ErrStateInsufficientLength, + }, + { + name: "fortuna_initial_max_capacity", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + want: uint64(acp176Config.MinMaxCapacity()), + }, { name: "pre_subnet_evm", upgrades: extras.TestPreSubnetEVMChainConfig.NetworkUpgrades, @@ -54,23 +72,82 @@ func GasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - got, err := GasLimit(config, feeConfig, test.parent, test.timestamp) + got, err := GasLimit(config, feeConfig, acp176Config, test.parent, test.timestamp) require.ErrorIs(err, test.wantErr) require.Equal(test.want, got) }) } } +func TestVerifyGasUsed(t *testing.T) { + tests := []struct { + name string + feeConfig commontype.FeeConfig + acp176Config acp176.Config + upgrades extras.NetworkUpgrades + parent *types.Header + header *types.Header + want error + }{ + { + name: "fortuna_invalid_capacity", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + }, + header: &types.Header{}, + want: acp176.ErrStateInsufficientLength, + }, + { + name: "fortuna_invalid_usage", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + Time: 1, + // The maximum allowed gas used is: + // (header.Time - parent.Time) * [acp176.MinMaxPerSecond] + // which is equal to [acp176.MinMaxPerSecond]. + GasUsed: acp176.MinMaxPerSecond + 1, + }, + want: errInvalidGasUsed, + }, + { + name: "fortuna_max_consumption", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + Time: 1, + GasUsed: acp176.MinMaxPerSecond, + }, + acp176Config: testACP176Config, + want: nil, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + config := &extras.ChainConfig{ + NetworkUpgrades: test.upgrades, + } + err := VerifyGasUsed(config, test.feeConfig, test.acp176Config, test.parent, test.header) + require.ErrorIs(t, err, test.want) + }) + } +} + func TestVerifyGasLimit(t *testing.T) { t.Run("normal", func(t *testing.T) { - VerifyGasLimitTest(t, testFeeConfig) + VerifyGasLimitTest(t, testFeeConfig, testACP176Config) }) t.Run("double", func(t *testing.T) { - VerifyGasLimitTest(t, testFeeConfigDouble) + VerifyGasLimitTest(t, testFeeConfigDouble, testACP176ConfigDouble) }) } -func VerifyGasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) { +func VerifyGasLimitTest(t *testing.T, feeConfig commontype.FeeConfig, acp176Config acp176.Config) { tests := []struct { name string upgrades extras.NetworkUpgrades @@ -78,6 +155,36 @@ func VerifyGasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) { header *types.Header want error }{ + { + name: "fortuna_invalid_header", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + }, + header: &types.Header{}, + want: acp176.ErrStateInsufficientLength, + }, + { + name: "fortuna_invalid", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + GasLimit: uint64(acp176Config.MinMaxCapacity()) + 1, + }, + want: errInvalidGasLimit, + }, + { + name: "fortuna_valid", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + header: &types.Header{ + GasLimit: uint64(acp176Config.MinMaxCapacity()), + }, + }, { name: "subnet_evm_valid", upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades, @@ -142,34 +249,46 @@ func VerifyGasLimitTest(t *testing.T, feeConfig commontype.FeeConfig) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - err := VerifyGasLimit(config, feeConfig, test.parent, test.header) + err := VerifyGasLimit(config, feeConfig, acp176Config, test.parent, test.header) require.ErrorIs(t, err, test.want) }) } } func TestGasCapacity(t *testing.T) { - t.Run("normal", func(t *testing.T) { - GasCapacityTest(t, testFeeConfig) - }) - t.Run("double", func(t *testing.T) { - GasCapacityTest(t, testFeeConfigDouble) - }) -} - -func GasCapacityTest(t *testing.T, feeConfig commontype.FeeConfig) { tests := []struct { - name string - upgrades extras.NetworkUpgrades - parent *types.Header - timestamp uint64 - want uint64 - wantErr error + name string + feeConfig commontype.FeeConfig + acp176Config acp176.Config + upgrades extras.NetworkUpgrades + parent *types.Header + timestamp uint64 + want uint64 + wantErr error }{ { - name: "subnet_evm", - upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades, - want: feeConfig.GasLimit.Uint64(), + name: "subnet_evm", + upgrades: extras.TestSubnetEVMChainConfig.NetworkUpgrades, + feeConfig: testFeeConfig, + want: testFeeConfig.GasLimit.Uint64(), + }, + { + name: "fortuna_invalid_header", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(1), + }, + wantErr: acp176.ErrStateInsufficientLength, + }, + { + name: "fortuna_after_1s", + upgrades: extras.TestFortunaChainConfig.NetworkUpgrades, + parent: &types.Header{ + Number: big.NewInt(0), + }, + timestamp: 1, + acp176Config: testACP176Config, + want: acp176.MinMaxPerSecond, }, } for _, test := range tests { @@ -179,7 +298,7 @@ func GasCapacityTest(t *testing.T, feeConfig commontype.FeeConfig) { config := &extras.ChainConfig{ NetworkUpgrades: test.upgrades, } - got, err := GasCapacity(config, feeConfig, test.parent, test.timestamp) + got, err := GasCapacity(config, test.feeConfig, test.acp176Config, test.parent, test.timestamp) require.ErrorIs(err, test.wantErr) require.Equal(test.want, got) }) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 0bae4947ed..d7eb8a1563 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -33,6 +33,8 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/chain" + "github.com/ava-labs/avalanchego/vms/components/gas" + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" "github.com/ava-labs/firewood-go-ethhash/ffi" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" @@ -601,6 +603,15 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig. if err != nil { return err } + + // If the gas target is specified, calculate the desired target excess and + // use it during block creation. + var desiredTargetExcess *gas.Gas + if vm.config.GasTarget != nil { + desiredTargetExcess = new(gas.Gas) + *desiredTargetExcess = acp176.DesiredTargetExcess(*vm.config.GasTarget) + } + vm.eth, err = eth.New( node, &vm.ethConfig, @@ -608,7 +619,11 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig. vm.chaindb, eth.Settings{MaxBlocksPerRequest: vm.config.MaxBlocksPerRequest}, lastAcceptedHash, - dummy.NewFakerWithClock(&vm.clock), + dummy.NewDummyEngine( + dummy.Mode{}, + &vm.clock, + desiredTargetExcess, + ), &vm.clock, ) if err != nil { @@ -619,11 +634,19 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig. vm.blockChain = vm.eth.BlockChain() vm.miner = vm.eth.Miner() lastAccepted := vm.blockChain.LastAcceptedBlock() - feeConfig, _, err := vm.blockChain.GetFeeConfigAt(lastAccepted.Header()) - if err != nil { - return err + if vm.chainConfigExtra().IsFortuna(lastAccepted.Header().Time) { + acp224FeeConfig, _, err := vm.blockChain.GetACP224FeeConfigAt(lastAccepted.Header()) + if err != nil { + return err + } + vm.txPool.SetMinFee(acp224FeeConfig.MinGasPrice) + } else { + feeConfig, _, err := vm.blockChain.GetFeeConfigAt(lastAccepted.Header()) + if err != nil { + return err + } + vm.txPool.SetMinFee(feeConfig.MinBaseFee) } - vm.txPool.SetMinFee(feeConfig.MinBaseFee) vm.txPool.SetGasTip(big.NewInt(0)) vm.eth.Start() diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go index 4f6e5c661a..18ee22c711 100644 --- a/plugin/evm/vm_warp_test.go +++ b/plugin/evm/vm_warp_test.go @@ -746,7 +746,8 @@ func testReceiveWarpMessage( // Require the block was built with a successful predicate result ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock - headerPredicateResultsBytes := customheader.PredicateBytesFromExtra(ethBlock.Extra()) + rules := params.GetExtra(vm.chainConfig).GetAvalancheRules(ethBlock.Time()) + headerPredicateResultsBytes := customheader.PredicateBytesFromExtra(rules, ethBlock.Extra()) blockResults, err := predicate.ParseBlockResults(headerPredicateResultsBytes) require.NoError(err) diff --git a/precompile/contracts/acp224feemanager/acp224feemanagertest/config_test.go b/precompile/contracts/acp224feemanager/acp224feemanagertest/config_test.go new file mode 100644 index 0000000000..4a2f42975a --- /dev/null +++ b/precompile/contracts/acp224feemanager/acp224feemanagertest/config_test.go @@ -0,0 +1,172 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package acp224feemanagertest + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ava-labs/avalanchego/vms/evm/upgrade/acp176" + "github.com/ava-labs/libevm/common" + "go.uber.org/mock/gomock" + + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/precompile/allowlist/allowlisttest" + "github.com/ava-labs/subnet-evm/precompile/contracts/acp224feemanager" + "github.com/ava-labs/subnet-evm/precompile/precompileconfig" + "github.com/ava-labs/subnet-evm/precompile/precompiletest" + "github.com/ava-labs/subnet-evm/utils" +) + +// TestVerify tests the verification of Config. +func TestVerify(t *testing.T) { + admins := []common.Address{allowlisttest.TestAdminAddr} + enableds := []common.Address{allowlisttest.TestEnabledAddr} + managers := []common.Address{allowlisttest.TestManagerAddr} + tests := map[string]precompiletest.ConfigVerifyTest{ + "valid config": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ValidTestACP224FeeConfig), + ChainConfig: func() precompileconfig.ChainConfig { + config := precompileconfig.NewMockChainConfig(gomock.NewController(t)) + config.EXPECT().IsDurango(gomock.Any()).Return(true).AnyTimes() + return config + }(), + ExpectedError: "", + }, + "invalid - nil TargetGas": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: nil, + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + }), + ExpectedError: "targetGas cannot be nil", + }, + "invalid - nil MinGasPrice": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: nil, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + }), + ExpectedError: "minGasPrice cannot be nil", + }, + "invalid - nil TimeToFillCapacity": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: nil, + TimeToDouble: big.NewInt(60), + }), + ExpectedError: "timeToFillCapacity cannot be nil", + }, + "invalid - nil TimeToDouble": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: nil, + }), + ExpectedError: "timeToDouble cannot be nil", + }, + "invalid - TargetGas <= 0": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(0), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + }), + ExpectedError: "targetGas = 0 cannot be less than or equal to 0", + }, + "invalid - MinGasPrice <= 0": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: big.NewInt(0), + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + }), + ExpectedError: "minGasPrice = 0 cannot be less than or equal to 0", + }, + "invalid - TimeToFillCapacity < 0": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(-1), + TimeToDouble: big.NewInt(60), + }), + ExpectedError: "timeToFillCapacity = -1 cannot be less than 0", + }, + "invalid - TimeToDouble < 0": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(-1), + }), + ExpectedError: "timeToDouble = -1 cannot be less than 0", + }, + "invalid - TimeToFillCapacity > MaxTimeToFillCapacity": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(acp176.MaxTimeToFillCapacity + 1), + TimeToDouble: big.NewInt(60), + }), + ExpectedError: fmt.Sprintf("timeToFillCapacity = %d cannot be greater than %d", big.NewInt(acp176.MaxTimeToFillCapacity+1), acp176.MaxTimeToFillCapacity), + }, + "invalid - TimeToDouble > MaxTimeToDouble": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(acp176.MaxTimeToDouble + 1), + }), + ExpectedError: fmt.Sprintf("timeToDouble = %d cannot be greater than %d", big.NewInt(acp176.MaxTimeToDouble+1), acp176.MaxTimeToDouble), + }, + } + // Verify the precompile with the allowlist. + // This adds allowlist verify tests to your custom tests + // and runs them all together. + // Even if you don't add any custom tests, keep this. This will still + // run the default allowlist verify tests. + allowlisttest.VerifyPrecompileWithAllowListTests(t, acp224feemanager.Module, tests) +} + +// TestEqual tests the equality of Config with other precompile configs. +func TestEqual(t *testing.T) { + admins := []common.Address{allowlisttest.TestAdminAddr} + enableds := []common.Address{allowlisttest.TestEnabledAddr} + managers := []common.Address{allowlisttest.TestManagerAddr} + tests := map[string]precompiletest.ConfigEqualTest{ + "non-nil config and nil other": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{}), + Other: nil, + Expected: false, + }, + "different type": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{}), + Other: precompileconfig.NewMockConfig(gomock.NewController(t)), + Expected: false, + }, + "different timestamp": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{}), + Other: acp224feemanager.NewConfig(utils.NewUint64(4), admins, enableds, managers, &commontype.ACP224FeeConfig{}), + Expected: false, + }, + "same config": { + Config: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{}), + Other: acp224feemanager.NewConfig(utils.NewUint64(3), admins, enableds, managers, &commontype.ACP224FeeConfig{}), + Expected: true, + }, + // CUSTOM CODE STARTS HERE + // Add your own Equal tests here + } + // Run allow list equal tests. + // This adds allowlist equal tests to your custom tests + // and runs them all together. + // Even if you don't add any custom tests, keep this. This will still + // run the default allowlist equal tests. + allowlisttest.EqualPrecompileWithAllowListTests(t, acp224feemanager.Module, tests) +} diff --git a/precompile/contracts/acp224feemanager/acp224feemanagertest/contract_test.go b/precompile/contracts/acp224feemanager/acp224feemanagertest/contract_test.go new file mode 100644 index 0000000000..f9049ff871 --- /dev/null +++ b/precompile/contracts/acp224feemanager/acp224feemanagertest/contract_test.go @@ -0,0 +1,406 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package acp224feemanagertest + +import ( + "math/big" + "testing" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/vm" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/core/extstate" + "github.com/ava-labs/subnet-evm/precompile/allowlist/allowlisttest" + "github.com/ava-labs/subnet-evm/precompile/contract" + "github.com/ava-labs/subnet-evm/precompile/contracts/acp224feemanager" + "github.com/ava-labs/subnet-evm/precompile/precompiletest" +) + +var ( + _ = vm.ErrOutOfGas + _ = big.NewInt + _ = common.Big0 + _ = require.New +) + +// These tests are run against the precompile contract directly with +// the given input and expected output. They're just a guide to +// help you write your own tests. These tests are for general cases like +// allowlist, readOnly behaviour, and gas cost. You should write your own +// tests for specific cases. +var ( + testBlockNumber = big.NewInt(7) + testDefaultUpdatedFeeConfig = commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + } + tests = map[string]precompiletest.PrecompileTest{ + "calling getFeeConfig from NoRole should succeed": { + Caller: allowlisttest.TestNoRoleAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(big.NewInt(6)).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfig() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigOutput(testDefaultUpdatedFeeConfig) + if err != nil { + panic(err) + } + return packedOutput + }(), + SuppliedGas: acp224feemanager.GetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling getFeeConfig from Enabled should succeed": { + Caller: allowlisttest.TestEnabledAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(big.NewInt(6)).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfig() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigOutput(testDefaultUpdatedFeeConfig) + if err != nil { + panic(err) + } + return packedOutput + }(), + SuppliedGas: acp224feemanager.GetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling getFeeConfig from Manager should succeed": { + Caller: allowlisttest.TestManagerAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(big.NewInt(6)).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfig() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigOutput(testDefaultUpdatedFeeConfig) + if err != nil { + panic(err) + } + return packedOutput + }(), + SuppliedGas: acp224feemanager.GetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling getFeeConfig from Admin should succeed": { + Caller: allowlisttest.TestAdminAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(big.NewInt(6)).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfig() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigOutput(testDefaultUpdatedFeeConfig) + if err != nil { + panic(err) + } + return packedOutput + }(), + SuppliedGas: acp224feemanager.GetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "insufficient gas for getFeeConfig should fail": { + Caller: common.Address{1}, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfig() + require.NoError(t, err) + return input + }, + SuppliedGas: acp224feemanager.GetFeeConfigGasCost - 1, + ReadOnly: false, + ExpectedErr: vm.ErrOutOfGas.Error(), + }, + "calling getFeeConfigLastChangedAt from NoRole should succeed": { + Caller: allowlisttest.TestNoRoleAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(testBlockNumber).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfigLastChangedAt() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigLastChangedAtOutput(testBlockNumber) + if err != nil { + panic(err) + } + return packedOutput + }(), + AfterHook: func(t testing.TB, state *extstate.StateDB) { + feeConfig := acp224feemanager.GetStoredFeeConfig(state, acp224feemanager.ContractAddress) + require.Equal(t, testDefaultUpdatedFeeConfig, feeConfig) + lastChangedAt := acp224feemanager.GetFeeConfigLastChangedAt(state, acp224feemanager.ContractAddress) + require.Equal(t, testBlockNumber, lastChangedAt) + }, + SuppliedGas: acp224feemanager.GetFeeConfigLastChangedAtGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling getFeeConfigLastChangedAt from Enabled should succeed": { + Caller: allowlisttest.TestEnabledAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(testBlockNumber).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfigLastChangedAt() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigLastChangedAtOutput(testBlockNumber) + if err != nil { + panic(err) + } + return packedOutput + }(), + AfterHook: func(t testing.TB, state *extstate.StateDB) { + feeConfig := acp224feemanager.GetStoredFeeConfig(state, acp224feemanager.ContractAddress) + require.Equal(t, testDefaultUpdatedFeeConfig, feeConfig) + lastChangedAt := acp224feemanager.GetFeeConfigLastChangedAt(state, acp224feemanager.ContractAddress) + require.Equal(t, testBlockNumber, lastChangedAt) + }, + SuppliedGas: acp224feemanager.GetFeeConfigLastChangedAtGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling getFeeConfigLastChangedAt from Manager should succeed": { + Caller: allowlisttest.TestManagerAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(testBlockNumber).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfigLastChangedAt() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigLastChangedAtOutput(testBlockNumber) + if err != nil { + panic(err) + } + return packedOutput + }(), + AfterHook: func(t testing.TB, state *extstate.StateDB) { + feeConfig := acp224feemanager.GetStoredFeeConfig(state, acp224feemanager.ContractAddress) + require.Equal(t, testDefaultUpdatedFeeConfig, feeConfig) + lastChangedAt := acp224feemanager.GetFeeConfigLastChangedAt(state, acp224feemanager.ContractAddress) + require.Equal(t, testBlockNumber, lastChangedAt) + }, + SuppliedGas: acp224feemanager.GetFeeConfigLastChangedAtGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling getFeeConfigLastChangedAt from Admin should succeed": { + Caller: allowlisttest.TestAdminAddr, + BeforeHook: func(t testing.TB, state *extstate.StateDB) { + blockContext := contract.NewMockBlockContext(gomock.NewController(t)) + blockContext.EXPECT().Number().Return(testBlockNumber).Times(1) + allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address)(t, state) + require.NoError(t, acp224feemanager.StoreFeeConfig(state, acp224feemanager.ContractAddress, testDefaultUpdatedFeeConfig, blockContext)) + }, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfigLastChangedAt() + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + packedOutput, err := acp224feemanager.PackGetFeeConfigLastChangedAtOutput(testBlockNumber) + if err != nil { + panic(err) + } + return packedOutput + }(), + AfterHook: func(t testing.TB, state *extstate.StateDB) { + feeConfig := acp224feemanager.GetStoredFeeConfig(state, acp224feemanager.ContractAddress) + require.Equal(t, testDefaultUpdatedFeeConfig, feeConfig) + lastChangedAt := acp224feemanager.GetFeeConfigLastChangedAt(state, acp224feemanager.ContractAddress) + require.Equal(t, testBlockNumber, lastChangedAt) + }, + SuppliedGas: acp224feemanager.GetFeeConfigLastChangedAtGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "insufficient gas for getFeeConfigLastChangedAt should fail": { + Caller: common.Address{1}, + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackGetFeeConfigLastChangedAt() + require.NoError(t, err) + return input + }, + SuppliedGas: acp224feemanager.GetFeeConfigLastChangedAtGasCost - 1, + ReadOnly: false, + ExpectedErr: vm.ErrOutOfGas.Error(), + }, + "calling setFeeConfig from NoRole should fail": { + Caller: allowlisttest.TestNoRoleAddr, + BeforeHook: allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address), + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackSetFeeConfig(testDefaultUpdatedFeeConfig) + require.NoError(t, err) + return input + }, + SuppliedGas: acp224feemanager.SetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: acp224feemanager.ErrCannotSetFeeConfig.Error(), + }, + "calling setFeeConfig from Enabled should succeed": { + Caller: allowlisttest.TestEnabledAddr, + BeforeHook: allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address), + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackSetFeeConfig(testDefaultUpdatedFeeConfig) + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + return []byte{} + }(), + SuppliedGas: acp224feemanager.SetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling setFeeConfig from Manager should succeed": { + Caller: allowlisttest.TestManagerAddr, + BeforeHook: allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address), + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackSetFeeConfig(testDefaultUpdatedFeeConfig) + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + return []byte{} + }(), + SuppliedGas: acp224feemanager.SetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "calling setFeeConfig from Admin should succeed": { + Caller: allowlisttest.TestAdminAddr, + BeforeHook: allowlisttest.SetDefaultRoles(acp224feemanager.Module.Address), + InputFn: func(t testing.TB) []byte { + input, err := acp224feemanager.PackSetFeeConfig(testDefaultUpdatedFeeConfig) + require.NoError(t, err) + return input + }, + ExpectedRes: func() []byte { + return []byte{} + }(), + SuppliedGas: acp224feemanager.SetFeeConfigGasCost, + ReadOnly: false, + ExpectedErr: "", + }, + "readOnly setFeeConfig should fail": { + Caller: common.Address{1}, + InputFn: func(t testing.TB) []byte { + testInput := commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + } + input, err := acp224feemanager.PackSetFeeConfig(testInput) + require.NoError(t, err) + return input + }, + SuppliedGas: acp224feemanager.SetFeeConfigGasCost, + ReadOnly: true, + ExpectedErr: vm.ErrWriteProtection.Error(), + }, + "insufficient gas for setFeeConfig should fail": { + Caller: common.Address{1}, + InputFn: func(t testing.TB) []byte { + testInput := commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(10_000_000), + MinGasPrice: common.Big1, + TimeToFillCapacity: big.NewInt(5), + TimeToDouble: big.NewInt(60), + } + input, err := acp224feemanager.PackSetFeeConfig(testInput) + require.NoError(t, err) + return input + }, + SuppliedGas: acp224feemanager.SetFeeConfigGasCost - 1, + ReadOnly: false, + ExpectedErr: vm.ErrOutOfGas.Error(), + }, + } +) + +// TestACP224FeeManagerRun tests the Run function of the precompile contract. +func TestACP224FeeManagerRun(t *testing.T) { + allowlisttest.RunPrecompileWithAllowListTests(t, acp224feemanager.Module, tests) +} + +// TestPackUnpackFeeConfigUpdatedEventData tests the Pack/UnpackFeeConfigUpdatedEventData. +func TestPackUnpackFeeConfigUpdatedEventData(t *testing.T) { + var senderInput common.Address = acp224feemanager.ContractAddress + oldFeeConfig := commontype.ValidTestACP224FeeConfig + newFeeConfig := commontype.ACP224FeeConfig{ + TargetGas: big.NewInt(42_000_000), + MinGasPrice: big.NewInt(42), + TimeToFillCapacity: big.NewInt(42), + TimeToDouble: big.NewInt(42), + } + + dataInput := acp224feemanager.FeeConfigUpdatedEventData{ + OldFeeConfig: oldFeeConfig, + NewFeeConfig: newFeeConfig, + } + + _, data, err := acp224feemanager.PackFeeConfigUpdatedEvent( + senderInput, + dataInput, + ) + require.NoError(t, err) + + unpacked, err := acp224feemanager.UnpackFeeConfigUpdatedEventData(data) + require.NoError(t, err) + require.Equal(t, dataInput, unpacked) +} diff --git a/precompile/contracts/acp224feemanager/config.go b/precompile/contracts/acp224feemanager/config.go new file mode 100644 index 0000000000..9f0ae40c41 --- /dev/null +++ b/precompile/contracts/acp224feemanager/config.go @@ -0,0 +1,90 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package acp224feemanager + +import ( + "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/precompile/allowlist" + "github.com/ava-labs/subnet-evm/precompile/precompileconfig" +) + +var _ precompileconfig.Config = (*Config)(nil) + +// Config implements the precompileconfig.Config interface and +// adds specific configuration for ACP224FeeManager. +type Config struct { + allowlist.AllowListConfig + precompileconfig.Upgrade + InitialFeeConfig *commontype.ACP224FeeConfig `json:"initialFeeConfig,omitempty"` // initial fee config to be immediately activated +} + +// NewConfig returns a config for a network upgrade at [blockTimestamp] that enables +// ACP224FeeManager with the given [admins], [enableds] and [managers] members of the allowlist . +func NewConfig( + blockTimestamp *uint64, + admins []common.Address, + enableds []common.Address, + managers []common.Address, + initialConfig *commontype.ACP224FeeConfig, +) *Config { + return &Config{ + AllowListConfig: allowlist.AllowListConfig{ + AdminAddresses: admins, + EnabledAddresses: enableds, + ManagerAddresses: managers, + }, + Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp}, + InitialFeeConfig: initialConfig, + } +} + +// NewDisableConfig returns config for a network upgrade at [blockTimestamp] +// that disables ACP224FeeManager. +func NewDisableConfig(blockTimestamp *uint64) *Config { + return &Config{ + Upgrade: precompileconfig.Upgrade{ + BlockTimestamp: blockTimestamp, + Disable: true, + }, + } +} + +// Key returns the key for the ACP224FeeManager precompileconfig. +// This should be the same key as used in the precompile module. +func (*Config) Key() string { return ConfigKey } + +// Equal returns true if [s] is a [*ACP224FeeManagerConfig] and it has been configured identical to [c]. +func (c *Config) Equal(s precompileconfig.Config) bool { + // typecast before comparison + other, ok := (s).(*Config) + if !ok { + return false + } + + eq := c.Upgrade.Equal(&other.Upgrade) && c.AllowListConfig.Equal(&other.AllowListConfig) + if !eq { + return false + } + + if c.InitialFeeConfig == nil { + return other.InitialFeeConfig == nil + } + + return c.InitialFeeConfig.Equal(other.InitialFeeConfig) +} + +// Verify tries to verify Config and returns an error accordingly. +func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error { + if err := c.AllowListConfig.Verify(chainConfig, c.Upgrade); err != nil { + return err + } + + if c.InitialFeeConfig == nil { + return nil + } + + return c.InitialFeeConfig.Verify() +} diff --git a/precompile/contracts/acp224feemanager/contract.abi b/precompile/contracts/acp224feemanager/contract.abi new file mode 100644 index 0000000000..c605eb142a --- /dev/null +++ b/precompile/contracts/acp224feemanager/contract.abi @@ -0,0 +1,256 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "oldFeeConfig", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "newFeeConfig", + "type": "tuple" + } + ], + "name": "FeeConfigUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "role", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldRole", + "type": "uint256" + } + ], + "name": "RoleSet", + "type": "event" + }, + { + "inputs": [], + "name": "getFeeConfig", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "config", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeeConfigLastChangedAt", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "readAllowList", + "outputs": [ + { + "internalType": "uint256", + "name": "role", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "targetGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToFillCapacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timeToDouble", + "type": "uint256" + } + ], + "internalType": "struct IACP224FeeManager.FeeConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "setFeeConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "setNone", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/precompile/contracts/acp224feemanager/contract.go b/precompile/contracts/acp224feemanager/contract.go new file mode 100644 index 0000000000..cc7f959b93 --- /dev/null +++ b/precompile/contracts/acp224feemanager/contract.go @@ -0,0 +1,337 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package acp224feemanager + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/core/vm" + + _ "embed" + + "github.com/ava-labs/subnet-evm/accounts/abi" + "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/precompile/allowlist" + "github.com/ava-labs/subnet-evm/precompile/contract" +) + +const ( + minFeeConfigFieldKey = iota + 1 + // add new fields below this + // must preserve order of these fields + targetGasKey = iota + minGasPriceKey + timeToFillCapacityKey + timeToDoubleKey + // add new fields above this + numFeeConfigField = iota - 1 + + // Gas costs for each function. These are set to 1 by default. + // You should set a gas cost for each function in your contract. + // Generally, you should not set gas costs very low as this may cause your network to be vulnerable to DoS attacks. + // There are some predefined gas costs in contract/utils.go that you can use. + // This contract also uses AllowList precompile. + // You should also increase gas costs of functions that read from AllowList storage. + GetFeeConfigGasCost uint64 = contract.ReadGasCostPerSlot * numFeeConfigField + GetFeeConfigLastChangedAtGasCost uint64 = contract.ReadGasCostPerSlot + FeeConfigUpdatedEventGasCost uint64 = (contract.LogGas) + (contract.LogTopicGas * 2) + (contract.LogDataGas * numFeeConfigField * common.HashLength * 2) + SetFeeConfigGasCost uint64 = allowlist.ReadAllowListGasCost + + contract.WriteGasCostPerSlot*(numFeeConfigField+1) + // plus one for setting last changed at + GetFeeConfigGasCost + // for reading existing fee config to be emitted in event + FeeConfigUpdatedEventGasCost +) + +var ( + // Singleton StatefulPrecompiledContract for setting fee configs by permissioned callers. + ACP224FeeManagerPrecompile contract.StatefulPrecompiledContract = createACP224FeeManagerPrecompile() + + feeConfigLastChangedAtKey = common.Hash{'l', 'c', 'a'} + + ErrCannotSetFeeConfig = errors.New("non-enabled cannot call setFeeConfig") + + // ACP224FeeManagerRawABI contains the raw ABI of ACP224FeeManager contract. + //go:embed contract.abi + ACP224FeeManagerRawABI string + + ACP224FeeManagerABI = contract.ParseABI(ACP224FeeManagerRawABI) +) + +// GetACP224FeeManagerAllowListStatus returns the role of [address] for the ACP224FeeManager list. +func GetACP224FeeManagerAllowListStatus( + stateDB contract.StateDB, + precompileAddr common.Address, + address common.Address, +) allowlist.Role { + return allowlist.GetAllowListStatus(stateDB, precompileAddr, address) +} + +// SetACP224FeeManagerAllowListStatus sets the permissions of [address] to [role] for the +// ACP224FeeManager list. Assumes [role] has already been verified as valid. +// This stores the [role] in the contract storage with address [ContractAddress] +// and [address] hash. It means that any reusage of the [address] key for different value +// conflicts with the same slot [role] is stored. +// Precompile implementations must use a different key than [address] for their storage. +func SetACP224FeeManagerAllowListStatus( + stateDB contract.StateDB, + precompileAddr common.Address, + address common.Address, + role allowlist.Role, +) { + allowlist.SetAllowListRole(stateDB, precompileAddr, address, role) +} + +// GetStoredFeeConfig returns fee config from contract storage in given state +func GetStoredFeeConfig(stateDB contract.StateReader, addr common.Address) commontype.ACP224FeeConfig { + feeConfig := commontype.ACP224FeeConfig{} + for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { + val := stateDB.GetState(addr, common.Hash{byte(i)}) + switch i { + case targetGasKey: + feeConfig.TargetGas = new(big.Int).Set(val.Big()) + case minGasPriceKey: + feeConfig.MinGasPrice = new(big.Int).Set(val.Big()) + case timeToFillCapacityKey: + feeConfig.TimeToFillCapacity = new(big.Int).Set(val.Big()) + case timeToDoubleKey: + feeConfig.TimeToDouble = new(big.Int).Set(val.Big()) + default: + // This should never encounter an unknown fee config key + panic(fmt.Sprintf("unknown fee config key: %d", i)) + } + } + return feeConfig +} + +func GetFeeConfigLastChangedAt(stateDB contract.StateReader, addr common.Address) *big.Int { + val := stateDB.GetState(addr, feeConfigLastChangedAtKey) + return val.Big() +} + +// StoreFeeConfig stores given [feeConfig] and block number in the [blockContext] to the [stateDB]. +// A validation on [feeConfig] is done before storing. +func StoreFeeConfig(stateDB contract.StateDB, addr common.Address, feeConfig commontype.ACP224FeeConfig, blockContext contract.ConfigurationBlockContext) error { + if err := feeConfig.Verify(); err != nil { + return fmt.Errorf("cannot verify fee config: %w", err) + } + + for i := minFeeConfigFieldKey; i <= numFeeConfigField; i++ { + var input common.Hash + switch i { + case targetGasKey: + input = common.BigToHash(feeConfig.TargetGas) + case minGasPriceKey: + input = common.BigToHash(feeConfig.MinGasPrice) + case timeToFillCapacityKey: + input = common.BigToHash(feeConfig.TimeToFillCapacity) + case timeToDoubleKey: + input = common.BigToHash(feeConfig.TimeToDouble) + default: + // This should never encounter an unknown fee config key + panic(fmt.Sprintf("unknown fee config key: %d", i)) + } + stateDB.SetState(addr, common.Hash{byte(i)}, input) + } + + blockNumber := blockContext.Number() + if blockNumber == nil { + return errors.New("blockNumber cannot be nil") + } + stateDB.SetState(addr, feeConfigLastChangedAtKey, common.BigToHash(blockNumber)) + return nil +} + +// PackGetFeeConfig packs the include selector (first 4 func signature bytes). +// This function is mostly used for tests. +func PackGetFeeConfig() ([]byte, error) { + return ACP224FeeManagerABI.Pack("getFeeConfig") +} + +// PackGetFeeConfigOutput attempts to pack given config of type commontype.ACP224FeeConfig +// to conform the ABI outputs. +func PackGetFeeConfigOutput(config commontype.ACP224FeeConfig) ([]byte, error) { + return ACP224FeeManagerABI.PackOutput("getFeeConfig", config) +} + +// UnpackGetFeeConfigOutput attempts to unpack given [output] into the commontype.ACP224FeeConfig type output +// assumes that [output] does not include selector (omits first 4 func signature bytes) +func UnpackGetFeeConfigOutput(output []byte) (commontype.ACP224FeeConfig, error) { + res, err := ACP224FeeManagerABI.Unpack("getFeeConfig", output) + if err != nil { + return commontype.ACP224FeeConfig{}, err + } + unpacked := *abi.ConvertType(res[0], new(commontype.ACP224FeeConfig)).(*commontype.ACP224FeeConfig) + return unpacked, nil +} + +func getFeeConfig( + accessibleState contract.AccessibleState, + _ common.Address, + addr common.Address, + _ []byte, + suppliedGas uint64, + _ bool, +) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = contract.DeductGas(suppliedGas, GetFeeConfigGasCost); err != nil { + return nil, 0, err + } + + feeConfig := GetStoredFeeConfig(accessibleState.GetStateDB(), addr) + + output, err := PackGetFeeConfigOutput(feeConfig) + if err != nil { + return nil, remainingGas, err + } + + // Return the fee config as output and the remaining gas + return output, remainingGas, err +} + +// PackGetFeeConfigLastChangedAt packs the include selector (first 4 func signature bytes). +// This function is mostly used for tests. +func PackGetFeeConfigLastChangedAt() ([]byte, error) { + return ACP224FeeManagerABI.Pack("getFeeConfigLastChangedAt") +} + +// PackGetFeeConfigLastChangedAtOutput attempts to pack given blockNumber of type *big.Int +// to conform the ABI outputs. +func PackGetFeeConfigLastChangedAtOutput(blockNumber *big.Int) ([]byte, error) { + return ACP224FeeManagerABI.PackOutput("getFeeConfigLastChangedAt", blockNumber) +} + +// UnpackGetFeeConfigLastChangedAtOutput attempts to unpack given [output] into the *big.Int type output +// assumes that [output] does not include selector (omits first 4 func signature bytes) +func UnpackGetFeeConfigLastChangedAtOutput(output []byte) (*big.Int, error) { + res, err := ACP224FeeManagerABI.Unpack("getFeeConfigLastChangedAt", output) + if err != nil { + return new(big.Int), err + } + unpacked := *abi.ConvertType(res[0], new(*big.Int)).(**big.Int) + return unpacked, nil +} + +func getFeeConfigLastChangedAt( + accessibleState contract.AccessibleState, + _ common.Address, + addr common.Address, + _ []byte, + suppliedGas uint64, + _ bool, +) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = contract.DeductGas(suppliedGas, GetFeeConfigLastChangedAtGasCost); err != nil { + return nil, 0, err + } + + lastChangedAt := GetFeeConfigLastChangedAt(accessibleState.GetStateDB(), addr) + packedOutput, err := PackGetFeeConfigLastChangedAtOutput(lastChangedAt) + if err != nil { + return nil, remainingGas, err + } + + return packedOutput, remainingGas, err +} + +// UnpackSetFeeConfigInput attempts to unpack [input] into the commontype.ACP224FeeConfig type argument +// assumes that [input] does not include selector (omits first 4 func signature bytes) +func UnpackSetFeeConfigInput(input []byte) (commontype.ACP224FeeConfig, error) { + res, err := ACP224FeeManagerABI.UnpackInput("setFeeConfig", input, false) + if err != nil { + return commontype.ACP224FeeConfig{}, err + } + unpacked := *abi.ConvertType(res[0], new(commontype.ACP224FeeConfig)).(*commontype.ACP224FeeConfig) + return unpacked, nil +} + +// PackSetFeeConfig packs [config] of type commontype.ACP224FeeConfig into the appropriate arguments for setFeeConfig. +// the packed bytes include selector (first 4 func signature bytes). +// This function is mostly used for tests. +func PackSetFeeConfig(config commontype.ACP224FeeConfig) ([]byte, error) { + return ACP224FeeManagerABI.Pack("setFeeConfig", config) +} + +func setFeeConfig( + accessibleState contract.AccessibleState, + caller common.Address, + addr common.Address, + input []byte, + suppliedGas uint64, + readOnly bool, +) (ret []byte, remainingGas uint64, err error) { + if remainingGas, err = contract.DeductGas(suppliedGas, SetFeeConfigGasCost); err != nil { + return nil, 0, err + } + + if readOnly { + return nil, remainingGas, vm.ErrWriteProtection + } + + feeConfig, err := UnpackSetFeeConfigInput(input) + if err != nil { + return nil, remainingGas, err + } + + stateDB := accessibleState.GetStateDB() + // Verify that the caller is in the allow list and therefore has the right to call this function. + callerStatus := GetACP224FeeManagerAllowListStatus(stateDB, addr, caller) + if !callerStatus.IsEnabled() { + return nil, remainingGas, fmt.Errorf("%w: %s", ErrCannotSetFeeConfig, caller) + } + + oldConfig := GetStoredFeeConfig(stateDB, addr) + eventData := FeeConfigUpdatedEventData{ + OldFeeConfig: oldConfig, + NewFeeConfig: feeConfig, + } + topics, data, err := PackFeeConfigUpdatedEvent( + caller, + eventData, + ) + if err != nil { + return nil, remainingGas, err + } + + stateDB.AddLog(&types.Log{ + Address: addr, + Topics: topics, + Data: data, + BlockNumber: accessibleState.GetBlockContext().Number().Uint64(), + }) + + if err := StoreFeeConfig(stateDB, addr, feeConfig, accessibleState.GetBlockContext()); err != nil { + return nil, remainingGas, err + } + + // Return an empty output and the remaining gas + return []byte{}, remainingGas, nil +} + +// createACP224FeeManagerPrecompile returns a StatefulPrecompiledContract with getters and setters for the precompile. +// Access to the getters/setters is controlled by an allow list for ContractAddress. +func createACP224FeeManagerPrecompile() contract.StatefulPrecompiledContract { + var functions []*contract.StatefulPrecompileFunction + functions = append(functions, allowlist.CreateAllowListFunctions(ContractAddress)...) + + abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{ + "getFeeConfig": getFeeConfig, + "getFeeConfigLastChangedAt": getFeeConfigLastChangedAt, + "setFeeConfig": setFeeConfig, + } + + for name, function := range abiFunctionMap { + method, ok := ACP224FeeManagerABI.Methods[name] + if !ok { + panic(fmt.Errorf("given method (%s) does not exist in the ABI", name)) + } + functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function)) + } + // Construct the contract with no fallback function. + statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions) + if err != nil { + panic(err) + } + return statefulContract +} diff --git a/precompile/contracts/acp224feemanager/event.go b/precompile/contracts/acp224feemanager/event.go new file mode 100644 index 0000000000..b56ad4d873 --- /dev/null +++ b/precompile/contracts/acp224feemanager/event.go @@ -0,0 +1,29 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package acp224feemanager + +import ( + "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/subnet-evm/commontype" +) + +// ACP224FeeManagerFeeConfigUpdated represents a FeeConfigUpdated non-indexed event data raised by the ACP224FeeManager contract. +type FeeConfigUpdatedEventData struct { + OldFeeConfig commontype.ACP224FeeConfig + NewFeeConfig commontype.ACP224FeeConfig +} + +// PackFeeConfigUpdatedEvent packs the event into the appropriate arguments for FeeConfigUpdated. +// It returns topic hashes and the encoded non-indexed data. +func PackFeeConfigUpdatedEvent(sender common.Address, data FeeConfigUpdatedEventData) ([]common.Hash, []byte, error) { + return ACP224FeeManagerABI.PackEvent("FeeConfigUpdated", sender, data.OldFeeConfig, data.NewFeeConfig) +} + +// UnpackFeeConfigUpdatedEventData attempts to unpack non-indexed [dataBytes]. +func UnpackFeeConfigUpdatedEventData(dataBytes []byte) (FeeConfigUpdatedEventData, error) { + eventData := FeeConfigUpdatedEventData{} + err := ACP224FeeManagerABI.UnpackIntoInterface(&eventData, "FeeConfigUpdated", dataBytes) + return eventData, err +} diff --git a/precompile/contracts/acp224feemanager/module.go b/precompile/contracts/acp224feemanager/module.go new file mode 100644 index 0000000000..6c67267b36 --- /dev/null +++ b/precompile/contracts/acp224feemanager/module.go @@ -0,0 +1,69 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package acp224feemanager + +import ( + "fmt" + + "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/subnet-evm/precompile/contract" + "github.com/ava-labs/subnet-evm/precompile/modules" + "github.com/ava-labs/subnet-evm/precompile/precompileconfig" +) + +var _ contract.Configurator = (*configurator)(nil) + +// ConfigKey is the key used in json config files to specify this precompile config. +// must be unique across all precompiles. +const ConfigKey = "acp224FeeManagerConfig" + +var ContractAddress = common.HexToAddress("0x0200000000000000000000000000000000000006") + +// Module is the precompile module. It is used to register the precompile contract. +var Module = modules.Module{ + ConfigKey: ConfigKey, + Address: ContractAddress, + Contract: ACP224FeeManagerPrecompile, + Configurator: &configurator{}, +} + +type configurator struct{} + +func init() { + // Register the precompile module. + // Each precompile contract registers itself through [RegisterModule] function. + if err := modules.RegisterModule(Module); err != nil { + panic(err) + } +} + +// MakeConfig returns a new precompile config instance. +// This is required to Marshal/Unmarshal the precompile config. +func (*configurator) MakeConfig() precompileconfig.Config { + return new(Config) +} + +// Configure configures [state] with the given [cfg] precompileconfig. +// This function is called by the EVM once per precompile contract activation. +func (*configurator) Configure(chainConfig precompileconfig.ChainConfig, cfg precompileconfig.Config, state contract.StateDB, blockContext contract.ConfigurationBlockContext) error { + config, ok := cfg.(*Config) + if !ok { + return fmt.Errorf("expected config type %T, got %T: %v", &Config{}, cfg, cfg) + } + + // Store the initial fee config into the state when the fee manager activates. + if config.InitialFeeConfig != nil { + if err := StoreFeeConfig(state, ContractAddress, *config.InitialFeeConfig, blockContext); err != nil { + // This should not happen since we already checked this config with Verify() + return fmt.Errorf("cannot configure given initial fee config: %w", err) + } + } else { + if err := StoreFeeConfig(state, ContractAddress, chainConfig.GetACP224FeeConfig(), blockContext); err != nil { + // This should not happen since we already checked the chain config in the genesis creation. + return fmt.Errorf("cannot configure fee config in chain config: %w", err) + } + } + return config.AllowListConfig.Configure(chainConfig, ContractAddress, state, blockContext) +} diff --git a/precompile/precompileconfig/config.go b/precompile/precompileconfig/config.go index 5af8bafdf6..54e87a47c1 100644 --- a/precompile/precompileconfig/config.go +++ b/precompile/precompileconfig/config.go @@ -77,6 +77,8 @@ type Accepter interface { // about the chain configuration. The precompile can access this information to initialize // its state. type ChainConfig interface { + // GetACP224FeeConfig returns the original ACP224FeeConfig that was set in the genesis. + GetACP224FeeConfig() commontype.ACP224FeeConfig // GetFeeConfig returns the original FeeConfig that was set in the genesis. GetFeeConfig() commontype.FeeConfig // AllowedFeeRecipients returns true if fee recipients are allowed in the genesis. diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index 41f8a16fa4..91ef9b88f0 100644 --- a/precompile/precompileconfig/mocks.go +++ b/precompile/precompileconfig/mocks.go @@ -203,6 +203,20 @@ func (mr *MockChainConfigMockRecorder) AllowedFeeRecipients() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllowedFeeRecipients", reflect.TypeOf((*MockChainConfig)(nil).AllowedFeeRecipients)) } +// GetACP224FeeConfig mocks base method. +func (m *MockChainConfig) GetACP224FeeConfig() commontype.ACP224FeeConfig { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetACP224FeeConfig") + ret0, _ := ret[0].(commontype.ACP224FeeConfig) + return ret0 +} + +// GetACP224FeeConfig indicates an expected call of GetACP224FeeConfig. +func (mr *MockChainConfigMockRecorder) GetACP224FeeConfig() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetACP224FeeConfig", reflect.TypeOf((*MockChainConfig)(nil).GetACP224FeeConfig)) +} + // GetFeeConfig mocks base method. func (m *MockChainConfig) GetFeeConfig() commontype.FeeConfig { m.ctrl.T.Helper() diff --git a/precompile/precompiletest/test_precompile.go b/precompile/precompiletest/test_precompile.go index 36790f7748..6d9d87c1cd 100644 --- a/precompile/precompiletest/test_precompile.go +++ b/precompile/precompiletest/test_precompile.go @@ -104,6 +104,7 @@ func (test PrecompileTest) setup(t testing.TB, module modules.Module, state *tes test.ChainConfigFn = func(ctrl *gomock.Controller) precompileconfig.ChainConfig { mockChainConfig := precompileconfig.NewMockChainConfig(ctrl) mockChainConfig.EXPECT().GetFeeConfig().AnyTimes().Return(commontype.ValidTestFeeConfig) + mockChainConfig.EXPECT().GetACP224FeeConfig().AnyTimes().Return(commontype.ValidTestACP224FeeConfig) mockChainConfig.EXPECT().AllowedFeeRecipients().AnyTimes().Return(false) mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(true) return mockChainConfig diff --git a/precompile/registry/registry.go b/precompile/registry/registry.go index ee304fd525..d55150a414 100644 --- a/precompile/registry/registry.go +++ b/precompile/registry/registry.go @@ -33,5 +33,6 @@ import ( // FeeManagerAddress = common.HexToAddress("0x0200000000000000000000000000000000000003") // RewardManagerAddress = common.HexToAddress("0x0200000000000000000000000000000000000004") // WarpAddress = common.HexToAddress("0x0200000000000000000000000000000000000005") +// ACP224FeeManagerAddress = common.HexToAddress("0x0200000000000000000000000000000000000006") // ADD YOUR PRECOMPILE HERE // {YourPrecompile}Address = common.HexToAddress("0x03000000000000000000000000000000000000??") diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 512c73bd51..0736775cde 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -307,7 +307,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // Prepare the EVM. txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) + rulesExtra := params.GetRulesExtra(config.Rules(block.Number(), params.IsMergeTODO, block.Time())) + context := core.NewEVMBlockContext(rulesExtra.AvalancheRules, block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee context.Random = nil