Documentation
¶
Overview ¶
Package gen produces random numscript programs for differential testing.
Generation targets this package's own AST rather than builder/ directly: cleanup.go rewrites the tree in place, and builder's Source/Destination are opaque render closures. convert.go renders once cleanup has settled.
Index ¶
- func GenerateScript(rng *rand.Rand) (vars map[string]string, balances map[BalanceKey]*big.Int, ...)
- func RandFromBytes(b []byte) *rand.Rand
- type AccountVarDecl
- type AccountVarFill
- type BalanceKey
- type DestAllotmentClause
- type DestInorderClause
- type DestKind
- type Destination
- type ExtraStatement
- type ExtraStatementKind
- type Focus
- type Generated
- type KeptOrDest
- type KeptOrDestKind
- type MetaKey
- type MetaType
- type Monetary
- type NumExpr
- type NumExprKind
- type PortionDiv
- type Program
- type ScenarioStmt
- type Script
- type Shape
- type Source
- type SourceAllotmentClause
- type SourceKind
- type Statement
- type Strategy
- type VarDecl
- type VarDeclKind
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GenerateScript ¶
func GenerateScript(rng *rand.Rand) (vars map[string]string, balances map[BalanceKey]*big.Int, metadata map[MetaKey]string, script string)
GenerateScript is Generate without the Shape.
func RandFromBytes ¶
RandFromBytes derives an RNG from arbitrary bytes, so a Go fuzz target can drive generation reproducibly from its []byte corpus.
Types ¶
type AccountVarDecl ¶
type AccountVarDecl struct {
Value string
}
AccountVarDecl is an account-typed `vars {}` declaration bound at run time from the vars map, unlike VarDecl which the compiler computes from an origin call.
Value is sometimes "world": the oracle rejects a var resolving to @world in source position, the interpreter accepts it. Compare tolerates that direction.
type AccountVarFill ¶
type AccountVarFill struct {
Var *builder.Var[builder.ExprTypeAccount]
Value string
}
AccountVarFill pairs a declared account-typed var with the literal value it must be bound to at run time — collected during toBuilderScript and consumed by GenerateScript (api.go), which merges it into the vars bindings map builder.BuildProgram returns. Unlike a VarFromBalance/ VarFromMeta var (whose value the compiler computes from an origin call), a plain account-typed var has no origin — its value can only come from the caller-supplied vars map, exactly as GenerateScript already does for the account/asset/etc. pools.
type BalanceKey ¶
BalanceKey identifies one (account, asset) starting balance.
type DestAllotmentClause ¶
type DestAllotmentClause struct {
Portion *big.Rat
// Same constraint as SourceAllotmentClause.PortionAsVar.
PortionAsVar bool
// Same meaning as SourceAllotmentClause.Div.
Div *PortionDiv
KeptOrDest KeptOrDest
}
type DestInorderClause ¶
type DestInorderClause struct {
Max Monetary
KeptOrDest KeptOrDest
}
type Destination ¶
type Destination struct {
Kind DestKind
// DestAccount only
Account string
AccountAsVar bool
// DestInorder, DestOneof
InorderClauses []DestInorderClause
Remaining *KeptOrDest
// DestAllotment only
AllotClauses []DestAllotmentClause
// DestAllotment only: non-nil adds a trailing `remaining` clause, and the
// clause portions sum to strictly less than 1.
AllotRemaining *KeptOrDest
}
type ExtraStatement ¶
type ExtraStatement struct {
Kind ExtraStatementKind
// ExtraSave: the amount to save, either a literal (Monetary) or a
// reference to Script.Vars[*VarIdx] (a declared VarFromBalance var) —
// exactly one of the two is set. ExtraSendFromAccountVar,
// ExtraSendToAccountVar: the send amount (always a literal).
Monetary *Monetary
// ExtraSave, ExtraSendVar (must index a VarFromBalance decl),
// ExtraSetTxMetaVar (must index a VarFromMeta decl)
VarIdx *int
// ExtraSendFromAccountVar, ExtraSendToAccountVar: index into
// Script.AccountVars for the account-typed var used as source/
// destination (respectively); the other side is the literal Account.
AccountVarIdx *int
// ExtraSave (source account), ExtraSetAccountMeta, ExtraSendVar (source),
// ExtraSendFromAccountVar (destination), ExtraSendToAccountVar (source)
Account string
AccountAsVar bool
// ExtraSaveAll
Asset string
AssetAsVar bool
// ExtraSetTxMeta, ExtraSetAccountMeta, ExtraSetTxMetaVar
Key string
Value NumExpr
// ExtraSetTxMeta, ExtraSetAccountMeta: when non-nil the meta value is this
// string rather than the Value expression, exercising string-typed values.
StringValue *string
StringValueAsVar bool
// ExtraSendVar: `send $<Vars[VarIdx]> (source = Account, destination = Destination)`
Destination string
DestinationAsVar bool
}
ExtraStatement is one non-send statement. Only the fields relevant to Kind are populated.
type ExtraStatementKind ¶
type ExtraStatementKind int
ExtraStatementKind enumerates the non-send statement kinds this generator can produce, on top of Program's send-only statements.
const ( ExtraSave ExtraStatementKind = iota ExtraSaveAll ExtraSetTxMeta ExtraSetAccountMeta ExtraSendVar ExtraSetTxMetaVar ExtraSendFromAccountVar ExtraSendToAccountVar )
type Generated ¶
type Generated struct {
Vars map[string]string
Balances map[BalanceKey]*big.Int
Metadata map[MetaKey]string
Script string
Shape Shape
// Flags are the feature flags the script needs on the numscript engines
// (interpreter and compiler+VM). Derived from Shape.
Flags []string
// OracleCompatible is false when the script uses shapes outside the legacy
// machine's grammar or decided behavior (oneof, colors, division portions,
// wrong-asset caps): the harness then runs only the two numscript engines
// and compares them against each other.
OracleCompatible bool
}
Generated is one runnable numscript program with the vars bindings it needs at run time, the starting balances and metadata it assumes, and the structural Shape of the program.
type KeptOrDest ¶
type KeptOrDest struct {
Kind KeptOrDestKind
Dest *Destination // valid only when Kind == To
}
type MetaType ¶
type MetaType int
MetaType is the type a `meta()` origin var is declared as. meta() returns TypeAny, so the declaration decides how the stored string is parsed and the var only compiles if the type matches the preset value.
type Monetary ¶
type Monetary struct {
Asset string
AssetAsVar bool
// Ignored for negative amounts: they have no literal form to bind.
AsVar bool
Amount *big.Int
}
An "AsVar" field marks one value occurrence as written through a `vars {}` variable instead of inline. Per occurrence, not per value: the same account can be inline in one place and a var in another, which is what makes two resources alias one account (formancehq/ledger#2056).
Only the rendering changes, never the value, so cleanup.go is unaffected.
type NumExpr ¶
type NumExpr struct {
Kind NumExprKind
// NumLit only
Lit *big.Int
// NumLit only.
LitAsVar bool
// NumAdd, NumSub only
Left, Right *NumExpr
}
type NumExprKind ¶
type NumExprKind int
NumExprKind is the arithmetic allowed in set_tx_meta/set_account_meta values. Only +/- over literals and vars: that is all NumScript.g4 has.
const ( NumLit NumExprKind = iota NumAdd NumSub )
type PortionDiv ¶ added in v0.1.1
PortionDiv is a portion written as a division expression `$num/den` (the numerator always renders through a runtime-bound number var, so any sign is expressible without unary minus). Unlike literals and portion vars, the value is unconstrained: negative and over-one portions are reachable this way, and both engines must reject them the same way. numscript-only: the oracle's grammar has no division expression.
func (PortionDiv) Rat ¶ added in v0.1.1
func (d PortionDiv) Rat() *big.Rat
type Program ¶
type Program []Statement
func GenerateProgram ¶
GenerateProgram generates a random program (with its own randomized account-pool size) and applies the cleanup pass (cleanup.go) that removes constructs the legacy machine would reject.
type ScenarioStmt ¶
type ScenarioStmt struct {
Kind string
Send *Statement
Extra *ExtraStatement
}
ScenarioStmt is one statement of a scenario block. Exactly one of Send and Extra is set. Kind is the setup/observer kind that built it, for coverage counting.
type Script ¶
type Script struct {
Vars []VarDecl
AccountVars []AccountVarDecl
Seeds Program
Program Program
Extra []ExtraStatement
Order []bool
Strategy Strategy
Focus *Focus
Scenario []ScenarioStmt
ScenarioPos int
Balances map[BalanceKey]*big.Int
Metadata map[MetaKey]string
}
Script is the full output of one round of generation: a `vars {}` block, optional seed-funding statements, the core send-only program, extra non-send statements, an optional scenario block, and/or pre-set starting balances/metadata (populated instead of, or alongside, Seeds — see genBalances). Order records a riffle-interleaving of Program and Extra (true = take next from Program, false = take next from Extra), so extra statements land throughout the script instead of only after every send. Scenario is emitted contiguously, just before the Order step at index ScenarioPos, or after the last step when ScenarioPos == len(Order). bodyStatements is the one walk of that layout.
type Shape ¶
type Shape struct {
Strategy Strategy
HasSave bool
HasBoundedOverdraft bool
// A save and a bounded-overdraft source on the same account, any assets.
SaveOverdraftSameAccount bool
// Same, on the same (account, asset).
SaveOverdraftSameResource bool
// Same, with the save executing before the overdraft draw.
SaveOverdraftSameResourceInOrder bool
// An in-order pair whose literal save amount exceeds the pair's starting
// balance (preset plus seed funding), with no earlier statement touching
// the pair. A var-valued or `*` save never counts.
SaveOverdrawsInitial bool
// An allotment (source or destination side) with a `remaining` clause.
HasAllotmentRemaining bool
// An allotment clause whose portion is a var rather than an n/d literal.
HasPortionVar bool
// A statement references a vars-block origin that reads through another
// origin var (VarDecl.AccountFromVarIdx). Counted on references, not
// declarations: an unreferenced origin var is never rendered into the
// script.
HasChainedOrigin bool
// numscript-only shapes (see sourceOptions.numscriptOnly). Any of these
// makes the script skip the oracle legs: the legacy machine's grammar has
// no oneof, colors or division expressions, and its clause-evaluation
// order on wrong-asset caps is not the behavior under test.
HasOneof bool
HasColoredSource bool
HasWrongAssetCap bool
HasPortionDiv bool
// Scenario kinds in block order, empty without a scenario block.
SetupKinds []string
ObserverKinds []string
}
Shape records structural facts about one Script, so a sweep can count how often the multi-statement shapes it cares about are actually generated. Everything here is read off the AST and the starting balances; no engine semantics are involved.
type Source ¶
type Source struct {
Kind SourceKind
// SrcAccount, SrcAccountOverdraft
Account string
AccountAsVar bool
// SrcAccount, SrcAccountOverdraft: non-empty adds a `\ "COLOR"` clause.
// numscript-only (experimental-asset-colors): scripts containing it skip
// the oracle legs.
Color string
ColorAsVar bool
// SrcAccountOverdraft only: nil means unbounded overdraft
Overdraft *Monetary
// SrcCapped only
Cap *Monetary
Inner *Source
// SrcInorder, SrcOneof
Sources []Source
// SrcAllotment only
Clauses []SourceAllotmentClause
// SrcAllotment only: non-nil adds a trailing `remaining from` clause, and
// the clause portions sum to strictly less than 1.
AllotmentRemaining *Source
}
type SourceAllotmentClause ¶
type SourceAllotmentClause struct {
Portion *big.Rat
// Rendered as a runtime-bound portion var instead of an n/d literal. Only
// legal in an allotment with a remaining clause: without one, the oracle
// rejects the block at compile time ("might be less than 100%").
PortionAsVar bool
// When non-nil, the clause portion is Div rendered as a division expression
// and Portion/PortionAsVar are ignored.
Div *PortionDiv
Source Source
}
type SourceKind ¶
type SourceKind int
const ( SrcAccount SourceKind = iota SrcAccountOverdraft SrcCapped SrcInorder SrcAllotment // numscript-only (experimental-oneof): scripts containing it skip the // oracle legs. SrcOneof )
type VarDecl ¶
type VarDecl struct {
Kind VarDeclKind
Account string
// Makes `balance($accountN, ...)` and a literal `@acc` elsewhere alias
// one account.
AccountAsVar bool
// Chains this origin onto an earlier declaration: the account read by
// balance()/meta() is the origin var at this index, which must be an
// earlier VarFromMeta declaration of MetaType MetaAccount. Account then
// holds the account name that parent's meta value resolves to (known at
// generation time), so the balance/metadata this declaration observes can
// still be steered; AccountAsVar is ignored.
AccountFromVarIdx *int
// VarFromBalance only
Asset string
AssetAsVar bool
// VarFromMeta only
Key string
// VarFromMeta only. Must match how the preset value at (Account, Key) is
// written.
MetaType MetaType
}
VarDecl is a `vars {}` declaration whose value comes from the compiler, not from a runtime binding.
type VarDeclKind ¶
type VarDeclKind int
VarDeclKind is the origin of a `vars {}` declaration this generator can produce: either `monetary $name = balance(<account>, <asset>)` or `number $name = meta(<account>, "<key>")`.
const ( VarFromBalance VarDeclKind = iota VarFromMeta )