Documentation
¶
Index ¶
- Constants
- Variables
- func AccountTierInvariant(view ViewKeeper) sdk.Invariant
- func AllInvariants(view ViewKeeper) sdk.Invariant
- func BalanceKey(addr crypto.Address, denom string) []byte
- func BalanceKeysInvariant(view ViewKeeper) sdk.Invariant
- func BalancePrefixKey(addr crypto.Address) []byte
- func ErrInputOutputMismatch() error
- func ErrNoInputs() error
- func ErrNoOutputs() error
- func NewHandler(bank BankKeeper) bankHandler
- func SupplyInvariant(view ViewKeeper) sdk.Invariant
- func SupplyKey(denom string) []byte
- func ValidateGenesis(data GenesisState) error
- func ValidateInputsOutputs(inputs []Input, outputs []Output) error
- type BankKeeper
- func (bank BankKeeper) AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) BurnCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) ExportGenesis(ctx sdk.Context) GenesisState
- func (bank BankKeeper) GetParams(ctx sdk.Context) Params
- func (bank BankKeeper) InitGenesis(ctx sdk.Context, data GenesisState)
- func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) error
- func (bank BankKeeper) MintCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) RecomputeSupply(ctx sdk.Context)
- func (bank BankKeeper) RestrictedDenoms(ctx sdk.Context) []string
- func (bank BankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) SetParams(ctx sdk.Context, params Params) error
- func (bank BankKeeper) SetRestrictedDenoms(ctx sdk.Context, restrictedDenoms []string)
- func (bank BankKeeper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
- func (bank BankKeeper) WillSetParam(ctx sdk.Context, key string, value any)
- type BankKeeperI
- type BankParamsContextKey
- type GenesisState
- type Input
- type InputOutputMismatchError
- func (InputOutputMismatchError) AssertABCIError()
- func (e InputOutputMismatchError) Error() string
- func (goo InputOutputMismatchError) MarshalBinary2(cdc *amino.Codec, buf []byte, offset int) (int, error)
- func (goo InputOutputMismatchError) SizeBinary2(cdc *amino.Codec) (int, error)
- func (goo *InputOutputMismatchError) UnmarshalBinary2(cdc *amino.Codec, bz []byte, anyDepth int) error
- type MsgMultiSend
- type MsgSend
- func (msg MsgSend) GetSignBytes() []byte
- func (msg MsgSend) GetSigners() []crypto.Address
- func (goo MsgSend) MarshalBinary2(cdc *amino.Codec, buf []byte, offset int) (int, error)
- func (msg MsgSend) Route() string
- func (goo MsgSend) SizeBinary2(cdc *amino.Codec) (int, error)
- func (msg MsgSend) SpendForSigner(signer crypto.Address) std.Coins
- func (msg MsgSend) Type() string
- func (goo *MsgSend) UnmarshalBinary2(cdc *amino.Codec, bz []byte, anyDepth int) error
- func (msg MsgSend) ValidateBasic() error
- type NoInputsError
- func (NoInputsError) AssertABCIError()
- func (e NoInputsError) Error() string
- func (goo NoInputsError) MarshalBinary2(cdc *amino.Codec, buf []byte, offset int) (int, error)
- func (goo NoInputsError) SizeBinary2(cdc *amino.Codec) (int, error)
- func (goo *NoInputsError) UnmarshalBinary2(cdc *amino.Codec, bz []byte, anyDepth int) error
- type NoOutputsError
- func (NoOutputsError) AssertABCIError()
- func (e NoOutputsError) Error() string
- func (goo NoOutputsError) MarshalBinary2(cdc *amino.Codec, buf []byte, offset int) (int, error)
- func (goo NoOutputsError) SizeBinary2(cdc *amino.Codec) (int, error)
- func (goo *NoOutputsError) UnmarshalBinary2(cdc *amino.Codec, bz []byte, anyDepth int) error
- type Output
- type Params
- type ViewKeeper
- func (view ViewKeeper) GetCoin(ctx sdk.Context, addr crypto.Address, denom string) int64
- func (view ViewKeeper) GetCoins(ctx sdk.Context, addr crypto.Address) std.Coins
- func (view ViewKeeper) HasCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) bool
- func (view ViewKeeper) Logger(ctx sdk.Context) *slog.Logger
- func (view ViewKeeper) TotalSupply(ctx sdk.Context, denom string) int64
- type ViewKeeperI
Constants ¶
const ( QueryBalance = "balances" QuerySupply = "supply" )
Query paths.
const ( // BalancePrefix namespaces split-tier balances — every denom outside the // account-tier allowlist, which is a strict superset of the realm-issued ones. BalancePrefix = "/b/" )
Balance storage.
A balance whose denom is in the account tier lives inside the account object, in its Coins field. Every other balance lives on its own at
/b/ || addr (20 raw bytes) || denom -> amount, 8-byte big-endian
The account tier is an allowlist of the chain's gas denoms, injected at construction (see NewViewKeeper). It is tiny by design: a balance there is free only because the account object is rewritten on every transaction its owner sends anyway, to bump the sequence, and that argument holds for gas denoms and nothing else. Anything else kept there would be unbounded third-party-writable data on the holder's own metered critical path — see the ADR for the attack that enables, and for why the allowlist is closed and injected rather than a governance param.
Not to be confused with who may *issue* a denom, which is std.IsRealmDenom and is enforced at the banker.
Layout constraints, all load-bearing:
- The address is not length-prefixed, which is safe only while crypto.Address is a fixed [20]byte: the denom is the remainder at a constant offset, so it may itself contain "/" and ":" freely.
- Store keys are not gas-metered, so key size is bounded only by MaxDenomLength (274) in tm2/pkg/std. Value size is metered, which is why the amount is fixed-width rather than amino-encoded.
- "/b/" must not prefix, or be prefixed by, any other key in the shared main store. Check bare names too — "gasPrice", "globalAccountNumber", "pkg:", "consensus_params", GnoVM object ids — not only "/..." prefixes. Pinned by TestBalancePrefixDoesNotCollide. Deliberately not nested under "/a/", where the session-account precedent shows nesting creates an iteration-filter hazard.
const (
ModuleName = "bank"
)
const RouterKey = ModuleName
RouterKey is they name of the bank module
const SupplyPrefix = "/supply/"
SupplyPrefix is where the per-denom supply counter lives:
/supply/ || denom -> amount, 8-byte big-endian
Spelled out rather than abbreviated. Every module shares one store here, so the prefix is what namespaces the keyspace — cosmos can afford a single opaque byte because each of its modules gets its own mounted store, and we cannot. Store keys are not gas-metered, so the extra bytes cost nothing but state.
Deliberately not "/s/": that is auth.SessionStoreKeyInfix verbatim, and while a session key begins "/a/" so the two ranges cannot overlap, sharing the spelling would make a search for one return the other.
Variables ¶
var Package = amino.RegisterPackage(amino.NewPackage( "github.com/gnolang/gno/tm2/pkg/sdk/bank", "bank", amino.GetCallersDirname(), ).WithDependencies().WithTypes( NoInputsError{}, "NoInputsError", NoOutputsError{}, "NoOutputsError", InputOutputMismatchError{}, "InputOutputMismatchError", MsgSend{}, "MsgSend", GenesisState{}, "GenesisState", Params{}, "Params", ))
Functions ¶
func AccountTierInvariant ¶
func AccountTierInvariant(view ViewKeeper) sdk.Invariant
AccountTierInvariant checks the balances held inside account objects.
Every denom there must be in the allowlist. A denom that is not is stranded: the keeper routes it to the split tier, so GetCoin reports zero while GetCoins reports it, and it cannot be spent. A realm-issuable denom there is worse than stranded and is reported as such — its issuer could mint into a shared metered blob, which is the griefing the split exists to close.
func AllInvariants ¶
func AllInvariants(view ViewKeeper) sdk.Invariant
AllInvariants runs every bank invariant.
func BalanceKey ¶
BalanceKey returns the store key holding addr's balance of denom.
Only valid for split-tier denoms; account-tier denoms live in the account object.
func BalanceKeysInvariant ¶
func BalanceKeysInvariant(view ViewKeeper) sdk.Invariant
BalanceKeysInvariant sweeps the split-tier keyspace.
Checks that every key under BalancePrefix is well formed and names a valid denom; that every value decodes to a positive amount; that no denom there belongs to the account tier; that a realm-shaped denom has a shape a realm could have issued; and that every address holding a balance has an account object, without which the funds are permanently unspendable because the address cannot sign.
Reads nothing through the keeper's typed accessors. splitCoins, GetCoins and decodeBalance all panic on precisely the states reported here, and an invariant must enumerate every violation rather than die at the first.
func BalancePrefixKey ¶
BalancePrefixKey returns the prefix covering every balance held by addr, the analogue of auth.SessionPrefixKey. Iterating it yields denoms in ascending order.
func ErrInputOutputMismatch ¶
func ErrInputOutputMismatch() error
func ErrNoInputs ¶
func ErrNoInputs() error
func ErrNoOutputs ¶
func ErrNoOutputs() error
func NewHandler ¶
func NewHandler(bank BankKeeper) bankHandler
NewHandler returns a handler for "bank" type messages.
func SupplyInvariant ¶
func SupplyInvariant(view ViewKeeper) sdk.Invariant
SupplyInvariant checks the recorded supply against the balances actually held.
This is the only redundancy check in the set: two numbers maintained by different code that must agree. The others are structural — they check shapes, tiers and key/field agreement — and a mint that bypassed the counter looks perfectly well formed to all of them. Here it does not.
Reports in both directions. A denom held with no record is what an unaccounted credit produces; a record with nothing held is what a lost balance write produces.
func ValidateGenesis ¶
func ValidateGenesis(data GenesisState) error
ValidateGenesis performs basic validation of genesis data returning an error for any failed validation criteria.
func ValidateInputsOutputs ¶
ValidateInputsOutputs validates that each respective input and output is valid and that the sum of inputs is equal to the sum of outputs.
Types ¶
type BankKeeper ¶
type BankKeeper struct {
ViewKeeper
// contains filtered or unexported fields
}
BankKeeper only allows transfers between accounts without the possibility of creating coins. It implements the BankKeeperI interface.
func NewBankKeeper ¶
func NewBankKeeper(acck auth.AccountKeeper, pk params.ParamsKeeperI, key store.StoreKey, accountDenoms []string) BankKeeper
NewBankKeeper returns a new BankKeeper.
accountDenoms is the allowlist of denoms held inside the account object; see balance.go. Everything else gets its own key. Pass the chain's gas denoms.
func (BankKeeper) ExportGenesis ¶
func (bank BankKeeper) ExportGenesis(ctx sdk.Context) GenesisState
ExportGenesis returns a GenesisState for a given context and keeper
func (BankKeeper) InitGenesis ¶
func (bank BankKeeper) InitGenesis(ctx sdk.Context, data GenesisState)
InitGenesis - Init store state from genesis data
func (BankKeeper) InputOutputCoins ¶
InputOutputCoins handles a list of inputs and outputs
func (BankKeeper) MintCoins ¶
MintCoins creates amt and credits it to addr, raising the supply counter.
This and BurnCoins are the only paths that change supply. Transfers are supply-neutral, so AddCoins and subtract deliberately do not touch the counter: they also carry fees and storage-deposit refunds, and a counter that followed them would make an unpaired credit look legitimate instead of breaking the supply invariant — which is the whole point of keeping a second number.
Supply is capped at MaxInt64 per denom. That is a real constraint, not a formality: before this counter existed two addresses could each hold MaxInt64 of one denom, since AddCoins bounds each balance and nothing bounded the total.
func (BankKeeper) RecomputeSupply ¶
func (bank BankKeeper) RecomputeSupply(ctx sdk.Context)
RecomputeSupply rewrites every supply record from the balances actually held.
For genesis and offline tooling only, never a metered path and never a live chain: it writes state outside any block, so a node that ran it and one that did not would diverge.
Genesis needs this rather than an incremental hook because SetCoins cannot compute its own delta. InitChainer writes the account object with the full pre-split balance *before* calling SetCoins, so SetCoins reads old == new and a delta would be zero for every vesting account — and that pre-write cannot be removed, since the vesting constructors validate OriginalVesting against it.
Do not run this to silence a SupplyInvariant finding. It makes the record agree with whatever is held, corruption included: on a double-homed denom — one with both an account-object entry and a /b/ key — the totals sum both homes, so this records the doubled figure and the supply invariant then reports healthy. Measured: 20 recorded against 25 held becomes 25 against 25. The corruption itself stays visible, because BalanceKeysInvariant owns the stray key and still reports it, so check the other invariants before concluding a supply mismatch was a bookkeeping error. Genesis cannot reach that state — a balances file lists one amount per denom per address, so SetCoins produces a single home — which is why this is a caution for offline tooling rather than a guard here.
func (BankKeeper) RestrictedDenoms ¶
func (bank BankKeeper) RestrictedDenoms(ctx sdk.Context) []string
func (BankKeeper) SendCoins ¶
func (bank BankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
SendCoins moves coins from one account to another, restrction could be applied
func (BankKeeper) SendCoinsUnrestricted ¶
func (bank BankKeeper) SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
SendCoinsUnrestricted is used for paying gas. It bypasses vesting and session-spend checks.
func (BankKeeper) SetCoins ¶
SetCoins replaces every balance at addr.
Replace-all is only meaningful at genesis and in tests; ordinary transfers use AddCoins/SubtractCoins, which touch one key per split-tier denom moved. Balances present but absent from amt are deleted, so this is a replacement and not a merge.
Does not maintain the supply counter. It cannot: InitChainer writes the account object with the full pre-split amount before calling this, so the old value read here equals the new one and any delta would be zero for a vesting account. Call RecomputeSupply after a batch of these; see supply.go.
Stays exported despite that, because genesis lives in another package (gno.land/pkg/gnoland) and has to call it. The protection that matters is already structural rather than lexical: SetCoins is absent from both vm.BankKeeperI and auth.BankKeeperI, so neither a realm nor the ante handler can reach it. Only a new tm2-side handler holding BankKeeperI could, which is what the warning below is for.
Cost is O(denoms currently held), and each removal is a full store write, so this is not safe to call on an address whose denom count an attacker controls — clearing a few hundred would exceed the block gas limit. Every caller today runs at genesis, where the count is known.
func (BankKeeper) SetParams ¶
func (bank BankKeeper) SetParams(ctx sdk.Context, params Params) error
func (BankKeeper) SetRestrictedDenoms ¶
func (bank BankKeeper) SetRestrictedDenoms(ctx sdk.Context, restrictedDenoms []string)
This is a convenience function for manually setting the restricted denoms. Useful for testing and initchain setup.
func (BankKeeper) SubtractCoins ¶
SubtractCoins subtracts amt from the coins at the addr.
Enforces vesting: if the account is a VestingAccount, the amount must not exceed the spendable (unlocked) balance at the current block time.
Does not enforce the session spend limit or the transfer restriction, which both live in SendCoins. BurnCoins debits through here, so a realm removing its own coin does not consume a session's allowance — which changes nothing it could not already do, since removal needs no consent from the holder.
func (BankKeeper) WillSetParam ¶
func (bank BankKeeper) WillSetParam(ctx sdk.Context, key string, value any)
type BankKeeperI ¶
type BankKeeperI interface {
ViewKeeperI
InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) error
SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
SendCoinsUnrestricted(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
InitGenesis(ctx sdk.Context, data GenesisState)
GetParams(ctx sdk.Context) Params
MintCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
BurnCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
RecomputeSupply(ctx sdk.Context)
}
bank.Keeper defines a module interface that facilitates the transfer of coins between accounts without the possibility of creating coins.
type BankParamsContextKey ¶
type BankParamsContextKey struct{}
type GenesisState ¶
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
}
GenesisState - all state that must be provided at genesis
func DefaultGenesisState ¶
func DefaultGenesisState() GenesisState
DefaultGenesisState - Return a default genesis state
func NewGenesisState ¶
func NewGenesisState(params Params) GenesisState
NewGenesisState - Create a new genesis state
func (GenesisState) MarshalBinary2 ¶
func (GenesisState) SizeBinary2 ¶
func (goo GenesisState) SizeBinary2(cdc *amino.Codec) (int, error)
func (*GenesisState) UnmarshalBinary2 ¶
type Input ¶
type Input struct {
Address crypto.Address `json:"address" yaml:"address"`
Coins std.Coins `json:"coins" yaml:"coins"`
}
Input models transaction input
func (Input) ValidateBasic ¶
ValidateBasic - validate transaction input
type InputOutputMismatchError ¶
type InputOutputMismatchError struct {
// contains filtered or unexported fields
}
func (InputOutputMismatchError) AssertABCIError ¶
func (InputOutputMismatchError) AssertABCIError()
func (InputOutputMismatchError) Error ¶
func (e InputOutputMismatchError) Error() string
func (InputOutputMismatchError) MarshalBinary2 ¶
func (InputOutputMismatchError) SizeBinary2 ¶
func (goo InputOutputMismatchError) SizeBinary2(cdc *amino.Codec) (int, error)
func (*InputOutputMismatchError) UnmarshalBinary2 ¶
type MsgMultiSend ¶
type MsgMultiSend struct {
Inputs []Input `json:"inputs" yaml:"inputs"`
Outputs []Output `json:"outputs" yaml:"outputs"`
}
MsgMultiSend - high level transaction of the coin module
func NewMsgMultiSend ¶
func NewMsgMultiSend(in []Input, out []Output) MsgMultiSend
NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg.
func (MsgMultiSend) GetSignBytes ¶
func (msg MsgMultiSend) GetSignBytes() []byte
GetSignBytes Implements Msg.
func (MsgMultiSend) GetSigners ¶
func (msg MsgMultiSend) GetSigners() []crypto.Address
GetSigners Implements Msg.
func (MsgMultiSend) SpendForSigner ¶
func (msg MsgMultiSend) SpendForSigner(signer crypto.Address) std.Coins
SpendForSigner implements std.SpendEstimator. Sums the coins of all inputs where Address == signer. An input-less signer returns nil.
func (MsgMultiSend) ValidateBasic ¶
func (msg MsgMultiSend) ValidateBasic() error
ValidateBasic Implements Msg.
type MsgSend ¶
type MsgSend struct {
FromAddress crypto.Address `json:"from_address" yaml:"from_address"`
ToAddress crypto.Address `json:"to_address" yaml:"to_address"`
Amount std.Coins `json:"amount" yaml:"amount"`
}
MsgSend - high level transaction of the coin module
func NewMsgSend ¶
NewMsgSend - construct arbitrary multi-in, multi-out send msg.
func (MsgSend) GetSigners ¶
GetSigners Implements Msg.
func (MsgSend) MarshalBinary2 ¶
func (MsgSend) SpendForSigner ¶
SpendForSigner implements std.SpendEstimator. Returns Amount when signer is the sender, zero otherwise.
func (*MsgSend) UnmarshalBinary2 ¶
func (MsgSend) ValidateBasic ¶
ValidateBasic Implements Msg.
type NoInputsError ¶
type NoInputsError struct {
// contains filtered or unexported fields
}
declare all bank errors. NOTE: these are meant to be used in conjunction with pkgs/errors.
func (NoInputsError) AssertABCIError ¶
func (NoInputsError) AssertABCIError()
func (NoInputsError) Error ¶
func (e NoInputsError) Error() string
func (NoInputsError) MarshalBinary2 ¶
func (NoInputsError) SizeBinary2 ¶
func (goo NoInputsError) SizeBinary2(cdc *amino.Codec) (int, error)
func (*NoInputsError) UnmarshalBinary2 ¶
type NoOutputsError ¶
type NoOutputsError struct {
// contains filtered or unexported fields
}
func (NoOutputsError) AssertABCIError ¶
func (NoOutputsError) AssertABCIError()
func (NoOutputsError) Error ¶
func (e NoOutputsError) Error() string
func (NoOutputsError) MarshalBinary2 ¶
func (NoOutputsError) SizeBinary2 ¶
func (goo NoOutputsError) SizeBinary2(cdc *amino.Codec) (int, error)
func (*NoOutputsError) UnmarshalBinary2 ¶
type Output ¶
type Output struct {
Address crypto.Address `json:"address" yaml:"address"`
Coins std.Coins `json:"coins" yaml:"coins"`
}
Output models transaction outputs
func (Output) ValidateBasic ¶
ValidateBasic - validate transaction output
type Params ¶
type Params struct {
RestrictedDenoms []string `json:"restricted_denoms" yaml:"restricted_denoms"`
}
Params defines the parameters for the bank module.
func (Params) MarshalBinary2 ¶
func (*Params) UnmarshalBinary2 ¶
type ViewKeeper ¶
type ViewKeeper struct {
// contains filtered or unexported fields
}
ViewKeeper implements a read only keeper implementation of ViewKeeperI.
func NewViewKeeper ¶
func NewViewKeeper(acck auth.AccountKeeper, key store.StoreKey, accountDenoms []string) ViewKeeper
NewViewKeeper returns a new ViewKeeper.
accountDenoms is the allowlist of denoms held inside the account object.
func (ViewKeeper) GetCoin ¶
GetCoin returns addr's balance of one denom without reading any other. This is the O(1) accessor.
func (ViewKeeper) GetCoins ¶
GetCoins returns every coin held at addr, from both tiers.
The two are merged rather than concatenated. An earlier version could concatenate, because the split tier held only "/"-prefixed denoms which sort below everything else; with an allowlist that no longer holds — a split denom such as "atom" sorts before an account-tier "ugnot" — so neither order is universally ascending. Coins.Add is a merge over sorted sets, and the tiers are disjoint by construction, so no two amounts are ever summed.
Costs O(number of split-tier denoms held). Use GetCoin when one denom will do — that is the whole point of the split.
func (ViewKeeper) HasCoins ¶
HasCoins returns whether or not an account has at least amt coins.
Checked per denom so that holding many unrelated denoms costs nothing here. An empty amt is satisfied, as under the previous IsAllGTE. The two differ on one unreachable edge — a zero-amount entry against an empty balance set, which IsAllGTE rejected and this accepts — because valid std.Coins never carry a zero amount.
func (ViewKeeper) Logger ¶
func (view ViewKeeper) Logger(ctx sdk.Context) *slog.Logger
Logger returns a module-specific logger.
func (ViewKeeper) TotalSupply ¶
func (view ViewKeeper) TotalSupply(ctx sdk.Context, denom string) int64
TotalSupply returns how much of denom exists, or zero for a denom nobody holds.
type ViewKeeperI ¶
type ViewKeeperI interface {
GetCoins(ctx sdk.Context, addr crypto.Address) std.Coins
GetCoin(ctx sdk.Context, addr crypto.Address, denom string) int64
HasCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) bool
TotalSupply(ctx sdk.Context, denom string) int64
}
ViewKeeperI defines a module interface that facilitates read only access to account balances.