diff --git a/tests/e2e/p/workflow.go b/tests/e2e/p/workflow.go index 35dcba1d7976..459aac1c69e9 100644 --- a/tests/e2e/p/workflow.go +++ b/tests/e2e/p/workflow.go @@ -57,6 +57,11 @@ var _ = e2e.DescribePChain("[Workflow]", func() { tests.Outf("{{green}} minimal validator stake: %d {{/}}\n", minValStake) tests.Outf("{{green}} minimal delegator stake: %d {{/}}\n", minDelStake) + tests.Outf("{{blue}} fetching minimal stake amounts {{/}}\n") + feeCfg, err := pChainClient.GetDynamicFeeConfig(e2e.DefaultContext()) + require.NoError(err) + tests.Outf("{{green}} fee config: %v {{/}}\n", feeCfg) + tests.Outf("{{blue}} fetching X-chain tx fee {{/}}\n") infoClient := info.NewClient(nodeURI.URI) staticFees, err := infoClient.GetTxFee(e2e.DefaultContext()) @@ -159,7 +164,7 @@ var _ = e2e.DescribePChain("[Workflow]", func() { require.NoError(err) // retrieve fees paid for the tx - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(nextGasPrice, nextGasCap)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(feeCfg.FeeDimensionWeights, nextGasPrice, nextGasCap)) pChainExportFee, err = feeCalc.CalculateFee(tx) require.NoError(err) }) diff --git a/vms/components/fee/calculator.go b/vms/components/fee/calculator.go index 2a43e233186c..56ad5248b4a4 100644 --- a/vms/components/fee/calculator.go +++ b/vms/components/fee/calculator.go @@ -18,25 +18,31 @@ var errGasBoundBreached = errors.New("gas bound breached") // Calculator performs fee-related operations that are share move P-chain and X-chain // Calculator is supposed to be embedded with chain specific calculators. type Calculator struct { + // feeWeights help consolidating complexity into gas + feeWeights Dimensions + // gas cap enforced with adding gas via CumulateGas gasCap Gas // Avax denominated gas price, i.e. fee per unit of complexity. gasPrice GasPrice - // blockGas helps aggregating the gas consumed in a single block + // blkComplexity helps aggregating the gas consumed in a single block // so that we can verify it's not too big/build it properly. - blockGas Gas + blkComplexity Dimensions + + gasReminder Gas // currentExcessGas stores current excess gas, cumulated over time // to be updated once a block is accepted with cumulatedGas currentExcessGas Gas } -func NewCalculator(gasPrice GasPrice, gasCap Gas) *Calculator { +func NewCalculator(feeWeights Dimensions, gasPrice GasPrice, gasCap Gas) *Calculator { return &Calculator{ - gasCap: gasCap, - gasPrice: gasPrice, + feeWeights: feeWeights, + gasCap: gasCap, + gasPrice: gasPrice, } } @@ -46,6 +52,7 @@ func NewUpdatedManager( parentBlkTime, childBlkTime time.Time, ) (*Calculator, error) { res := &Calculator{ + feeWeights: feesConfig.FeeDimensionWeights, gasCap: gasCap, currentExcessGas: currentExcessGas, } @@ -82,60 +89,59 @@ func (c *Calculator) GetGasPrice() GasPrice { return c.gasPrice } -func (c *Calculator) GetBlockGas() Gas { - return c.blockGas +func (c *Calculator) GetBlockGas() (Gas, error) { + return ToGas(c.feeWeights, c.blkComplexity) } func (c *Calculator) GetGasCap() Gas { return c.gasCap } -func (c *Calculator) GetExcessGas() Gas { - return c.currentExcessGas +func (c *Calculator) GetExcessGas() (Gas, error) { + blkGas, err := c.GetBlockGas() + if err != nil { + return ZeroGas, err + } + + g, err := safemath.Add64(uint64(c.currentExcessGas), uint64(blkGas)) + if err != nil { + return ZeroGas, err + } + return Gas(g), nil } // CalculateFee must be a stateless method -func (c *Calculator) CalculateFee(g Gas) (uint64, error) { - return safemath.Mul64(uint64(c.gasPrice), uint64(g)) +func (c *Calculator) CalculateFee(complexity Dimensions) (uint64, error) { + gas, reminder, err := toGasWithReminder(c.feeWeights, complexity, c.gasReminder) + if err != nil { + return 0, err + } + c.gasReminder = reminder + + return safemath.Mul64(uint64(c.gasPrice), uint64(gas)) } -// CumulateGas tries to cumulate the consumed gas [units]. Before +// CumulateComplexity tries to cumulate the consumed gas [units]. Before // actually cumulating it, it checks whether the result would breach [bounds]. // If so, it returns the first dimension to breach bounds. -func (c *Calculator) CumulateGas(gas Gas) error { +func (c *Calculator) CumulateComplexity(complexity Dimensions) error { // Ensure we can consume (don't want partial update of values) - blkGas, err := safemath.Add64(uint64(c.blockGas), uint64(gas)) + blkComplexity, err := Add(c.blkComplexity, complexity) if err != nil { return fmt.Errorf("%w: %w", errGasBoundBreached, err) } - if Gas(blkGas) > c.gasCap { - return errGasBoundBreached - } - - excessGas, err := safemath.Add64(uint64(c.currentExcessGas), uint64(gas)) - if err != nil { - return fmt.Errorf("%w: %w", errGasBoundBreached, err) - } - - c.blockGas = Gas(blkGas) - c.currentExcessGas = Gas(excessGas) + c.blkComplexity = blkComplexity return nil } // Sometimes, e.g. while building a tx, we'd like freedom to speculatively add complexity // and to remove it later on. [RemoveGas] grants this freedom -func (c *Calculator) RemoveGas(gasToRm Gas) error { - rBlkdGas, err := safemath.Sub(c.blockGas, gasToRm) - if err != nil { - return fmt.Errorf("%w: current Gas %d, gas to revert %d", err, c.blockGas, gasToRm) - } - rExcessGas, err := safemath.Sub(c.currentExcessGas, gasToRm) +func (c *Calculator) RemoveComplexity(complexity Dimensions) error { + rBlkdComplexity, err := Remove(c.blkComplexity, complexity) if err != nil { - return fmt.Errorf("%w: current Excess gas %d, gas to revert %d", err, c.currentExcessGas, gasToRm) + return fmt.Errorf("%w: current Gas %d, gas to revert %d", err, c.blkComplexity, complexity) } - - c.blockGas = rBlkdGas - c.currentExcessGas = rExcessGas + c.blkComplexity = rBlkdComplexity return nil } diff --git a/vms/components/fee/dimensions.go b/vms/components/fee/dimensions.go index 7c5e14f785e2..8e4decea61e2 100644 --- a/vms/components/fee/dimensions.go +++ b/vms/components/fee/dimensions.go @@ -42,17 +42,34 @@ func Add(lhs, rhs Dimensions) (Dimensions, error) { return res, nil } +func Remove(lhs, rhs Dimensions) (Dimensions, error) { + var res Dimensions + for i := 0; i < FeeDimensions; i++ { + v, err := safemath.Sub(lhs[i], rhs[i]) + if err != nil { + return res, err + } + res[i] = v + } + return res, nil +} + func ToGas(weights, dimensions Dimensions) (Gas, error) { - var res uint64 + gas, _, err := toGasWithReminder(weights, dimensions, ZeroGas) + return gas, err +} + +func toGasWithReminder(weights, dimensions Dimensions, gasReminder Gas) (Gas, Gas, error) { + res := uint64(gasReminder) for i := 0; i < FeeDimensions; i++ { v, err := safemath.Mul64(weights[i], dimensions[i]) if err != nil { - return ZeroGas, err + return ZeroGas, ZeroGas, err } res, err = safemath.Add64(res, v) if err != nil { - return ZeroGas, err + return ZeroGas, ZeroGas, err } } - return Gas(res), nil + return Gas(res) / 10, Gas(res) % 10, nil } diff --git a/vms/components/fee/manager_test.go b/vms/components/fee/manager_test.go index 15b4896b7df3..280063c64df2 100644 --- a/vms/components/fee/manager_test.go +++ b/vms/components/fee/manager_test.go @@ -155,10 +155,7 @@ func TestPChainGasPriceIncreaseDueToPeak(t *testing.T) { ) // at peak the total fee should be no more than 100 Avax. - childGas, err := ToGas(testDynamicFeeCfg.FeeDimensionWeights, childBlkData.complexity) - require.NoError(err) - - fee, err := m.CalculateFee(childGas) + fee, err := m.CalculateFee(childBlkData.complexity) require.NoError(err) require.Less(fee, 100*units.Avax, fmt.Sprintf("iteration: %d, total: %d", i, len(blockComplexities))) diff --git a/vms/platformvm/block/builder/builder.go b/vms/platformvm/block/builder/builder.go index 15c851f4117a..7501e2dfb6c2 100644 --- a/vms/platformvm/block/builder/builder.go +++ b/vms/platformvm/block/builder/builder.go @@ -375,10 +375,18 @@ func packBlockTxs( // pre e upgrade is active, we fill blocks till a target size // post e upgrade is active, we fill blocks till a target gas - targetSizeReached := (!isEActive && txSize > remainingSize) || - (isEActive && feeCalculator.GetBlockGas() >= gasCap) - if targetSizeReached { - break + if !isEActive { + if txSize > remainingSize { + break + } + } else { + blkGas, err := feeCalculator.GetBlockGas() + if err != nil { + return nil, err + } + if blkGas >= gasCap { + break + } } mempool.Remove(tx) diff --git a/vms/platformvm/block/executor/verifier.go b/vms/platformvm/block/executor/verifier.go index 0d8b13899e17..ff0295998442 100644 --- a/vms/platformvm/block/executor/verifier.go +++ b/vms/platformvm/block/executor/verifier.go @@ -421,7 +421,15 @@ func (v *verifier) proposalBlock( return err } - blkGas := feeCalculator.GetBlockGas() + blkGas, err := feeCalculator.GetBlockGas() + if err != nil { + return err + } + excessGas, err := feeCalculator.GetExcessGas() + if err != nil { + return err + } + if feeCalculator.IsEActive() { nextGasCap := commonfee.UpdateGasCap(currentGasCap, blkGas) onCommitState.SetCurrentGasCap(nextGasCap) @@ -451,7 +459,7 @@ func (v *verifier) proposalBlock( // always be the same as the Banff Proposal Block. timestamp: onAbortState.GetTimestamp(), blockGas: blkGas, - excessGas: feeCalculator.GetExcessGas(), + excessGas: excessGas, atomicRequests: atomicRequests, } return nil @@ -477,7 +485,15 @@ func (v *verifier) standardBlock( blkID := b.ID() - blkGas := feeCalculator.GetBlockGas() + blkGas, err := feeCalculator.GetBlockGas() + if err != nil { + return err + } + excessGas, err := feeCalculator.GetExcessGas() + if err != nil { + return err + } + if feeCalculator.IsEActive() { nextGasCap := commonfee.UpdateGasCap(currentGasCap, blkGas) onAcceptState.SetCurrentGasCap(nextGasCap) @@ -491,7 +507,7 @@ func (v *verifier) standardBlock( timestamp: onAcceptState.GetTimestamp(), blockGas: blkGas, - excessGas: feeCalculator.GetExcessGas(), + excessGas: excessGas, inputs: inputs, atomicRequests: atomicRequests, } @@ -558,7 +574,11 @@ func (v *verifier) processStandardTxs( } if v.txExecutorBackend.Config.UpgradeConfig.IsEActivated(state.GetTimestamp()) { - state.SetExcessGas(feeCalculator.GetExcessGas()) + excessGas, err := feeCalculator.GetExcessGas() + if err != nil { + return nil, nil, nil, err + } + state.SetExcessGas(excessGas) } if numFuncs := len(funcs); numFuncs == 1 { diff --git a/vms/platformvm/client.go b/vms/platformvm/client.go index 5e2fbf72f16e..49369b7c9af9 100644 --- a/vms/platformvm/client.go +++ b/vms/platformvm/client.go @@ -123,6 +123,9 @@ type Client interface { GetBlock(ctx context.Context, blockID ids.ID, options ...rpc.Option) ([]byte, error) // GetBlockByHeight returns the block at the given [height]. GetBlockByHeight(ctx context.Context, height uint64, options ...rpc.Option) ([]byte, error) + + // GetDynamicFeeConfig returns DynamicFeesConfig + GetDynamicFeeConfig(ctx context.Context, options ...rpc.Option) (commonfee.DynamicFeesConfig, error) // GetNextGasData returns the gas price that a transaction must pay to be accepted now // and the gas cap, i.e. the maximum gas a transactions can consume GetNextGasData(ctx context.Context, options ...rpc.Option) (commonfee.GasPrice, commonfee.Gas, error) @@ -549,6 +552,12 @@ func AwaitTxAccepted( } } +func (c *client) GetDynamicFeeConfig(ctx context.Context, options ...rpc.Option) (commonfee.DynamicFeesConfig, error) { + res := &DynamicFeesConfigReply{} + err := c.requester.SendRequest(ctx, "platform.getDynamicFeeConfig", struct{}{}, res, options...) + return res.DynamicFeesConfig, err +} + func (c *client) GetNextGasData(ctx context.Context, options ...rpc.Option) (commonfee.GasPrice, commonfee.Gas, error) { res := &GetGasPriceReply{} err := c.requester.SendRequest(ctx, "platform.getNextGasData", struct{}{}, res, options...) diff --git a/vms/platformvm/service.go b/vms/platformvm/service.go index deefaafad2d2..d0cd3da96f53 100644 --- a/vms/platformvm/service.go +++ b/vms/platformvm/service.go @@ -36,6 +36,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" "github.com/ava-labs/avalanchego/vms/secp256k1fx" avajson "github.com/ava-labs/avalanchego/utils/json" @@ -1829,6 +1830,36 @@ func (s *Service) GetBlockByHeight(_ *http.Request, args *api.GetBlockByHeightAr return err } +// DynamicFeesConfigReply is the response from GetDynamicFeeConfig +type DynamicFeesConfigReply struct { + commonfee.DynamicFeesConfig `json:"nextGasPrice"` +} + +// GetNextFeeRates returns the next fee rates that a transaction must pay to be accepted now +func (s *Service) GetDynamicFeeConfig(_ *http.Request, _ *struct{}, reply *DynamicFeesConfigReply) error { + s.vm.ctx.Log.Debug("API called", + zap.String("service", "platform"), + zap.String("method", "getDynamicFeeConfig"), + ) + + s.vm.ctx.Lock.Lock() + defer s.vm.ctx.Lock.Unlock() + + chainTime := s.vm.state.GetTimestamp() + isEActive := s.vm.Config.UpgradeConfig.IsEActivated(chainTime) + cfg, err := fee.GetDynamicConfig(isEActive) + switch err { + case fee.ErrDynamicFeeConfigNotAvailable: + reply.DynamicFeesConfig = commonfee.DynamicFeesConfig{} + return nil + case nil: + reply.DynamicFeesConfig = cfg + return nil + default: + return err + } +} + // GetGasPriceReply is the response from GetFeeRates type GetGasPriceReply struct { NextGasPrice commonfee.GasPrice `json:"nextGasPrice"` diff --git a/vms/platformvm/service_test.go b/vms/platformvm/service_test.go index 15c0903072a9..634a1768af4d 100644 --- a/vms/platformvm/service_test.go +++ b/vms/platformvm/service_test.go @@ -1223,7 +1223,7 @@ func TestGetFeeRates(t *testing.T) { // let time tick. Fee rates will go down service.vm.ctx.Lock.Lock() - now = now.Add(3 * time.Second) + now = now.Add(10 * time.Second) service.vm.clock.Set(now) service.vm.ctx.Lock.Unlock() diff --git a/vms/platformvm/txs/executor/helpers_test.go b/vms/platformvm/txs/executor/helpers_test.go index 56f20b546b8e..30f28865f0e5 100644 --- a/vms/platformvm/txs/executor/helpers_test.go +++ b/vms/platformvm/txs/executor/helpers_test.go @@ -233,6 +233,8 @@ func addSubnet(t *testing.T, env *environment) { stateDiff, err := state.NewDiff(lastAcceptedID, env) require.NoError(err) + _, _, feeCalc, err = env.factory.NewWallet(preFundedKeys[0]) + require.NoError(err) executor := StandardTxExecutor{ Backend: &env.backend, FeeCalculator: feeCalc, diff --git a/vms/platformvm/txs/fee/calculator.go b/vms/platformvm/txs/fee/calculator.go index 1b4fd2b6de76..fe544d625e2a 100644 --- a/vms/platformvm/txs/fee/calculator.go +++ b/vms/platformvm/txs/fee/calculator.go @@ -18,8 +18,8 @@ type Calculator interface { AddFeesFor(complexity fee.Dimensions) (uint64, error) RemoveFeesFor(unitsToRm fee.Dimensions) (uint64, error) GetGasPrice() fee.GasPrice - GetBlockGas() fee.Gas - GetExcessGas() fee.Gas + GetBlockGas() (fee.Gas, error) + GetExcessGas() (fee.Gas, error) GetGasCap() fee.Gas setCredentials(creds []verify.Verifiable) IsEActive() bool diff --git a/vms/platformvm/txs/fee/calculator_test.go b/vms/platformvm/txs/fee/calculator_test.go index 154ce7f5ae0a..3fe578df7220 100644 --- a/vms/platformvm/txs/fee/calculator_test.go +++ b/vms/platformvm/txs/fee/calculator_test.go @@ -29,8 +29,8 @@ import ( ) var ( - testGasPrice = fee.GasPrice(10 * units.NanoAvax) - + testFeeWeights = fee.Dimensions{1, 1, 1, 1} + testGasPrice = fee.GasPrice(10 * units.NanoAvax) testBlockMaxGas = fee.Gas(100_000) preFundedKeys = secp256k1.TestKeys() @@ -43,29 +43,36 @@ var ( func TestAddAndRemoveFees(t *testing.T) { r := require.New(t) - fc := NewDynamicCalculator(fee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := NewDynamicCalculator(fee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) var ( units = fee.Dimensions{1, 2, 3, 4} - gas = fee.Gas(10) - doubleGas = fee.Gas(20) + gas = fee.Gas(1) + doubleGas = fee.Gas(2) ) feeDelta, err := fc.AddFeesFor(units) r.NoError(err) - r.Equal(gas, fc.GetBlockGas()) + + haveGas, err := fc.GetBlockGas() + r.NoError(err) + r.Equal(gas, haveGas) r.NotZero(feeDelta) r.Equal(feeDelta, fc.GetFee()) feeDelta2, err := fc.AddFeesFor(units) r.NoError(err) - r.Equal(doubleGas, fc.GetBlockGas()) + haveGas, err = fc.GetBlockGas() + r.NoError(err) + r.Equal(doubleGas, haveGas) r.Equal(feeDelta, feeDelta2) r.Equal(feeDelta+feeDelta2, fc.GetFee()) feeDelta3, err := fc.RemoveFeesFor(units) r.NoError(err) - r.Equal(gas, fc.GetBlockGas()) + haveGas, err = fc.GetBlockGas() + r.NoError(err) + r.Equal(gas, haveGas) r.Equal(feeDelta, feeDelta3) r.Equal(feeDelta, fc.GetFee()) @@ -141,8 +148,10 @@ func TestTxFees(t *testing.T) { expectedError: nil, unsignedAndSignedTx: addSubnetValidatorTx, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 29_110*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(2_911), c.GetBlockGas()) + require.Equal(t, 2_910*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(291), haveGas) }, }, { @@ -192,8 +201,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: createChainTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 19_540*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(1_954), c.GetBlockGas()) + require.Equal(t, 1_950*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(195), haveGas) }, }, { @@ -228,8 +239,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: createSubnetTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 18_590*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(1_859), c.GetBlockGas()) + require.Equal(t, 1_850*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(185), haveGas) }, }, { @@ -256,8 +269,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: removeSubnetValidatorTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 28_870*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(2_887), c.GetBlockGas()) + require.Equal(t, 2_880*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(288), haveGas) }, }, { @@ -284,8 +299,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: transformSubnetTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 19_720*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(1_972), c.GetBlockGas()) + require.Equal(t, 1_970*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(197), haveGas) }, }, { @@ -312,8 +329,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: transferSubnetOwnershipTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 19_030*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(1_903), c.GetBlockGas()) + require.Equal(t, 1_900*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(190), haveGas) }, }, { @@ -356,8 +375,10 @@ func TestTxFees(t *testing.T) { }, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 33_170*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(3_317), c.GetBlockGas()) + require.Equal(t, 3_310*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(331), haveGas) }, }, { @@ -370,8 +391,10 @@ func TestTxFees(t *testing.T) { }, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 33_170*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(3_317), c.GetBlockGas()) + require.Equal(t, 3_310*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(331), haveGas) }, }, { @@ -417,8 +440,10 @@ func TestTxFees(t *testing.T) { }, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 31_250*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(3_125), c.GetBlockGas()) + require.Equal(t, 3_120*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(312), haveGas) }, }, { @@ -429,8 +454,10 @@ func TestTxFees(t *testing.T) { }, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 31_250*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(3_125), c.GetBlockGas()) + require.Equal(t, 3_120*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(312), haveGas) }, }, { @@ -461,8 +488,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: baseTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 18_190*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(1_819), c.GetBlockGas()) + require.Equal(t, 1_810*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(181), haveGas) }, }, { @@ -489,8 +518,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: importTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 31_230*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(3_123), c.GetBlockGas()) + require.Equal(t, 3_120*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(312), haveGas) }, }, { @@ -517,8 +548,10 @@ func TestTxFees(t *testing.T) { unsignedAndSignedTx: exportTx, expectedError: nil, checksF: func(t *testing.T, c Calculator) { - require.Equal(t, 20_410*units.NanoAvax, c.GetFee()) - require.Equal(t, fee.Gas(2_041), c.GetBlockGas()) + require.Equal(t, 2_040*units.NanoAvax, c.GetFee()) + haveGas, err := c.GetBlockGas() + require.NoError(t, err) + require.Equal(t, fee.Gas(204), haveGas) }, }, { @@ -594,7 +627,7 @@ func TestTxFees(t *testing.T) { if !upgrades.IsEActivated(tt.chainTime) { c = NewStaticCalculator(feeTestsDefaultCfg, upgrades, tt.chainTime) } else { - c = NewDynamicCalculator(fee.NewCalculator(testGasPrice, gasCap)) + c = NewDynamicCalculator(fee.NewCalculator(testFeeWeights, testGasPrice, gasCap)) } var creds []verify.Verifiable diff --git a/vms/platformvm/txs/fee/dynamic_calculator.go b/vms/platformvm/txs/fee/dynamic_calculator.go index 2fcbe530c5ee..ac2f9efa03df 100644 --- a/vms/platformvm/txs/fee/dynamic_calculator.go +++ b/vms/platformvm/txs/fee/dynamic_calculator.go @@ -52,20 +52,10 @@ func (c *dynamicCalculator) AddFeesFor(complexity fee.Dimensions) (uint64, error if complexity == fee.Empty { return 0, nil } - - feeCfg, err := GetDynamicConfig(true /*isEActive*/) - if err != nil { - return 0, fmt.Errorf("failed adding fees: %w", err) - } - txGas, err := fee.ToGas(feeCfg.FeeDimensionWeights, complexity) - if err != nil { - return 0, fmt.Errorf("failed adding fees: %w", err) - } - - if err := c.fc.CumulateGas(txGas); err != nil { + if err := c.fc.CumulateComplexity(complexity); err != nil { return 0, fmt.Errorf("failed cumulating complexity: %w", err) } - fee, err := c.fc.CalculateFee(txGas) + fee, err := c.fc.CalculateFee(complexity) if err != nil { return 0, fmt.Errorf("%w: %w", errFailedFeeCalculation, err) } @@ -78,20 +68,10 @@ func (c *dynamicCalculator) RemoveFeesFor(unitsToRm fee.Dimensions) (uint64, err if unitsToRm == fee.Empty { return 0, nil } - - feeCfg, err := GetDynamicConfig(true /*isEActive*/) - if err != nil { - return 0, fmt.Errorf("failed adding fees: %w", err) - } - txGas, err := fee.ToGas(feeCfg.FeeDimensionWeights, unitsToRm) - if err != nil { - return 0, fmt.Errorf("failed adding fees: %w", err) - } - - if err := c.fc.RemoveGas(txGas); err != nil { + if err := c.fc.RemoveComplexity(unitsToRm); err != nil { return 0, fmt.Errorf("failed removing units: %w", err) } - fee, err := c.fc.CalculateFee(txGas) + fee, err := c.fc.CalculateFee(unitsToRm) if err != nil { return 0, fmt.Errorf("%w: %w", errFailedFeeCalculation, err) } @@ -108,9 +88,9 @@ func (c *dynamicCalculator) ResetFee(newFee uint64) { func (c *dynamicCalculator) GetGasPrice() fee.GasPrice { return c.fc.GetGasPrice() } -func (c *dynamicCalculator) GetBlockGas() fee.Gas { return c.fc.GetBlockGas() } +func (c *dynamicCalculator) GetBlockGas() (fee.Gas, error) { return c.fc.GetBlockGas() } -func (c *dynamicCalculator) GetExcessGas() fee.Gas { return c.fc.GetExcessGas() } +func (c *dynamicCalculator) GetExcessGas() (fee.Gas, error) { return c.fc.GetExcessGas() } func (c *dynamicCalculator) GetGasCap() fee.Gas { return c.fc.GetGasCap() } diff --git a/vms/platformvm/txs/fee/dynamic_config.go b/vms/platformvm/txs/fee/dynamic_config.go index ca028031d5db..d7d3b7b928f3 100644 --- a/vms/platformvm/txs/fee/dynamic_config.go +++ b/vms/platformvm/txs/fee/dynamic_config.go @@ -15,7 +15,7 @@ import ( ) var ( - errDynamicFeeConfigNotAvailable = errors.New("dynamic fee config not available") + ErrDynamicFeeConfigNotAvailable = errors.New("dynamic fee config not available") eUpgradeDynamicFeesConfig = commonfee.DynamicFeesConfig{ MinGasPrice: commonfee.GasPrice(10 * units.NanoAvax), @@ -37,7 +37,7 @@ func init() { func GetDynamicConfig(isEActive bool) (commonfee.DynamicFeesConfig, error) { if !isEActive { - return commonfee.DynamicFeesConfig{}, errDynamicFeeConfigNotAvailable + return commonfee.DynamicFeesConfig{}, ErrDynamicFeeConfigNotAvailable } if customDynamicFeesConfig != nil { diff --git a/vms/platformvm/txs/fee/static_calculator.go b/vms/platformvm/txs/fee/static_calculator.go index 86cce9700356..5351aa02cb7a 100644 --- a/vms/platformvm/txs/fee/static_calculator.go +++ b/vms/platformvm/txs/fee/static_calculator.go @@ -67,9 +67,9 @@ func (*staticCalculator) RemoveFeesFor(fee.Dimensions) (uint64, error) { func (*staticCalculator) GetGasPrice() fee.GasPrice { return fee.ZeroGasPrice } -func (*staticCalculator) GetBlockGas() fee.Gas { return fee.ZeroGas } +func (*staticCalculator) GetBlockGas() (fee.Gas, error) { return fee.ZeroGas, nil } -func (*staticCalculator) GetExcessGas() fee.Gas { return fee.ZeroGas } +func (*staticCalculator) GetExcessGas() (fee.Gas, error) { return fee.ZeroGas, nil } func (*staticCalculator) GetGasCap() fee.Gas { return fee.ZeroGas } diff --git a/wallet/chain/p/builder_test.go b/wallet/chain/p/builder_test.go index 375c25508a61..e1fd8597b0f4 100644 --- a/wallet/chain/p/builder_test.go +++ b/wallet/chain/p/builder_test.go @@ -56,6 +56,7 @@ var ( } testStaticConfig = staticFeesConfigFromContext(testContext) + testFeeWeights = commonfee.Dimensions{1, 1, 1, 1} testGasPrice = commonfee.GasPrice(10 * units.MicroAvax) testBlockMaxGas = commonfee.Gas(100_000) ) @@ -95,7 +96,7 @@ func TestBaseTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewBaseTx( outputsToMove, feeCalc, @@ -105,10 +106,10 @@ func TestBaseTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(30_620*units.MicroAvax, fee) + require.Equal(3_060*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins @@ -199,17 +200,17 @@ func TestAddSubnetValidatorTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewAddSubnetValidatorTx(subnetValidator, feeCalc) require.NoError(err) tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(40_610*units.MicroAvax, fee) + require.Equal(4_060*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins @@ -287,7 +288,7 @@ func TestRemoveSubnetValidatorTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewRemoveSubnetValidatorTx( ids.GenerateTestNodeID(), subnetID, @@ -298,10 +299,10 @@ func TestRemoveSubnetValidatorTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(40_370*units.MicroAvax, fee) + require.Equal(4_030*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins @@ -389,7 +390,7 @@ func TestCreateChainTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewCreateChainTx( subnetID, genesisBytes, @@ -403,19 +404,19 @@ func TestCreateChainTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(31_040*units.MicroAvax, fee) + require.Equal(1_760*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins outs := utx.Outs - require.Len(ins, 2) + require.Len(ins, 1) require.Len(outs, 1) expectedConsumed := fee - consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() require.Equal(expectedConsumed, consumed) } @@ -491,7 +492,7 @@ func TestCreateSubnetTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewCreateSubnetTx( subnetOwner, feeCalc, @@ -501,19 +502,19 @@ func TestCreateSubnetTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(29_400*units.MicroAvax, fee) + require.Equal(1_590*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins outs := utx.Outs - require.Len(ins, 2) + require.Len(ins, 1) require.Len(outs, 1) expectedConsumed := fee - consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() require.Equal(expectedConsumed, consumed) } @@ -585,7 +586,7 @@ func TestTransferSubnetOwnershipTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewTransferSubnetOwnershipTx( subnetID, subnetOwner, @@ -596,19 +597,19 @@ func TestTransferSubnetOwnershipTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(30_570*units.MicroAvax, fee) + require.Equal(1_710*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins outs := utx.Outs - require.Len(ins, 2) + require.Len(ins, 1) require.Len(outs, 1) expectedConsumed := fee - consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() require.Equal(expectedConsumed, consumed) } @@ -675,7 +676,7 @@ func TestImportTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewImportTx( sourceChainID, importTo, @@ -686,21 +687,21 @@ func TestImportTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(42_770*units.MicroAvax, fee) + require.Equal(1_590*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins outs := utx.Outs importedIns := utx.ImportedInputs - require.Len(ins, 2) + require.Empty(ins) require.Len(importedIns, 1) require.Len(outs, 1) expectedConsumed := fee - consumed := importedIns[0].In.Amount() + ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() + consumed := importedIns[0].In.Amount() - outs[0].Out.Amount() require.Equal(expectedConsumed, consumed) } @@ -765,7 +766,7 @@ func TestExportTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewExportTx( subnetID, exportedOutputs, @@ -776,10 +777,10 @@ func TestExportTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(30_980*units.MicroAvax, fee) + require.Equal(3_090*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins @@ -867,7 +868,7 @@ func TestTransformSubnetTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewTransformSubnetTx( subnetID, subnetAssetID, @@ -890,10 +891,10 @@ func TestTransformSubnetTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(46_250*units.MicroAvax, fee) + require.Equal(4_620*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins @@ -989,7 +990,7 @@ func TestAddPermissionlessValidatorTx(t *testing.T) { require.NoError(err) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewAddPermissionlessValidatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -1011,10 +1012,10 @@ func TestAddPermissionlessValidatorTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(75_240*units.MicroAvax, fee) + require.Equal(7_520*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins @@ -1108,7 +1109,7 @@ func TestAddPermissionlessDelegatorTx(t *testing.T) { ) { // Post E-Upgrade - feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + feeCalc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) utx, err := builder.NewAddPermissionlessDelegatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -1127,10 +1128,10 @@ func TestAddPermissionlessDelegatorTx(t *testing.T) { tx, err := signer.SignUnsigned(stdcontext.Background(), s, utx) require.NoError(err) - fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testGasPrice, testBlockMaxGas)) + fc := fee.NewDynamicCalculator(commonfee.NewCalculator(testFeeWeights, testGasPrice, testBlockMaxGas)) fee, err := fc.CalculateFee(tx) require.NoError(err) - require.Equal(73_320*units.MicroAvax, fee) + require.Equal(7_330*units.MicroAvax, fee) // check UTXOs selection and fee financing ins := utx.Ins diff --git a/wallet/chain/p/wallet.go b/wallet/chain/p/wallet.go index 0b608c8db98d..c44fe2871af2 100644 --- a/wallet/chain/p/wallet.go +++ b/wallet/chain/p/wallet.go @@ -291,6 +291,7 @@ type wallet struct { isEForkActive bool staticFeesConfig fee.StaticConfig + feeCfg commonfee.DynamicFeesConfig gasPrice commonfee.GasPrice gasCap commonfee.Gas } @@ -632,7 +633,7 @@ func (w *wallet) feeCalculator(ctx *builder.Context, options ...common.Option) ( return fee.NewStaticCalculator(w.staticFeesConfig, upgrade.Config{}, time.Time{}), nil } - return fee.NewDynamicCalculator(commonfee.NewCalculator(w.gasPrice, w.gasCap)), nil + return fee.NewDynamicCalculator(commonfee.NewCalculator(w.feeCfg.FeeDimensionWeights, w.gasPrice, w.gasCap)), nil } func (w *wallet) refreshFeesData(ctx *builder.Context, options ...common.Option) error { @@ -646,7 +647,15 @@ func (w *wallet) refreshFeesData(ctx *builder.Context, options ...common.Option) return err } eUpgradeTime := version.GetEUpgradeTime(w.builder.Context().NetworkID) - w.isEForkActive = !chainTime.Before(eUpgradeTime) + isEForkActive := !chainTime.Before(eUpgradeTime) + + if !w.isEForkActive && isEForkActive { + w.feeCfg, err = w.client.GetDynamicFeeConfig(opsCtx) + if err != nil { + return err + } + } + w.isEForkActive = isEForkActive if !w.isEForkActive { w.staticFeesConfig = staticFeesConfigFromContext(ctx)