Skip to content

Commit 81cfa59

Browse files
authored
set minfee from state on mempool init (#1594)
1 parent 43afaf4 commit 81cfa59

File tree

3 files changed

+169
-4
lines changed

3 files changed

+169
-4
lines changed

RELEASES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Updated dockerhub image name to `avaplatform/subnet-evm_avalanchego` and tags to accommodate the new versioning scheme: {subnet-evm version}_{avalanchego version}
66
- Updated golang version to 1.23.9
7+
- Fixed a bug in mempool where the min fee was not updated after restart
78

89
## [v0.7.3](https://github.com/ava-labs/subnet-evm/releases/tag/v0.7.3)
910

plugin/evm/vm.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -594,13 +594,18 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig.
594594
}
595595
vm.eth.SetEtherbase(ethConfig.Miner.Etherbase)
596596
vm.txPool = vm.eth.TxPool()
597-
vm.txPool.SetMinFee(vm.chainConfigExtra().FeeConfig.MinBaseFee)
598-
vm.txPool.SetGasTip(big.NewInt(0))
599597
vm.blockChain = vm.eth.BlockChain()
600598
vm.miner = vm.eth.Miner()
599+
lastAccepted := vm.blockChain.LastAcceptedBlock()
600+
feeConfig, _, err := vm.blockChain.GetFeeConfigAt(lastAccepted.Header())
601+
if err != nil {
602+
return err
603+
}
604+
vm.txPool.SetMinFee(feeConfig.MinBaseFee)
605+
vm.txPool.SetGasTip(big.NewInt(0))
601606

602607
vm.eth.Start()
603-
return vm.initChainState(vm.blockChain.LastAcceptedBlock())
608+
return vm.initChainState(lastAccepted)
604609
}
605610

606611
// initializeStateSyncClient initializes the client for performing state sync.

plugin/evm/vm_test.go

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2510,7 +2510,6 @@ func TestFeeManagerChangeFee(t *testing.T) {
25102510

25112511
block := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock
25122512

2513-
// Contract is initialized but no state is given, reader should return genesis fee config
25142513
feeConfig, lastChangedAt, err = vm.blockChain.GetFeeConfigAt(block.Header())
25152514
require.NoError(t, err)
25162515
require.EqualValues(t, testHighFeeConfig, feeConfig)
@@ -3161,3 +3160,163 @@ func TestStandaloneDB(t *testing.T) {
31613160
assert.False(t, isDBEmpty(vm.db))
31623161
assert.False(t, isDBEmpty(vm.acceptedBlockDB))
31633162
}
3163+
3164+
func TestFeeManagerRegressionMempoolMinFeeAfterRestart(t *testing.T) {
3165+
// Setup chain params
3166+
genesis := &core.Genesis{}
3167+
if err := genesis.UnmarshalJSON([]byte(genesisJSONSubnetEVM)); err != nil {
3168+
t.Fatal(err)
3169+
}
3170+
precompileActivationTime := utils.NewUint64(genesis.Timestamp + 5) // 5 seconds after genesis
3171+
configExtra := params.GetExtra(genesis.Config)
3172+
configExtra.GenesisPrecompiles = extras.Precompiles{
3173+
feemanager.ConfigKey: feemanager.NewConfig(precompileActivationTime, testEthAddrs[0:1], nil, nil, nil),
3174+
}
3175+
3176+
// set a higher fee config now
3177+
testHighFeeConfig := commontype.FeeConfig{
3178+
GasLimit: big.NewInt(8_000_000),
3179+
TargetBlockRate: 5, // in seconds
3180+
3181+
MinBaseFee: big.NewInt(50_000_000),
3182+
TargetGas: big.NewInt(18_000_000),
3183+
BaseFeeChangeDenominator: big.NewInt(3396),
3184+
3185+
MinBlockGasCost: big.NewInt(0),
3186+
MaxBlockGasCost: big.NewInt(4_000_000),
3187+
BlockGasCostStep: big.NewInt(500_000),
3188+
}
3189+
3190+
configExtra.FeeConfig = testHighFeeConfig
3191+
genesisJSON, err := genesis.MarshalJSON()
3192+
if err != nil {
3193+
t.Fatal(err)
3194+
}
3195+
issuer, vm, sharedDB, appSender := GenesisVM(t, true, string(genesisJSON), "", "")
3196+
3197+
// tx pool min base fee should be the high fee config
3198+
tx := types.NewTx(&types.DynamicFeeTx{
3199+
ChainID: genesis.Config.ChainID,
3200+
Nonce: uint64(0),
3201+
To: &feemanager.ContractAddress,
3202+
Gas: 21_000,
3203+
Value: common.Big0,
3204+
GasFeeCap: big.NewInt(5_000_000), // give a lower base fee
3205+
GasTipCap: common.Big0,
3206+
Data: nil,
3207+
})
3208+
signedTx, err := types.SignTx(tx, types.LatestSigner(genesis.Config), testKeys[0])
3209+
require.NoError(t, err)
3210+
3211+
errs := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx})
3212+
require.Len(t, errs, 1)
3213+
require.ErrorIs(t, errs[0], txpool.ErrUnderpriced) // should fail because mempool expects higher fee
3214+
3215+
// restart vm and try again
3216+
genesisBytes := buildGenesisTest(t, string(genesisJSON))
3217+
restartedVM, err := restartVM(vm, sharedDB, genesisBytes, issuer, appSender, true)
3218+
require.NoError(t, err)
3219+
3220+
// it still should fail
3221+
errs = restartedVM.txPool.AddRemotesSync([]*types.Transaction{signedTx})
3222+
require.Len(t, errs, 1)
3223+
require.ErrorIs(t, errs[0], txpool.ErrUnderpriced)
3224+
3225+
// send a tx to activate the precompile
3226+
newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1)
3227+
restartedVM.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan)
3228+
restartedVM.clock.Set(utils.Uint64ToTime(precompileActivationTime).Add(time.Second * 10))
3229+
tx = types.NewTransaction(uint64(0), testEthAddrs[0], common.Big0, 21000, big.NewInt(testHighFeeConfig.MinBaseFee.Int64()), nil)
3230+
signedTx, err = types.SignTx(tx, types.LatestSigner(genesis.Config), testKeys[0])
3231+
require.NoError(t, err)
3232+
errs = restartedVM.txPool.AddRemotesSync([]*types.Transaction{signedTx})
3233+
require.NoError(t, errs[0])
3234+
blk := issueAndAccept(t, issuer, restartedVM)
3235+
newHead := <-newTxPoolHeadChan
3236+
require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID()))
3237+
// Contract is initialized but no preconfig is given, reader should return genesis fee config
3238+
feeConfig, lastChangedAt, err := vm.blockChain.GetFeeConfigAt(vm.blockChain.Genesis().Header())
3239+
require.NoError(t, err)
3240+
require.EqualValues(t, feeConfig, testHighFeeConfig)
3241+
require.Zero(t, vm.blockChain.CurrentBlock().Number.Cmp(lastChangedAt))
3242+
3243+
// set a lower fee config now through feemanager
3244+
testLowFeeConfig := testHighFeeConfig
3245+
testLowFeeConfig.MinBaseFee = big.NewInt(25_000_000)
3246+
data, err := feemanager.PackSetFeeConfig(testLowFeeConfig)
3247+
require.NoError(t, err)
3248+
tx = types.NewTx(&types.DynamicFeeTx{
3249+
ChainID: genesis.Config.ChainID,
3250+
Nonce: uint64(1),
3251+
To: &feemanager.ContractAddress,
3252+
Gas: 1_000_000,
3253+
Value: common.Big0,
3254+
GasFeeCap: testHighFeeConfig.MinBaseFee, // the blockchain state still expects high fee
3255+
Data: data,
3256+
})
3257+
// let some time pass for block gas cost
3258+
restartedVM.clock.Set(restartedVM.clock.Time().Add(time.Second * 10))
3259+
signedTx, err = types.SignTx(tx, types.LatestSigner(genesis.Config), testKeys[0])
3260+
require.NoError(t, err)
3261+
errs = restartedVM.txPool.AddRemotesSync([]*types.Transaction{signedTx})
3262+
require.NoError(t, errs[0])
3263+
blk = issueAndAccept(t, issuer, restartedVM)
3264+
newHead = <-newTxPoolHeadChan
3265+
require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID()))
3266+
3267+
// check that the fee config is updated
3268+
block := blk.(*chain.BlockWrapper).Block.(*Block).ethBlock
3269+
feeConfig, lastChangedAt, err = restartedVM.blockChain.GetFeeConfigAt(block.Header())
3270+
require.NoError(t, err)
3271+
require.EqualValues(t, restartedVM.blockChain.CurrentBlock().Number, lastChangedAt)
3272+
require.EqualValues(t, testLowFeeConfig, feeConfig)
3273+
3274+
// send another tx with low fee
3275+
tx = types.NewTransaction(uint64(2), testEthAddrs[0], common.Big0, 21000, big.NewInt(testLowFeeConfig.MinBaseFee.Int64()), nil)
3276+
signedTx, err = types.SignTx(tx, types.LatestSigner(genesis.Config), testKeys[0])
3277+
require.NoError(t, err)
3278+
errs = restartedVM.txPool.AddRemotesSync([]*types.Transaction{signedTx})
3279+
require.NoError(t, errs[0])
3280+
// let some time pass for block gas cost and fees to be updated
3281+
restartedVM.clock.Set(restartedVM.clock.Time().Add(time.Hour * 10))
3282+
blk = issueAndAccept(t, issuer, restartedVM)
3283+
newHead = <-newTxPoolHeadChan
3284+
require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID()))
3285+
3286+
// Regression: Mempool should see the new config after restart
3287+
restartedVM, err = restartVM(restartedVM, sharedDB, genesisBytes, issuer, appSender, true)
3288+
require.NoError(t, err)
3289+
newTxPoolHeadChan = make(chan core.NewTxPoolReorgEvent, 1)
3290+
restartedVM.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan)
3291+
// send a tx with low fee
3292+
tx = types.NewTransaction(uint64(3), testEthAddrs[0], common.Big0, 21000, big.NewInt(testLowFeeConfig.MinBaseFee.Int64()), nil)
3293+
signedTx, err = types.SignTx(tx, types.LatestSigner(genesis.Config), testKeys[0])
3294+
require.NoError(t, err)
3295+
errs = restartedVM.txPool.AddRemotesSync([]*types.Transaction{signedTx})
3296+
require.NoError(t, errs[0])
3297+
blk = issueAndAccept(t, issuer, restartedVM)
3298+
newHead = <-newTxPoolHeadChan
3299+
require.Equal(t, newHead.Head.Hash(), common.Hash(blk.ID()))
3300+
}
3301+
3302+
func restartVM(vm *VM, sharedDB database.Database, genesisBytes []byte, issuer chan commonEng.Message, appSender commonEng.AppSender, finishBootstrapping bool) (*VM, error) {
3303+
vm.Shutdown(context.Background())
3304+
restartedVM := &VM{}
3305+
vm.ctx.Metrics = metrics.NewPrefixGatherer()
3306+
err := restartedVM.Initialize(context.Background(), vm.ctx, sharedDB, genesisBytes, nil, nil, issuer, []*commonEng.Fx{}, appSender)
3307+
if err != nil {
3308+
return nil, err
3309+
}
3310+
3311+
if finishBootstrapping {
3312+
err = restartedVM.SetState(context.Background(), snow.Bootstrapping)
3313+
if err != nil {
3314+
return nil, err
3315+
}
3316+
err = restartedVM.SetState(context.Background(), snow.NormalOp)
3317+
if err != nil {
3318+
return nil, err
3319+
}
3320+
}
3321+
return restartedVM, nil
3322+
}

0 commit comments

Comments
 (0)