fee

package
v1.7.41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

Documentation

Overview

Package fee is the whole native fee model of a Lux service chain: the ADMISSION policy a VM declares at boot (policy.go) and the SETTLEMENT mechanism that meters, debits and burns the admitted fee during block execution (balance.go, meter.go, settle.go).

Admission is the half every user-facing chain must declare: a Policy whose MinTxFee is > 0, or the NoUserTxPolicy sentinel for committee-only chains, checked once by Validate. Settlement is the half the 2026-05 fee audit found missing: nothing could actually METER, DEBIT, and BURN a fee during block execution the way the C-Chain EVM does (evm/core/state_transition.go buyGas: balance check -> ErrInsufficientFunds -> SubBalance). Service chains charged "fees" that were unbacked integers a caller wrote into a JSON request — never settled against real on-chain balance.

This package supplies the three pillars those chains lacked, modelled on the EVM's buyGas but for the native account model (P/X-Chain style direct usage, not EVM-gas yet — that is a later dual-metering layer that composes on top):

  • Balances (balance.go) — a debitable balance surface the VM can Burn from. Burn(acct, amount) is the debit: it removes funds from the payer AND reduces circulating supply (no coinbase credit) — i.e. a native burn. Credit funds an account (genesis / future treasury inflows). Ledger (ledger.go) is the canonical KV-backed implementation; any chain whose state is a luxfi/database.Database gets a working ledger with no bespoke code.

  • GasMeter (meter.go) — per-operation gas metering with a hard limit, mirroring the EVM gas pool (SubGas / ErrOutOfGas). A VM meters each operation's real cost against the payer's GasLimit before pricing it.

  • Settlement (settle.go) — Cost converts metered gas to nLUX at a price; CanPay is the read-only affordability check a block runs in Verify (so an unpayable block is never accepted — fail closed); Charge is the authoritative debit+burn a block runs in Accept. Settlement happens INSIDE consensus block processing, atomically with the operation's state effect via the VM's versiondb commit — never in a synchronous RPC.

Orthogonality. Admission and settlement are two files of one package, not one mechanism: a VM declares a Policy AND settles through a Ledger; the two compose, they do not overlap. The schedule of "which operation costs how much gas" is supplied BY THE VM (keyvm prices per cryptographic algorithm) — this package is the pure mechanism, the VM owns the values.

It lives in the chains module on purpose: the VMs are plugin binaries built without the node daemon in their dependency closure, so the fee surface they program against must live beside them.

Index

Constants

View Source
const MinTxFeeFloor uint64 = 1_000_000

MinTxFeeFloor is the minimum tx fee, in the base unit (constants.MicroLux, 1e-6 LUX — written nLUX throughout this package), that any user-facing chain SHOULD charge. It is the lower bound used when reviewing per-VM policies; Validate does not enforce it (a VM is free to charge MORE), but a VM choosing less is flagged at review.

Known intentional exception: the X-Chain prices transactions through its own UTXO fee subsystem, NOT a FlatPolicy, and sits deliberately outside this floor — its UTXO economics are set independently of the account-model floor.

Variables

View Source
var (
	// ErrInsufficientFunds mirrors the EVM's error of the same intent
	// (core/state_transition.go buyGas): the payer cannot cover the fee.
	ErrInsufficientFunds = errors.New("fee: insufficient funds")

	// ErrBalanceOverflow is returned by Credit when an account balance or the
	// burned-supply counter would exceed 2^64-1 nLUX.
	ErrBalanceOverflow = errors.New("fee: balance overflow")
)

Sentinel errors. Settlement is fail-secure: every error path denies the operation (the block fails Verify or Accept) — none silently proceeds.

View Source
var (
	// ErrZeroMinFee is returned by Validate when a non-sentinel policy declares
	// a zero minimum fee. User-facing chains MUST charge > 0.
	ErrZeroMinFee = errors.New("fee policy declares zero min tx fee on a user-facing chain")

	// ErrWrongFeeAsset is returned by Policy.ValidateFee when the tx pays in an
	// asset other than the policy's FeeAssetID.
	ErrWrongFeeAsset = errors.New("tx pays fee in wrong asset")

	// ErrInsufficientFee is returned by Policy.ValidateFee when the paid amount
	// is below MinTxFee.
	ErrInsufficientFee = errors.New("tx fee below policy minimum")

	// ErrChainAcceptsNoUserTxs is returned by NoUserTxPolicy.ValidateFee for
	// any tx — committee-driven chains have no user mempool, so any arrival at
	// the fee gate is a wiring bug.
	ErrChainAcceptsNoUserTxs = errors.New("chain accepts no user-submitted txs")
)

Sentinel errors returned by Policy implementations and Validate.

View Source
var ErrOutOfGas = errors.New("fee: out of gas")

ErrOutOfGas is returned by GasMeter.Consume when an operation's gas exceeds the remaining limit. It mirrors the EVM gas pool's exhaustion error and is fail-secure: the operation does not proceed.

Functions

func CanPay

func CanPay(b Balances, acct Account, fee uint64) error

CanPay is the read-only affordability check a block runs in Verify, for every fee-bearing transaction, BEFORE the block can be accepted. It never mutates state, so verifying a block cannot move funds; it only proves the payer could cover the fee. A block containing any unaffordable transaction fails Verify and is never accepted — fail closed.

func Charge

func Charge(b Balances, acct Account, fee uint64) error

Charge is the authoritative settlement a block runs in Accept: it debits the fee from the payer and burns it (reduces circulating supply). It is the native analogue of the EVM's buyGas SubBalance, but burning rather than crediting a coinbase. Because it writes through the VM's versiondb, the debit commits atomically with the operation it pays for. If the payer cannot cover the fee (which Verify should already have prevented), Charge returns ErrInsufficientFunds and the caller MUST abort block acceptance — a key operation never takes effect unpaid.

func Cost

func Cost(gasUsed, price Gas) (uint64, error)

Cost converts metered gas to a fee in nLUX at the given per-unit price, refusing overflow (fail-secure: a fee must never wrap to a smaller number). price is nLUX per unit of Gas.

func Validate added in v1.7.41

func Validate(p Policy) error

Validate is the boot-time check run against a VM's declared Policy. It returns ErrZeroMinFee if a non-sentinel policy declares MinTxFee() == 0, and nil for NoUserTxPolicy (the explicit opt-out) or any MinTxFee > 0.

Types

type Account

type Account = ids.ShortID

Account is a fee payer identity. It is the canonical 20-byte Lux address (ids.ShortID) — the same type P/X-Chain use for UTXO owners — so balances here interoperate with existing address tooling. It is PUBLIC: an account identifier never carries secret material.

type Balances

type Balances interface {
	Balance(acct Account) (uint64, error)
	Credit(acct Account, amount uint64) error
	Burn(acct Account, amount uint64) error
	Burned() (uint64, error)
}

Balances is the debitable balance surface a fee-charging VM exposes to the settler. It is the minimal contract the EVM expresses as GetBalance / SubBalance, adapted to the native account model and to BURNING (no coinbase):

  • Balance reports an account's spendable nLUX.
  • Credit adds nLUX (genesis seeding; future treasury/bridge inflows).
  • Burn removes nLUX from the payer AND reduces circulating supply — the fee debit. It is the only spend path here; there is intentionally no account->account transfer, because service-chain fees are burned, not paid to a validator. (A treasury split, if ever wanted, is a new method, not a reinterpretation of Burn.)
  • Burned reports cumulative burned supply, for audit.

Implementations MUST be atomic with respect to a single call and MUST be driven inside a transaction/versiondb whose commit is the block's commit, so a fee debit and the operation it pays for either both land or neither does.

type FlatPolicy added in v1.7.41

type FlatPolicy struct {
	// Fee is the per-tx burn amount, in nLUX. MUST be > 0.
	Fee uint64

	// AssetID is the fee asset. For primary-network burn, use
	// constants.UTXOAssetIDFor(networkID).
	AssetID ids.ID
}

FlatPolicy charges a fixed fee per user tx — the canonical policy for VMs without dynamic gas pricing.

func (FlatPolicy) FeeAssetID added in v1.7.41

func (p FlatPolicy) FeeAssetID() ids.ID

FeeAssetID returns the configured fee asset.

func (FlatPolicy) MinTxFee added in v1.7.41

func (p FlatPolicy) MinTxFee() uint64

MinTxFee returns the flat fee.

func (FlatPolicy) ValidateFee added in v1.7.41

func (p FlatPolicy) ValidateFee(paid uint64, asset ids.ID) error

ValidateFee enforces the flat policy.

type Gas

type Gas uint64

Gas is a unit of metered work. A VM's gas schedule assigns a Gas cost to each operation (keyvm prices per cryptographic algorithm); Cost converts Gas to nLUX at a per-unit price.

type GasMeter

type GasMeter struct {
	// contains filtered or unexported fields
}

GasMeter meters gas consumption against a hard limit, exactly like the EVM gas pool (SubGas / out-of-gas). A VM constructs one per fee-bearing operation with the payer's declared GasLimit, then Consumes the operation's metered cost; Consume past the limit denies the operation rather than overdraw.

func NewGasMeter

func NewGasMeter(limit Gas) *GasMeter

NewGasMeter returns a meter with the given limit, fully unconsumed.

func (*GasMeter) Consume

func (m *GasMeter) Consume(amount Gas) error

Consume deducts amount from the remaining gas. It returns ErrOutOfGas (and changes nothing) if amount exceeds what remains.

func (*GasMeter) Limit

func (m *GasMeter) Limit() Gas

Limit reports the meter's hard limit.

func (*GasMeter) Remaining

func (m *GasMeter) Remaining() Gas

Remaining reports unconsumed gas.

func (*GasMeter) Used

func (m *GasMeter) Used() Gas

Used reports consumed gas (limit - remaining).

type KV

type KV interface {
	Has(key []byte) (bool, error)
	Get(key []byte) ([]byte, error)
	Put(key []byte, value []byte) error
}

KV is the minimal key/value surface Ledger needs. It is the read/write subset of luxfi/database.Database (satisfied by versiondb, memdb, and any backing store), declared locally so the settlement primitive does not pin a database module version — keeping it buildable beside any VM under GOWORK=off.

type Ledger

type Ledger struct {
	// contains filtered or unexported fields
}

Ledger is the canonical KV-backed Balances implementation. It writes to the VM's state KV (a versiondb in production), so every Credit/Burn participates in the block's atomic commit: a fee burn and the operation it pays for land together or not at all. Balances are nLUX (1e-6 LUX), matching the node/vms/types/fee floor units.

func NewLedger

func NewLedger(kv KV) *Ledger

NewLedger returns a Ledger over kv. kv is the VM's state database; in a block Accept it is the versiondb whose Commit the block performs.

func (*Ledger) Balance

func (l *Ledger) Balance(acct Account) (uint64, error)

Balance returns acct's spendable nLUX (0 if never funded).

func (*Ledger) Burn

func (l *Ledger) Burn(acct Account, amount uint64) error

Burn debits amount nLUX from acct and reduces circulating supply by the same amount (the burned counter rises). It is the fee settlement op: it returns ErrInsufficientFunds if acct cannot cover amount, leaving state untouched.

func (*Ledger) Burned

func (l *Ledger) Burned() (uint64, error)

Burned returns cumulative burned supply in nLUX.

func (*Ledger) Credit

func (l *Ledger) Credit(acct Account, amount uint64) error

Credit adds amount nLUX to acct. Overflow is refused (fail-secure: minting must never silently wrap to a smaller balance).

type NoUserTxPolicy added in v1.7.41

type NoUserTxPolicy struct{}

NoUserTxPolicy is the sentinel for chains that accept no user-submitted txs (committee-driven only). Distinguishing it from "policy not set" is what lets Validate refuse zero-fee user-facing chains without false positives on the committee chains.

func (NoUserTxPolicy) FeeAssetID added in v1.7.41

func (NoUserTxPolicy) FeeAssetID() ids.ID

FeeAssetID returns ids.Empty — there is no fee asset.

func (NoUserTxPolicy) MinTxFee added in v1.7.41

func (NoUserTxPolicy) MinTxFee() uint64

MinTxFee always returns 0 — there are no user txs to charge.

func (NoUserTxPolicy) ValidateFee added in v1.7.41

func (NoUserTxPolicy) ValidateFee(uint64, ids.ID) error

ValidateFee always returns ErrChainAcceptsNoUserTxs — any caller reaching this gate is a wiring bug.

type Policy added in v1.7.41

type Policy interface {
	// MinTxFee returns the minimum fee, in nLUX, that any user tx must pay.
	// MUST be > 0 for user-facing VMs.
	MinTxFee() uint64

	// FeeAssetID returns the asset the fee is paid in. For primary-network
	// burn this is constants.UTXOAssetIDFor(networkID).
	FeeAssetID() ids.ID

	// ValidateFee returns nil if the paid amount and asset satisfy the policy,
	// else ErrWrongFeeAsset, ErrInsufficientFee or ErrChainAcceptsNoUserTxs.
	ValidateFee(paidNanoLux uint64, paidAsset ids.ID) error
}

Policy is the ADMISSION half of the fee model: how a VM decides whether a user-submitted tx pays enough to enter at all. Every chain that accepts user txs MUST declare a non-nil Policy whose MinTxFee() is > 0. Chains that accept no user txs declare NoUserTxPolicy — the only legal way to opt out.

Policy is declared at boot and checked once by Validate; settlement of the admitted fee during block execution is the Ledger / Charge half of this package. The two compose and do not overlap.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL