diff --git a/config/config.go b/config/config.go index 42b2a2be4b7..09ef025944b 100644 --- a/config/config.go +++ b/config/config.go @@ -776,6 +776,8 @@ func getTxFeeConfig(v *viper.Viper, networkID uint32) genesis.TxFeeConfig { AddSubnetValidatorFee: v.GetUint64(AddSubnetValidatorFeeKey), AddSubnetDelegatorFee: v.GetUint64(AddSubnetDelegatorFeeKey), }, + // TODO: Set these values via flags + DynamicFeeConfig: genesis.LocalParams.DynamicFeeConfig, } } return genesis.GetTxFeeConfig(networkID) diff --git a/genesis/genesis_fuji.go b/genesis/genesis_fuji.go index 23af60cfc98..c47e691cc3f 100644 --- a/genesis/genesis_fuji.go +++ b/genesis/genesis_fuji.go @@ -10,7 +10,9 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/platformvm/reward" - "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" + + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) var ( @@ -21,7 +23,7 @@ var ( FujiParams = Params{ TxFeeConfig: TxFeeConfig{ CreateAssetTxFee: 10 * units.MilliAvax, - StaticFeeConfig: fee.StaticConfig{ + StaticFeeConfig: txfee.StaticConfig{ TxFee: units.MilliAvax, CreateSubnetTxFee: 100 * units.MilliAvax, TransformSubnetTxFee: 1 * units.Avax, @@ -31,6 +33,20 @@ var ( AddSubnetValidatorFee: units.MilliAvax, AddSubnetDelegatorFee: units.MilliAvax, }, + // TODO: Set these values to something more reasonable + DynamicFeeConfig: feecomponent.Config{ + Weights: feecomponent.Dimensions{ + feecomponent.Bandwidth: 1, + feecomponent.DBRead: 1, + feecomponent.DBWrite: 1, + feecomponent.Compute: 1, + }, + MaxGasCapacity: 1_000_000, + MaxGasPerSecond: 1_000, + TargetGasPerSecond: 500, + MinGasPrice: 1, + ExcessConversionConstant: 1, + }, }, StakingConfig: StakingConfig{ UptimeRequirement: .8, // 80% diff --git a/genesis/genesis_local.go b/genesis/genesis_local.go index 2039eaa99e2..095e664d249 100644 --- a/genesis/genesis_local.go +++ b/genesis/genesis_local.go @@ -13,7 +13,9 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/platformvm/reward" - "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" + + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) // PrivateKey-vmRQiZeXEXYMyJhEiqdC2z5JhuDbxL8ix9UVvjgMu2Er1NepE => P-local1g65uqn6t77p656w64023nh8nd9updzmxyymev2 @@ -39,7 +41,7 @@ var ( LocalParams = Params{ TxFeeConfig: TxFeeConfig{ CreateAssetTxFee: units.MilliAvax, - StaticFeeConfig: fee.StaticConfig{ + StaticFeeConfig: txfee.StaticConfig{ TxFee: units.MilliAvax, CreateSubnetTxFee: 100 * units.MilliAvax, TransformSubnetTxFee: 100 * units.MilliAvax, @@ -49,6 +51,20 @@ var ( AddSubnetValidatorFee: units.MilliAvax, AddSubnetDelegatorFee: units.MilliAvax, }, + // TODO: Set these values to something more reasonable + DynamicFeeConfig: feecomponent.Config{ + Weights: feecomponent.Dimensions{ + feecomponent.Bandwidth: 1, + feecomponent.DBRead: 1, + feecomponent.DBWrite: 1, + feecomponent.Compute: 1, + }, + MaxGasCapacity: 1_000_000, + MaxGasPerSecond: 1_000, + TargetGasPerSecond: 500, + MinGasPrice: 1, + ExcessConversionConstant: 1, + }, }, StakingConfig: StakingConfig{ UptimeRequirement: .8, // 80% diff --git a/genesis/genesis_mainnet.go b/genesis/genesis_mainnet.go index 66660c1f265..e0e7228bb15 100644 --- a/genesis/genesis_mainnet.go +++ b/genesis/genesis_mainnet.go @@ -10,7 +10,9 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/platformvm/reward" - "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" + + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) var ( @@ -21,7 +23,7 @@ var ( MainnetParams = Params{ TxFeeConfig: TxFeeConfig{ CreateAssetTxFee: 10 * units.MilliAvax, - StaticFeeConfig: fee.StaticConfig{ + StaticFeeConfig: txfee.StaticConfig{ TxFee: units.MilliAvax, CreateSubnetTxFee: 1 * units.Avax, TransformSubnetTxFee: 10 * units.Avax, @@ -31,6 +33,20 @@ var ( AddSubnetValidatorFee: units.MilliAvax, AddSubnetDelegatorFee: units.MilliAvax, }, + // TODO: Set these values to something more reasonable + DynamicFeeConfig: feecomponent.Config{ + Weights: feecomponent.Dimensions{ + feecomponent.Bandwidth: 1, + feecomponent.DBRead: 1, + feecomponent.DBWrite: 1, + feecomponent.Compute: 1, + }, + MaxGasCapacity: 1_000_000, + MaxGasPerSecond: 1_000, + TargetGasPerSecond: 500, + MinGasPrice: 1, + ExcessConversionConstant: 1, + }, }, StakingConfig: StakingConfig{ UptimeRequirement: .8, // 80% diff --git a/genesis/params.go b/genesis/params.go index 9a3cc04a6ee..01eb7067d81 100644 --- a/genesis/params.go +++ b/genesis/params.go @@ -8,7 +8,9 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/vms/platformvm/reward" - "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" + + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) type StakingConfig struct { @@ -35,8 +37,9 @@ type StakingConfig struct { } type TxFeeConfig struct { - CreateAssetTxFee uint64 `json:"createAssetTxFee"` - StaticFeeConfig fee.StaticConfig `json:"staticFeeConfig"` + CreateAssetTxFee uint64 `json:"createAssetTxFee"` + StaticFeeConfig txfee.StaticConfig `json:"staticFeeConfig"` + DynamicFeeConfig feecomponent.Config `json:"dynamicFeeConfig"` } type Params struct { diff --git a/node/node.go b/node/node.go index e7d8f0921db..e63f92e82bc 100644 --- a/node/node.go +++ b/node/node.go @@ -1229,6 +1229,7 @@ func (n *Node) initVMs() error { TrackedSubnets: n.Config.TrackedSubnets, CreateAssetTxFee: n.Config.CreateAssetTxFee, StaticFeeConfig: n.Config.StaticFeeConfig, + DynamicFeeConfig: n.Config.DynamicFeeConfig, UptimePercentage: n.Config.UptimeRequirement, MinValidatorStake: n.Config.MinValidatorStake, MaxValidatorStake: n.Config.MaxValidatorStake, diff --git a/vms/platformvm/block/builder/builder.go b/vms/platformvm/block/builder/builder.go index b6c50cdb2a0..3916c6b84ef 100644 --- a/vms/platformvm/block/builder/builder.go +++ b/vms/platformvm/block/builder/builder.go @@ -24,8 +24,10 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/block/executor" txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) // targetBlockSize is maximum number of transaction bytes to place into a @@ -243,7 +245,7 @@ func (b *builder) PackBlockTxs(targetBlockSize int) ([]*txs.Tx, error) { return nil, fmt.Errorf("%w: %s", errMissingPreferredState, preferredID) } - return packBlockTxs( + return packDurangoBlockTxs( preferredID, preferredState, b.Mempool, @@ -263,15 +265,30 @@ func buildBlock( forceAdvanceTime bool, parentState state.Chain, ) (block.Block, error) { - blockTxs, err := packBlockTxs( - parentID, - parentState, - builder.Mempool, - builder.txExecutorBackend, - builder.blkManager, - timestamp, - targetBlockSize, + var ( + blockTxs []*txs.Tx + err error ) + if builder.txExecutorBackend.Config.UpgradeConfig.IsEActivated(timestamp) { + blockTxs, err = packEtnaBlockTxs( + parentID, + parentState, + builder.Mempool, + builder.txExecutorBackend, + builder.blkManager, + timestamp, + ) + } else { + blockTxs, err = packDurangoBlockTxs( + parentID, + parentState, + builder.Mempool, + builder.txExecutorBackend, + builder.blkManager, + timestamp, + targetBlockSize, + ) + } if err != nil { return nil, fmt.Errorf("failed to pack block txs: %w", err) } @@ -313,7 +330,7 @@ func buildBlock( ) } -func packBlockTxs( +func packDurangoBlockTxs( parentID ids.ID, parentState state.Chain, mempool mempool.Mempool, @@ -345,55 +362,162 @@ func packBlockTxs( if txSize > remainingSize { break } - mempool.Remove(tx) - // Invariant: [tx] has already been syntactically verified. - - txDiff, err := state.NewDiffOn(stateDiff) + shouldAdd, err := executeTx( + parentID, + stateDiff, + mempool, + backend, + manager, + &inputs, + feeCalculator, + tx, + ) if err != nil { return nil, err } + if !shouldAdd { + continue + } + + remainingSize -= txSize + blockTxs = append(blockTxs, tx) + } + + return blockTxs, nil +} + +func packEtnaBlockTxs( + parentID ids.ID, + parentState state.Chain, + mempool mempool.Mempool, + backend *txexecutor.Backend, + manager blockexecutor.Manager, + timestamp time.Time, +) ([]*txs.Tx, error) { + stateDiff, err := state.NewDiffOn(parentState) + if err != nil { + return nil, err + } + + if _, err := txexecutor.AdvanceTimeTo(backend, stateDiff, timestamp); err != nil { + return nil, err + } - executor := &txexecutor.StandardTxExecutor{ - Backend: backend, - State: txDiff, - FeeCalculator: feeCalculator, - Tx: tx, + feeComplexity := stateDiff.GetFeeComplexity() + return packEtnaBlockTxsOn(parentID, stateDiff, mempool, backend, manager, feeComplexity.Capacity) +} + +func packEtnaBlockTxsOn( + parentID ids.ID, + stateDiff state.Diff, + mempool mempool.Mempool, + backend *txexecutor.Backend, + manager blockexecutor.Manager, + capacity feecomponent.Gas, +) ([]*txs.Tx, error) { + var ( + blockTxs []*txs.Tx + inputs set.Set[ids.ID] + blockComplexity feecomponent.Dimensions + feeCalculator = state.PickFeeCalculator(backend.Config, stateDiff) + ) + for { + tx, exists := mempool.Peek() + if !exists { + break } - err = tx.Unsigned.Visit(executor) + txComplexity, err := txfee.TxComplexity(tx.Unsigned) if err != nil { - txID := tx.ID() - mempool.MarkDropped(txID, err) - continue + return nil, err } - - if inputs.Overlaps(executor.Inputs) { - txID := tx.ID() - mempool.MarkDropped(txID, blockexecutor.ErrConflictingBlockTxs) - continue + newBlockComplexity, err := blockComplexity.Add(&txComplexity) + if err != nil { + return nil, err } - err = manager.VerifyUniqueInputs(parentID, executor.Inputs) + newBlockGas, err := newBlockComplexity.ToGas(backend.Config.DynamicFeeConfig.Weights) if err != nil { - txID := tx.ID() - mempool.MarkDropped(txID, err) - continue + return nil, err + } + if newBlockGas > capacity { + break } - inputs.Union(executor.Inputs) - txDiff.AddTx(tx, status.Committed) - err = txDiff.Apply(stateDiff) + shouldAdd, err := executeTx( + parentID, + stateDiff, + mempool, + backend, + manager, + &inputs, + feeCalculator, + tx, + ) if err != nil { return nil, err } + if !shouldAdd { + continue + } - remainingSize -= txSize + blockComplexity = newBlockComplexity blockTxs = append(blockTxs, tx) } return blockTxs, nil } +func executeTx( + parentID ids.ID, + stateDiff state.Diff, + mempool mempool.Mempool, + backend *txexecutor.Backend, + manager blockexecutor.Manager, + inputs *set.Set[ids.ID], + feeCalculator txfee.Calculator, + tx *txs.Tx, +) (bool, error) { + mempool.Remove(tx) + + // Invariant: [tx] has already been syntactically verified. + + txDiff, err := state.NewDiffOn(stateDiff) + if err != nil { + return false, err + } + + executor := &txexecutor.StandardTxExecutor{ + Backend: backend, + State: txDiff, + FeeCalculator: feeCalculator, + Tx: tx, + } + + err = tx.Unsigned.Visit(executor) + if err != nil { + txID := tx.ID() + mempool.MarkDropped(txID, err) + return false, nil + } + + if inputs.Overlaps(executor.Inputs) { + txID := tx.ID() + mempool.MarkDropped(txID, blockexecutor.ErrConflictingBlockTxs) + return false, nil + } + err = manager.VerifyUniqueInputs(parentID, executor.Inputs) + if err != nil { + txID := tx.ID() + mempool.MarkDropped(txID, err) + return false, nil + } + inputs.Union(executor.Inputs) + + txDiff.AddTx(tx, status.Committed) + return true, txDiff.Apply(stateDiff) +} + // getNextStakerToReward returns the next staker txID to remove from the staking // set with a RewardValidatorTx rather than an AdvanceTimeTx. [chainTimestamp] // is the timestamp of the chain at the time this validator would be getting diff --git a/vms/platformvm/block/executor/verifier.go b/vms/platformvm/block/executor/verifier.go index a8f2aeaf1de..e07c78887e4 100644 --- a/vms/platformvm/block/executor/verifier.go +++ b/vms/platformvm/block/executor/verifier.go @@ -15,7 +15,9 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" - "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" + + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) var ( @@ -375,7 +377,7 @@ func (v *verifier) proposalBlock( onDecisionState state.Diff, onCommitState state.Diff, onAbortState state.Diff, - feeCalculator fee.Calculator, + feeCalculator txfee.Calculator, inputs set.Set[ids.ID], atomicRequests map[ids.ID]*atomic.Requests, onAcceptFunc func(), @@ -424,7 +426,7 @@ func (v *verifier) proposalBlock( // standardBlock populates the state of this block if [nil] is returned func (v *verifier) standardBlock( b *block.ApricotStandardBlock, - feeCalculator fee.Calculator, + feeCalculator txfee.Calculator, onAcceptState state.Diff, ) error { inputs, atomicRequests, onAcceptFunc, err := v.processStandardTxs(b.Transactions, feeCalculator, onAcceptState, b.Parent()) @@ -448,19 +450,42 @@ func (v *verifier) standardBlock( return nil } -func (v *verifier) processStandardTxs(txs []*txs.Tx, feeCalculator fee.Calculator, state state.Diff, parentID ids.ID) ( +func (v *verifier) processStandardTxs( + txs []*txs.Tx, + feeCalculator txfee.Calculator, + state state.Diff, + parentID ids.ID, +) ( set.Set[ids.ID], map[ids.ID]*atomic.Requests, func(), error, ) { + timestamp := state.GetTimestamp() + isEtnaActivated := v.txExecutorBackend.Config.UpgradeConfig.IsEActivated(timestamp) + var ( - onAcceptFunc func() - inputs set.Set[ids.ID] - funcs = make([]func(), 0, len(txs)) - atomicRequests = make(map[ids.ID]*atomic.Requests) + blockComplexity feecomponent.Dimensions + onAcceptFunc func() + inputs set.Set[ids.ID] + funcs = make([]func(), 0, len(txs)) + atomicRequests = make(map[ids.ID]*atomic.Requests) ) for _, tx := range txs { + if isEtnaActivated { + txComplexity, err := txfee.TxComplexity(tx.Unsigned) + if err != nil { + txID := tx.ID() + v.MarkDropped(txID, err) // cache tx as dropped + return nil, nil, nil, err + } + + blockComplexity, err = blockComplexity.Add(&txComplexity) + if err != nil { + return nil, nil, nil, err + } + } + txExecutor := executor.StandardTxExecutor{ Backend: v.txExecutorBackend, State: state, @@ -497,6 +522,21 @@ func (v *verifier) processStandardTxs(txs []*txs.Tx, feeCalculator fee.Calculato } } + if isEtnaActivated { + blockGas, err := blockComplexity.ToGas(v.txExecutorBackend.Config.DynamicFeeConfig.Weights) + if err != nil { + return nil, nil, nil, err + } + + feeComplexity := state.GetFeeComplexity() + feeComplexity, err = feeComplexity.ConsumeGas(blockGas) + if err != nil { + return nil, nil, nil, err + } + + state.SetFeeComplexity(feeComplexity) + } + if err := v.verifyUniqueInputs(parentID, inputs); err != nil { return nil, nil, nil, err } diff --git a/vms/platformvm/config/config.go b/vms/platformvm/config/config.go index ee457047e5a..61dedea3ff2 100644 --- a/vms/platformvm/config/config.go +++ b/vms/platformvm/config/config.go @@ -14,8 +14,10 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/reward" "github.com/ava-labs/avalanchego/vms/platformvm/txs" - "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" "github.com/ava-labs/avalanchego/vms/platformvm/upgrade" + + feecomponent "github.com/ava-labs/avalanchego/vms/components/fee" + txfee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" ) // Struct collecting all foundational parameters of PlatformVM @@ -31,9 +33,12 @@ type Config struct { // calling VM.Initialize. Validators validators.Manager - // All static fees config active before E-upgrade + // Static fees are active before the E-upgrade CreateAssetTxFee uint64 // Override for CreateSubnet and CreateChain before AP3 - StaticFeeConfig fee.StaticConfig + StaticFeeConfig txfee.StaticConfig + + // Dynamic fees are active after the E-upgrade + DynamicFeeConfig feecomponent.Config // Provides access to the uptime manager as a thread safe data structure UptimeLockedCalculator uptime.LockedCalculator diff --git a/vms/platformvm/state/chain_time_helpers.go b/vms/platformvm/state/chain_time_helpers.go index 7b5dd6ebe10..14577ac5d28 100644 --- a/vms/platformvm/state/chain_time_helpers.go +++ b/vms/platformvm/state/chain_time_helpers.go @@ -77,7 +77,19 @@ func GetNextStakerChangeTime(state Chain) (time.Time, error) { // PickFeeCalculator does not modify [state]. func PickFeeCalculator(cfg *config.Config, state Chain) fee.Calculator { timestamp := state.GetTimestamp() - return NewStaticFeeCalculator(cfg, timestamp) + if !cfg.UpgradeConfig.IsEActivated(timestamp) { + return NewStaticFeeCalculator(cfg, timestamp) + } + + feeComplexity := state.GetFeeComplexity() + gasPrice := cfg.DynamicFeeConfig.MinGasPrice.MulExp( + feeComplexity.Excess, + cfg.DynamicFeeConfig.ExcessConversionConstant, + ) + return fee.NewDynamicCalculator( + cfg.DynamicFeeConfig.Weights, + gasPrice, + ) } // NewStaticFeeCalculator creates a static fee calculator, with the config set diff --git a/vms/platformvm/state/diff.go b/vms/platformvm/state/diff.go index 91fb01d08fc..924f4171147 100644 --- a/vms/platformvm/state/diff.go +++ b/vms/platformvm/state/diff.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/fee" "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" @@ -34,6 +35,7 @@ type diff struct { stateVersions Versions timestamp time.Time + feeState fee.State // Subnet ID --> supply of native asset of the subnet currentSupply map[ids.ID]uint64 @@ -71,6 +73,7 @@ func NewDiff( parentID: parentID, stateVersions: stateVersions, timestamp: parentState.GetTimestamp(), + feeState: parentState.GetFeeComplexity(), subnetOwners: make(map[ids.ID]fx.Owner), }, nil } @@ -97,6 +100,14 @@ func (d *diff) SetTimestamp(timestamp time.Time) { d.timestamp = timestamp } +func (d *diff) GetFeeComplexity() fee.State { + return d.feeState +} + +func (d *diff) SetFeeComplexity(feeState fee.State) { + d.feeState = feeState +} + func (d *diff) GetCurrentSupply(subnetID ids.ID) (uint64, error) { supply, ok := d.currentSupply[subnetID] if ok { @@ -401,6 +412,7 @@ func (d *diff) DeleteUTXO(utxoID ids.ID) { func (d *diff) Apply(baseState Chain) error { baseState.SetTimestamp(d.timestamp) + baseState.SetFeeComplexity(d.feeState) for subnetID, supply := range d.currentSupply { baseState.SetCurrentSupply(subnetID, supply) } diff --git a/vms/platformvm/state/mock_state.go b/vms/platformvm/state/mock_state.go index c1321567e6a..4333ceff81d 100644 --- a/vms/platformvm/state/mock_state.go +++ b/vms/platformvm/state/mock_state.go @@ -20,6 +20,7 @@ import ( validators "github.com/ava-labs/avalanchego/snow/validators" logging "github.com/ava-labs/avalanchego/utils/logging" avax "github.com/ava-labs/avalanchego/vms/components/avax" + fee "github.com/ava-labs/avalanchego/vms/components/fee" block "github.com/ava-labs/avalanchego/vms/platformvm/block" fx "github.com/ava-labs/avalanchego/vms/platformvm/fx" status "github.com/ava-labs/avalanchego/vms/platformvm/status" @@ -257,6 +258,20 @@ func (mr *MockChainMockRecorder) GetDelegateeReward(arg0, arg1 any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDelegateeReward", reflect.TypeOf((*MockChain)(nil).GetDelegateeReward), arg0, arg1) } +// GetFeeComplexity mocks base method. +func (m *MockChain) GetFeeComplexity() fee.State { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFeeComplexity") + ret0, _ := ret[0].(fee.State) + return ret0 +} + +// GetFeeComplexity indicates an expected call of GetFeeComplexity. +func (mr *MockChainMockRecorder) GetFeeComplexity() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeeComplexity", reflect.TypeOf((*MockChain)(nil).GetFeeComplexity)) +} + // GetPendingDelegatorIterator mocks base method. func (m *MockChain) GetPendingDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { m.ctrl.T.Helper() @@ -451,6 +466,18 @@ func (mr *MockChainMockRecorder) SetDelegateeReward(arg0, arg1, arg2 any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDelegateeReward", reflect.TypeOf((*MockChain)(nil).SetDelegateeReward), arg0, arg1, arg2) } +// SetFeeComplexity mocks base method. +func (m *MockChain) SetFeeComplexity(arg0 fee.State) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetFeeComplexity", arg0) +} + +// SetFeeComplexity indicates an expected call of SetFeeComplexity. +func (mr *MockChainMockRecorder) SetFeeComplexity(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeeComplexity", reflect.TypeOf((*MockChain)(nil).SetFeeComplexity), arg0) +} + // SetSubnetOwner mocks base method. func (m *MockChain) SetSubnetOwner(arg0 ids.ID, arg1 fx.Owner) { m.ctrl.T.Helper() @@ -719,6 +746,20 @@ func (mr *MockDiffMockRecorder) GetDelegateeReward(arg0, arg1 any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDelegateeReward", reflect.TypeOf((*MockDiff)(nil).GetDelegateeReward), arg0, arg1) } +// GetFeeComplexity mocks base method. +func (m *MockDiff) GetFeeComplexity() fee.State { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFeeComplexity") + ret0, _ := ret[0].(fee.State) + return ret0 +} + +// GetFeeComplexity indicates an expected call of GetFeeComplexity. +func (mr *MockDiffMockRecorder) GetFeeComplexity() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeeComplexity", reflect.TypeOf((*MockDiff)(nil).GetFeeComplexity)) +} + // GetPendingDelegatorIterator mocks base method. func (m *MockDiff) GetPendingDelegatorIterator(arg0 ids.ID, arg1 ids.NodeID) (StakerIterator, error) { m.ctrl.T.Helper() @@ -913,6 +954,18 @@ func (mr *MockDiffMockRecorder) SetDelegateeReward(arg0, arg1, arg2 any) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDelegateeReward", reflect.TypeOf((*MockDiff)(nil).SetDelegateeReward), arg0, arg1, arg2) } +// SetFeeComplexity mocks base method. +func (m *MockDiff) SetFeeComplexity(arg0 fee.State) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetFeeComplexity", arg0) +} + +// SetFeeComplexity indicates an expected call of SetFeeComplexity. +func (mr *MockDiffMockRecorder) SetFeeComplexity(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeeComplexity", reflect.TypeOf((*MockDiff)(nil).SetFeeComplexity), arg0) +} + // SetSubnetOwner mocks base method. func (m *MockDiff) SetSubnetOwner(arg0 ids.ID, arg1 fx.Owner) { m.ctrl.T.Helper() @@ -1306,6 +1359,20 @@ func (mr *MockStateMockRecorder) GetDelegateeReward(arg0, arg1 any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDelegateeReward", reflect.TypeOf((*MockState)(nil).GetDelegateeReward), arg0, arg1) } +// GetFeeComplexity mocks base method. +func (m *MockState) GetFeeComplexity() fee.State { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFeeComplexity") + ret0, _ := ret[0].(fee.State) + return ret0 +} + +// GetFeeComplexity indicates an expected call of GetFeeComplexity. +func (mr *MockStateMockRecorder) GetFeeComplexity() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeeComplexity", reflect.TypeOf((*MockState)(nil).GetFeeComplexity)) +} + // GetLastAccepted mocks base method. func (m *MockState) GetLastAccepted() ids.ID { m.ctrl.T.Helper() @@ -1604,6 +1671,18 @@ func (mr *MockStateMockRecorder) SetDelegateeReward(arg0, arg1, arg2 any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDelegateeReward", reflect.TypeOf((*MockState)(nil).SetDelegateeReward), arg0, arg1, arg2) } +// SetFeeComplexity mocks base method. +func (m *MockState) SetFeeComplexity(arg0 fee.State) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetFeeComplexity", arg0) +} + +// SetFeeComplexity indicates an expected call of SetFeeComplexity. +func (mr *MockStateMockRecorder) SetFeeComplexity(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeeComplexity", reflect.TypeOf((*MockState)(nil).SetFeeComplexity), arg0) +} + // SetHeight mocks base method. func (m *MockState) SetHeight(arg0 uint64) { m.ctrl.T.Helper() diff --git a/vms/platformvm/state/state.go b/vms/platformvm/state/state.go index a6266a48085..928293bea92 100644 --- a/vms/platformvm/state/state.go +++ b/vms/platformvm/state/state.go @@ -33,6 +33,7 @@ import ( "github.com/ava-labs/avalanchego/utils/timer" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/fee" "github.com/ava-labs/avalanchego/vms/platformvm/block" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/fx" @@ -98,6 +99,9 @@ type Chain interface { GetTimestamp() time.Time SetTimestamp(tm time.Time) + GetFeeComplexity() fee.State + SetFeeComplexity(f fee.State) + GetCurrentSupply(subnetID ids.ID) (uint64, error) SetCurrentSupply(subnetID ids.ID, cs uint64) @@ -353,6 +357,7 @@ type state struct { // The persisted fields represent the current database value timestamp, persistedTimestamp time.Time + feeState, persistedFeeState fee.State currentSupply, persistedCurrentSupply uint64 // [lastAccepted] is the most recently accepted block. lastAccepted, persistedLastAccepted ids.ID @@ -1005,6 +1010,14 @@ func (s *state) SetTimestamp(tm time.Time) { s.timestamp = tm } +func (s *state) GetFeeComplexity() fee.State { + return s.feeState +} + +func (s *state) SetFeeComplexity(feeState fee.State) { + s.feeState = feeState +} + func (s *state) GetLastAccepted() ids.ID { return s.lastAccepted } @@ -1287,6 +1300,8 @@ func (s *state) loadMetadata() error { s.persistedTimestamp = timestamp s.SetTimestamp(timestamp) + // TODO: Load fee state + currentSupply, err := database.GetUInt64(s.singletonDB, CurrentSupplyKey) if err != nil { return err @@ -2261,6 +2276,10 @@ func (s *state) writeMetadata() error { } s.persistedTimestamp = s.timestamp } + if s.feeState != s.persistedFeeState { + // TODO: Write new fee state + s.persistedFeeState = s.feeState + } if s.persistedCurrentSupply != s.currentSupply { if err := database.PutUInt64(s.singletonDB, CurrentSupplyKey, s.currentSupply); err != nil { return fmt.Errorf("failed to write current supply: %w", err) diff --git a/vms/platformvm/txs/executor/state_changes.go b/vms/platformvm/txs/executor/state_changes.go index 3086358304a..c81a46a65df 100644 --- a/vms/platformvm/txs/executor/state_changes.go +++ b/vms/platformvm/txs/executor/state_changes.go @@ -165,12 +165,22 @@ func AdvanceTimeTo( changed = true } - if err := changes.Apply(parentState); err != nil { - return false, err + if backend.Config.UpgradeConfig.IsEActivated(newChainTime) { + previousChainTime := changes.GetTimestamp() + duration := uint64(newChainTime.Sub(previousChainTime) / time.Second) + + feeComplexity := changes.GetFeeComplexity() + feeComplexity = feeComplexity.AdvanceTime( + backend.Config.DynamicFeeConfig.MaxGasCapacity, + backend.Config.DynamicFeeConfig.MaxGasPerSecond, + backend.Config.DynamicFeeConfig.TargetGasPerSecond, + duration, + ) + changes.SetFeeComplexity(feeComplexity) } - parentState.SetTimestamp(newChainTime) - return changed, nil + changes.SetTimestamp(newChainTime) + return changed, changes.Apply(parentState) } func GetRewardsCalculator(