Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion tests/e2e/p/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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)
})
Expand Down
76 changes: 41 additions & 35 deletions vms/components/fee/calculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand All @@ -46,6 +52,7 @@ func NewUpdatedManager(
parentBlkTime, childBlkTime time.Time,
) (*Calculator, error) {
res := &Calculator{
feeWeights: feesConfig.FeeDimensionWeights,
gasCap: gasCap,
currentExcessGas: currentExcessGas,
}
Expand Down Expand Up @@ -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
}

Expand Down
25 changes: 21 additions & 4 deletions vms/components/fee/dimensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
5 changes: 1 addition & 4 deletions vms/components/fee/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)))

Expand Down
16 changes: 12 additions & 4 deletions vms/platformvm/block/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
30 changes: 25 additions & 5 deletions vms/platformvm/block/executor/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -491,7 +507,7 @@ func (v *verifier) standardBlock(

timestamp: onAcceptState.GetTimestamp(),
blockGas: blkGas,
excessGas: feeCalculator.GetExcessGas(),
excessGas: excessGas,
inputs: inputs,
atomicRequests: atomicRequests,
}
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions vms/platformvm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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...)
Expand Down
31 changes: 31 additions & 0 deletions vms/platformvm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"`
Expand Down
2 changes: 1 addition & 1 deletion vms/platformvm/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
2 changes: 2 additions & 0 deletions vms/platformvm/txs/executor/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions vms/platformvm/txs/fee/calculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading