Documentation
¶
Index ¶
- Constants
- Variables
- func Verify(p Program) error
- type AssetMismatchError
- type BadMetaValueError
- type BytecodeVersion
- type CannotCastScopedAccountToString
- type DivideByZeroError
- type ExecutionError
- type Instruction
- type InternalError
- type InvalidAccountName
- type InvalidAllotmentSum
- type InvalidColor
- type InvalidPostingError
- type InvalidScope
- type InvalidUncappedSource
- type MetadataNotFoundError
- type MissingFundsError
- type NegativeAmountError
- type NegativeBalanceError
- type NegativePortionError
- type Opcode
- type Program
- type Store
- type StoreError
- type UnsupportedBytecodeVersionError
- type Vars
- type VerifiedVarsInfo
- type Vm
Constants ¶
const ( SectionInstructions uint16 = 0x01 // NUMB only SectionStringsPool uint16 = 0x02 SectionIntsPool uint16 = 0x03 SectionMaxRegisters uint16 = 0x04 // NUMB only; optional, absent => every bank defaults to maxRegDefault )
Variables ¶
var CurrentBytecodeVersion = BytecodeVersion{Major: 0, Minor: 1}
CurrentBytecodeVersion is the version Encode writes and the newest one the decoders read.
Functions ¶
func Verify ¶
Verify statically checks that a program is safe to execute. It is opt-in: Exec assumes well-formed bytecode (the compiler's output always is) and does not call this. Run it on any program that did not come out of ir.Assemble in this process — anything decoded from a file or a wire.
A nil result guarantees the execution loop cannot read out of bounds: no truncated instruction, no jump into the middle of one, no out-of-range pool or register index, and no read of a register that was not written on every path reaching it.
It does not check that vars were supplied; for that see VerifyWithVars.
Types ¶
type AssetMismatchError ¶
func (AssetMismatchError) Error ¶
func (e AssetMismatchError) Error() string
type BadMetaValueError ¶
func (BadMetaValueError) Error ¶
func (e BadMetaValueError) Error() string
type BytecodeVersion ¶
BytecodeVersion identifies the bytecode wire format a program or vars blob was encoded with, as major.minor — two 16-bit header fields, major first. It versions the bytecode format only: the compiler and the library are versioned independently of it, and a new compiler release does not imply a new bytecode version.
The split carries the compatibility rule (see CanRead). A minor bump is additive — new opcodes, new sections, new optional operands — and leaves the meaning of everything an older writer could produce untouched, so a 1.1 reader runs 1.0 bytecode. A 1.0 reader does not accept 1.1 bytecode: it may happen to know every opcode a given blob uses, but that is not assumed. A major bump changes the meaning of existing encodings, so a 2.0 reader accepts no 1.x blob at all.
Major 0 is unstable: any 0.x change may change the meaning of existing encodings, so a 0.x reader accepts exactly its own version, older and newer minors alike. A host holding a 0.x blob that this build rejects should recompile the script from source rather than fail.
func PeekProgramVersion ¶
func PeekProgramVersion(buf []byte) (BytecodeVersion, error)
PeekProgramVersion checks that buf starts with a valid NUMB header and returns its bytecode version, without parsing the sections that follow and without checking that this build can read it.
func PeekVarsVersion ¶
func PeekVarsVersion(buf []byte) (BytecodeVersion, error)
PeekVarsVersion checks that buf starts with a valid NVAR header and returns its bytecode version, without parsing the sections that follow and without checking that this build can read it.
func (BytecodeVersion) CanRead ¶
func (v BytecodeVersion) CanRead(encoded BytecodeVersion) bool
CanRead reports whether a reader at version v accepts a blob encoded with version encoded: the same major, and a minor no newer than the reader's. An unstable (0.x) reader accepts only its exact version.
func (BytecodeVersion) String ¶
func (v BytecodeVersion) String() string
type CannotCastScopedAccountToString ¶
func (CannotCastScopedAccountToString) Error ¶
func (e CannotCastScopedAccountToString) Error() string
type DivideByZeroError ¶
func (DivideByZeroError) Error ¶
func (e DivideByZeroError) Error() string
type ExecutionError ¶
type ExecutionError interface {
error
// contains filtered or unexported methods
}
func Exec ¶
func Exec[S Store]( ctx context.Context, vm *Vm, vars *Vars, store S, ) (funds.ExecutionResult, ExecutionError)
type Instruction ¶
func (Instruction) GetBC ¶
func (i Instruction) GetBC() uint16
Little endian view of the b and c fields
type InternalError ¶
type InternalError struct {
Err error
}
InternalError signals a malformed program the VM cannot execute: a bug in whatever produced the bytecode, never a user-script error. Returned rather than panicked so the VM never crashes its host.
The mark violations land here rather than getting user-facing error types, since both are properties a verifier can decide from the instruction stream alone and neither is a legitimate outcome of a well-formed program.
func (InternalError) Error ¶
func (e InternalError) Error() string
func (InternalError) Unwrap ¶
func (e InternalError) Unwrap() error
type InvalidAccountName ¶
type InvalidAccountName struct {
Name string
}
func (InvalidAccountName) Error ¶
func (e InvalidAccountName) Error() string
type InvalidAllotmentSum ¶
func (InvalidAllotmentSum) Error ¶
func (e InvalidAllotmentSum) Error() string
type InvalidColor ¶
type InvalidColor struct {
Color string
}
func (InvalidColor) Error ¶
func (e InvalidColor) Error() string
type InvalidPostingError ¶
InvalidPostingError is wrapped in an InternalError when a posting fails funds.ValidatePosting.
func (InvalidPostingError) Error ¶
func (e InvalidPostingError) Error() string
type InvalidScope ¶
type InvalidScope struct {
Scope string
}
func (InvalidScope) Error ¶
func (e InvalidScope) Error() string
type InvalidUncappedSource ¶
type InvalidUncappedSource struct {
Account string
}
func (InvalidUncappedSource) Error ¶
func (e InvalidUncappedSource) Error() string
type MetadataNotFoundError ¶
func (MetadataNotFoundError) Error ¶
func (e MetadataNotFoundError) Error() string
type MissingFundsError ¶
func (MissingFundsError) Error ¶
func (e MissingFundsError) Error() string
type NegativeAmountError ¶
NegativeAmountError is a sent/saved amount that evaluated to negative — unlike NegativeBalanceError, it is not tied to any account.
func (NegativeAmountError) Error ¶
func (e NegativeAmountError) Error() string
type NegativeBalanceError ¶
func (NegativeBalanceError) Error ¶
func (e NegativeBalanceError) Error() string
type NegativePortionError ¶
func (NegativePortionError) Error ¶
func (e NegativePortionError) Error() string
type Opcode ¶
type Opcode byte
const ( // --- state & assertions (0x00) --- Op_SetCurrentAsset Opcode = 0x00 Op_AssertSameAsset Opcode = 0x01 // errors if the account name in str reg A is not well-formed Op_AssertValidAccount Opcode = 0x02 // errors (NegativeBalanceError) if the amount in int reg A is negative; // B = account str reg (for the error) Op_AssertNonNegativeBalance Opcode = 0x03 // checks the allotment leftover portion in reg A: errors if negative (portions // summing to > 1), and — when B == 1 (no `remaining` clause) — if non-zero Op_AssertLeftover Opcode = 0x04 Op_CheckEnoughFunds Opcode = 0x05 // errors if the color in str reg A is not well-formed Op_AssertValidColor Opcode = 0x06 // errors (NegativeAmountError) if the amount in int reg A is negative — // a sent/saved amount, not tied to any account (unlike // Op_AssertNonNegativeBalance, which is a balance() read) Op_AssertNonNegativeAmount Opcode = 0x07 // errors if the scope in str reg A is not well-formed Op_AssertValidScope Opcode = 0x08 // errors (NegativePortionError) if the portion in reg A is negative — an // allotment clause portion Op_AssertNonNegativePortion Opcode = 0x09 // errors (CannotCastScopedAccountToString) if the scope in str reg A is not // empty; B = account str reg (for the error) Op_AssertUnscoped Opcode = 0x0A // --- constants & variables (0x10) --- // may split into one opcode per expr_typ later Op_LoadInt Opcode = 0x10 // LoadConst (`Int) -> b_c = const-pool index Op_LoadStr Opcode = 0x11 // LoadConst (`String) -> b_c = const-pool index Op_LoadVarInt Opcode = 0x12 // b_c = int-var index Op_LoadVarStr Opcode = 0x13 // b_c = string-var index // A = dest (bool reg); one opcode per constant, so there is no operand to decode Op_ConstTrue Opcode = 0x15 Op_ConstFalse Opcode = 0x16 // --- metadata (0x20) --- // A = key (str reg), B = value (str reg) Op_SetTxMeta Opcode = 0x20 // A = account (str reg), B = key (str reg), C = value (str reg); ext.A = // scope (str reg, 0xFF = unscoped) Op_SetAccountMeta Opcode = 0x21 // meta(account, key) read, dispatched on the target type. // A = dest, B = account (str reg), C = key (str reg); ext.A = scope (str reg, // 0xFF = unscoped) Op_MetaStr Opcode = 0x22 Op_MetaInt Opcode = 0x23 Op_MetaPortion Opcode = 0x24 // as above, but a monetary needs two destinations, so the amount's goes in an // ext word: A = dest asset (str reg), ext.A = dest amount (int reg), ext.B = // scope (str reg, 0xFF = unscoped) Op_MetaMonetary Opcode = 0x25 // --- arithmetic & constructors (0x30) --- Op_AddInt Opcode = 0x30 Op_SubInt Opcode = 0x31 // 0x32 was Op_MinInt: a min is a comparison and a copy, so it is Op_LtInt // plus a branch. Reserved, do not reuse. Op_SubPortion Opcode = 0x33 Op_MkPortion Opcode = 0x34 // 0x35 was Op_MkMonetary: a monetary is a (str asset, int amount) register // pair, so there is nothing to construct. Reserved, do not reuse. Op_AddString Opcode = 0x36 // not adjacent to Op_SubPortion (0x33) because 0x32 is burned and 0x34..0x37 // are taken Op_AddPortion Opcode = 0x38 // an allotment share is a mul plus a floor (Op_PortionToInt) Op_MulPortion Opcode = 0x39 // --- unary & conversions (0x40) --- // 0x40 was Op_GetAmount and 0x41 was Op_GetAsset: projecting a monetary is now // just naming one of its two registers. Reserved, do not reuse. // // One copy per register bank: A = dest, B = src, both in that bank. There is no // monetary copy — a monetary is a (str asset, int amount) pair, so copy the two // halves. The family is split across 0x42..0x43 and 0x4A..0x4B because // 0x44..0x49 were already spoken for. Op_IntCopy Opcode = 0x42 Op_PortionCopy Opcode = 0x43 Op_NegInt Opcode = 0x44 Op_IntToString Opcode = 0x45 Op_PortionToString Opcode = 0x46 // A = dest (str reg), B = asset (str reg), C = amount (int reg) Op_MonetaryToString Opcode = 0x47 // the other two bank copies; see Op_IntCopy above Op_StrCopy Opcode = 0x4A Op_BoolCopy Opcode = 0x4B // Op_IntToPortion is exact; Op_PortionToInt floors. Op_IntToPortion Opcode = 0x4C Op_PortionToInt Opcode = 0x4D // The most general form: account,cap,overdraft,color,scope // The 0xFF special register means NULL for cap,overdraft,color and scope // ext.A = overdraft (int reg), ext.B = color (str reg), ext.C = scope (str reg) Op_PullAccount Opcode = 0x50 // account?, cap?, scope? Op_SendToAccount Opcode = 0x51 // save: reduce balance of account A for asset B by amount C (C == nilReg => // save all), floored at 0; ext.A = scope (str reg, 0xFF = unscoped) Op_Save Opcode = 0x52 // reads the account balance from the run-state; A = dest, B = account, C = // asset; ext.A = scope (str reg, 0xFF = unscoped) Op_Balance Opcode = 0x54 // opens a region at the current source-queue depth and posting count Op_MarkPush Opcode = 0x55 // A = rewind flag. Always pops the innermost mark; A == 1 additionally repays // everything pulled and reverses everything posted since the matching // Op_MarkPush, while A == 0 commits it. Op_MarkEnd Opcode = 0x56 // --- comparisons (0x60) --- // A = dest (bool reg) for all of them; the operand banks are what the opcode // implies. Op_IsZero is unary and the rest binary, but they are one group // because they are the whole set of bool *producers*. // // Only `<` and `==` exist, per type. The other surface operators are normalised // by the front end: // // a < b -> Lt(a, b) // a > b -> Lt(b, a) operands swapped // a <= b -> Not(Lt(b, a)) // a >= b -> Not(Lt(a, b)) // a == b -> Eq(a, b) // a != b -> Not(Eq(a, b)) Op_LtInt Opcode = 0x60 Op_EqInt Opcode = 0x61 Op_StrEq Opcode = 0x62 // was 0x37 Op_IsZero Opcode = 0x63 // was 0x48 Op_LtPortion Opcode = 0x64 Op_EqPortion Opcode = 0x65 // --- bool ops (0x70) --- // A = dest (bool reg), B = src (bool reg). Op_Not Opcode = 0x70 // was 0x49 // --- control flow (0x90) --- // A = cond (bool reg); b_c = unsigned forward delta, added to the pc of the // next instruction. A quantity is not a condition: project it with Op_IsZero. Op_JmpIfFalse Opcode = 0x90 // unconditional; b_c = unsigned forward delta, as above Op_Jmp Opcode = 0x91 // the dual of Op_JmpIfFalse, so either edge of a bool can be the branch without // a negation instruction Op_JmpIfTrue Opcode = 0x92 )
Opcodes are grouped by category with gaps, so new instructions can be added to a category without renumbering. See instruction-encoding.md.
type Program ¶
type Program struct {
Instructions []Instruction
StringsPool []string
IntsPool []big.Int
MaxRegString byte
MaxRegInt byte
MaxRegPortion byte
MaxRegBool byte
// Version is the bytecode version this program was assembled for, or (after
// DecodeProgram) the version it was actually encoded with. Encode always
// writes CurrentBytecodeVersion regardless of this field.
Version BytecodeVersion
}
func DecodeProgram ¶
type StoreError ¶
type StoreError struct {
Wrapped error
}
StoreError wraps an error returned by the host Store (balance or metadata fetch): neither a script error nor a bytecode bug, so the wrapped error is preserved for the host to inspect.
func (StoreError) Error ¶
func (e StoreError) Error() string
func (StoreError) Unwrap ¶
func (e StoreError) Unwrap() error
type UnsupportedBytecodeVersionError ¶
type UnsupportedBytecodeVersionError struct {
Encoded BytecodeVersion
Supported BytecodeVersion
}
UnsupportedBytecodeVersionError is what the decoders return for a blob this build cannot read: another major, a minor newer than CurrentBytecodeVersion, or, while the current version is unstable, any other 0.x minor.
func (UnsupportedBytecodeVersionError) Error ¶
func (e UnsupportedBytecodeVersionError) Error() string
type Vars ¶
type Vars struct {
StringsPool []string
IntsPool []big.Int
// Version is the bytecode version these vars were built for, or (after
// DecodeVars) the version they were actually encoded with. Encode always
// writes CurrentBytecodeVersion regardless of this field.
Version BytecodeVersion
}
func DecodeVars ¶
type VerifiedVarsInfo ¶
type VerifiedVarsInfo struct {
// contains filtered or unexported fields
}
VerifiedVarsInfo records, opaquely, the variable-pool sizes a Program was found to require by a successful VerifyWithVars call. A caller that keeps the Program around — e.g. one compiled artifact reused across many calls with (normally) the same Vars shape — can hold on to this instead of the Program's raw pool-size requirements, and later ask CheckVars whether a new Vars value would still satisfy VerifyWithVars, without paying for the static pass again. The fields are deliberately unexported: what "shape" means is this package's business, not a caller's.
func VerifyWithVars ¶
func VerifyWithVars(p Program, vars *Vars) (VerifiedVarsInfo, error)
VerifyWithVars is Verify plus the check that vars carries every variable the program loads. A nil *Vars is only legal for a program that reads none. On success it also returns the VerifiedVarsInfo backing that check; see its doc for why a caller would want to keep it.
func (VerifiedVarsInfo) CheckVars ¶
func (info VerifiedVarsInfo) CheckVars(vars *Vars) bool
CheckVars reports, in O(1) and without re-running verification, whether vars is guaranteed to satisfy the Program this VerifiedVarsInfo was obtained from — i.e. whether VerifyWithVars(thatProgram, vars) would succeed, without calling it. A false result is not itself a rejection: it only means the caller must call VerifyWithVars to get an authoritative answer (and, on failure, a precise error).
type Vm ¶
type Vm struct {
Program Program
// contains filtered or unexported fields
}
func NewVm ¶
NewVm sizes each register bank from the count the program declares. MaxRegX is a count and 0xFF is the nil-register sentinel, so the real indices are 0..MaxRegX-1 and this is exact rather than an upper bound.
Nothing here re-derives those counts: a program whose instructions name a register beyond its own declaration is malformed, and Exec is entitled to assume it isn't — see Verify, which is what checks it.