Documentation
¶
Overview ¶
Package bal is the conservation primitive (@B): an append-only double-entry journal over bounded accounts, where conservation is an arithmetic identity — every transfer writes two signed entries (−a, +a) in one transaction, so the system total always equals the sum of its boundary accounts.
Identity (@B09a): the substrate-preferred two-identity split. The external account_id is a namespaced STRING at every boundary — no numeric width exists to truncate, so the /ts id-boundary bug class is structurally impossible here (@C04d the strong way). The internal account key is a dense uint32, engine-internal, fixed-width codec, never on any wire struct.
Numerics (@B04): amounts are int64 minor units through the account's scale. No float64 touches an amount anywhere, ever — ParseAmount is the only sanctioned string→amount crossing.
Index ¶
- func EncodeAccountKey(k AccountKey) [4]byte
- func FormatAmount(v int64, scale uint8) string
- func ParseAmount(s string, scale uint8) (int64, error)
- func ValidateAccountID(id string) error
- type AccountDef
- type AccountKey
- type AmountScaleError
- type BoundsError
- type ChainBreak
- type Entry
- type NotPostableError
- type Store
- func (s *Store) AccountScale(ctx context.Context, accountID string) (uint8, error)
- func (s *Store) Balance(ctx context.Context, accountID string) (value int64, version int64, err error)
- func (s *Store) ChainOracle() chronicle.RebuildOracle
- func (s *Store) DefineAccount(ctx context.Context, def AccountDef) (AccountKey, error)
- func (s *Store) Entries(ctx context.Context, accountID string, afterEntryID int64, limit int) ([]Entry, error)
- func (s *Store) GlobalFoldOracle() chronicle.RebuildOracle
- func (s *Store) Init(ctx context.Context) error
- func (s *Store) Transfer(ctx context.Context, transferID, from, to string, amount int64, memo string, ...) error
- func (s *Store) VerifyChains(ctx context.Context) ([]ChainBreak, error)
- type UnknownAccountError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EncodeAccountKey ¶
func EncodeAccountKey(k AccountKey) [4]byte
EncodeAccountKey writes the key with an explicit fixed-width codec (4 bytes, big-endian) — the sanctioned byte crossing.
func FormatAmount ¶
FormatAmount renders minor units back to the canonical decimal string at the account's scale.
func ParseAmount ¶
ParseAmount converts a decimal string to int64 minor units under the account's scale — the ONLY string→amount crossing. It refuses anything that is not an exact integer of minor units at that scale (XOLU-BAL004 territory), and never passes through float64.
func ValidateAccountID ¶
ValidateAccountID enforces the external-id shape: non-empty, ≤256, no whitespace; '/' and ':' and '.' are the namespace vocabulary.
Types ¶
type AccountDef ¶
type AccountDef struct {
ID string // namespaced external id, e.g. "warehouse:A/widget" or "1.1.9.10"
Unit string // "EUR", "widget", "gram"
Scale uint8 // decimal places of the minor unit
Floor int64 // minimum balance (default 0)
Ceiling *int64 // optional maximum balance
Postable bool // only leaf (imputable) accounts accept entries (@B03a)
}
AccountDef defines an account (@B03, @B03a).
type AccountKey ¶
type AccountKey uint32
AccountKey is the engine-internal dense identity: uint32, the wave-1 per-primitive width. It never appears in JSON.
const MaxAccountKey AccountKey = 0xFFFFFFFF
MaxAccountKey is the codec ceiling; it fits uint32 exactly.
func DecodeAccountKey ¶
func DecodeAccountKey(b [4]byte) AccountKey
DecodeAccountKey reads the fixed-width codec back, losslessly across the full uint32 span.
type AmountScaleError ¶
type AmountScaleError struct{ Detail string }
func (*AmountScaleError) Error ¶
func (e *AmountScaleError) Error() string
type BoundsError ¶
func (*BoundsError) Error ¶
func (e *BoundsError) Error() string
type ChainBreak ¶
ChainBreak localises a violation of the per-account arithmetic chain.
type Entry ¶ added in v0.16.15
type Entry struct {
EntryID int64 `json:"entry_id"`
TransferID string `json:"transfer_id"`
AccountID string `json:"account_id"`
Amount int64 `json:"-"` // rendered as string by the handler (@B04)
PreviousBalance int64 `json:"-"`
CurrentBalance int64 `json:"-"`
Version int64 `json:"version"`
Memo string `json:"memo,omitempty"`
At time.Time `json:"at"`
}
Entry is one journal row on the API surface: external account id, signed amount, the chain triple, memo, instant. Internal keys never appear (@B09a).
type NotPostableError ¶
type NotPostableError struct{ AccountID string }
func (*NotPostableError) Error ¶
func (e *NotPostableError) Error() string
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is bal's SQL plane (@B05): the append-only journal and the balances table, maintained in the same transaction as each entry. The bounds guard's input commits-or-aborts with the entry it guards (@C04a); no rollup is ever consulted by a guard.
The db handle must carry WAL + busy_timeout (the house defaults). Transfer is deliberately WRITE-FIRST: its opening statement is the guarded UPDATE itself (accounts resolved by subquery), so the transaction is a writer from its first statement and contending transfers queue under busy_timeout even on plain deferred transactions. A read-first shape would hit WAL's snapshot invalidation (SQLITE_BUSY past the busy handler) on read→write upgrade — the G-13 harness caught exactly that in an earlier form.
func (*Store) AccountScale ¶ added in v0.16.15
AccountScale returns an account's scale (render support).
func (*Store) Balance ¶
func (s *Store) Balance(ctx context.Context, accountID string) (value int64, version int64, err error)
Balance returns the current balance and version for an account.
func (*Store) ChainOracle ¶
func (s *Store) ChainOracle() chronicle.RebuildOracle
ChainOracle wraps VerifyChains as a rebuild oracle for iolu db check.
func (*Store) DefineAccount ¶
func (s *Store) DefineAccount(ctx context.Context, def AccountDef) (AccountKey, error)
DefineAccount creates an account and its zero balance row. The internal key is allocated densely (MAX+1) inside the transaction.
func (*Store) Entries ¶ added in v0.16.15
func (s *Store) Entries(ctx context.Context, accountID string, afterEntryID int64, limit int) ([]Entry, error)
Entries returns up to limit journal rows for an account, oldest first, starting after afterEntryID (0 = from the beginning).
func (*Store) GlobalFoldOracle ¶
func (s *Store) GlobalFoldOracle() chronicle.RebuildOracle
GlobalFoldOracle: SELECT SUM per account from the journal, compared row-for-row against balances — derive(journal) == current, exactly.
func (*Store) Init ¶
Init creates the bal tables. Idempotent. The journal's `state` column (default 'committed') leaves room for holds (@B10) without migration.
func (*Store) Transfer ¶
func (s *Store) Transfer(ctx context.Context, transferID, from, to string, amount int64, memo string, at time.Time) error
Transfer moves amount minor units from `from` to `to` as two signed journal entries (−a, +a) in ONE transaction (@B03). Admission is the house CAS discipline (@B06, T-34): the decision lives inside each UPDATE's predicate, rows-affected is the verdict — never read-decide-write. The guarded UPDATE is the transaction's FIRST statement (write-first; see Store docs), resolving the account and its floor by subquery and returning the chain triple in the same statement. Error discrimination (unknown vs not-postable vs bounds) happens on the failure path only, where the transaction is read-only and about to roll back.
func (*Store) VerifyChains ¶
func (s *Store) VerifyChains(ctx context.Context) ([]ChainBreak, error)
VerifyChains is the local verifier (@B08): per account, every entry satisfies previous+amount=current, entryₙ.previous = entryₙ₋₁.current, and versions are contiguous. A lost, duplicated, or altered entry is not merely detected but LOCALISED to the exact break.
type UnknownAccountError ¶
type UnknownAccountError struct{ AccountID string }
func (*UnknownAccountError) Error ¶
func (e *UnknownAccountError) Error() string