AluVM Overview
Both lines run contract scripts in AluVM, but in two very different versions: zk-AluVM 0.12 in Line A and rgb-aluvm 0.11.1 in Line B. See Two RGB lines.
AluVM ("algorithmic logic unit VM") is a small, deterministic, register-based virtual machine. It has no access to the network, the clock, randomness or the file system, so a script gives the same result on every machine. RGB uses it for the parts of contract validation that a contract wants to program, such as "inputs sum to outputs". AluVM is a general VM, maintained in the AluVM GitHub org.
Which AluVM each line uses​
| Line A: v0.12 (rgb-wallet 0.12.0-rc.3) | Line B: v0.11 (rgb-cmd 0.11.1) | |
|---|---|---|
| Crates | aluvm 0.12.0, zk-aluvm 0.12.0, ultrasonic 0.12.0 | rgb-aluvm 0.11.1, a fork of AluVM 0.11 |
| Design | A rewrite: a minimal control-flow core plus ISA extensions, aimed at future zk-STARK proving | The classic AluVM 0.11 with a large ALU instruction set |
| Data registers | 16 field-element registers E1–E8, EA–EH (each an fe256), plus the field-order register FQ | Integer a8…a1024, float f16b…f512, byte r128…r8192 (32 of each size), and 16 string registers s16 (up to 64 KiB each) |
| Status registers | CK (check, ok/fail), CO (test result), CH (halt on failure), CF (failure count) | One flag, st0 |
| Call stack | At most 255 frames | At most 65,536 frames |
| Loop bound | Complexity limit only (CA/CL); CY exists but nothing increments it | Jump counter cy0 (u16, max 2^16 jumps) plus optional complexity limit cl0 |
| Arithmetic | Field arithmetic only: add, mul, neg, eq, … | Integer and float arithmetic, bit operations, byte strings |
| Crypto | None | Hashes ripemd, sha2, blake3; secp256k1 point ops secpgen, secpmul, secpadd, secpneg; vts checks a transition's signature |
| RGB state access | USONIC ISA: cknxi, cknxo, ldw, ldi, ldo, rsti, rsto | RGB ISA: cnp, cns, cng, cnc, ldp, lds, ldf, ldg, ldc, ldm, svs, sas, sps, vts |
| Assembler macro | uasm! / zk_aluasm! | rgbasm! / aluasm! |
Sources: aluvm-0.12.0/src/core/core.rs, zk-aluvm-0.12.0/src/core/core.rs, ultrasonic-0.12.0/src/isa/instr.rs; rgb-aluvm 0.11.1 src/reg/core_regs.rs, src/isa/instr.rs; rgb-consensus 0.11.1 src/vm/op_contract.rs. Details are on the instruction set page.
Shared ideas​
Registers start undefined. Every data register starts as "no value" (None), not zero. Reading an undefined register, or an arithmetic overflow, marks the result as failed (st0 = false in B, CO/CK in A) instead of raising an exception. clr makes a register undefined again.
Programs are libraries. Code is packaged as a Lib with four segments (aluvm 0.12.0 src/library/lib.rs; rgb-aluvm 0.11.1 src/library/):
| Segment | Holds | Limit |
|---|---|---|
| ISAE | Names of the instruction-set extensions the code uses | 255 (A), 64 (B) |
| code | Bytecode | 65,535 bytes |
| data | Read-only constants that instructions reference | 65,535 bytes |
| libs | Ids of other libraries this one may call | 255 (B also caps a whole program at 1,024 libraries) |
A library is identified by a hash of all four segments, its LibId. An entry point is a LibSite: library id plus code offset. Changing any constant in the data segment therefore changes the LibId, and the schema or codex that references it.
There is no RAM, heap, stack segment, or input or output area. The only data comes from registers, the data segment, and the RGB-specific load instructions that read the operation being validated.
Complexity metering. Each instruction has a complexity cost that is added to an accumulator (CA in A, ca0 in B). If a limit is set (CL, cl0) and reached, execution stops with failure. Nobody pays a fee, but a contract can't make validators run forever. Line A codices set the limit in verification_config. The ultrasonic test codex uses 10,000,000.
The only output is pass or fail. A script can't produce new state. The operation declares its new state, and the script checks it. Line B also reads an error code from a8[0] when a script fails.
Where AluVM sits in validation​
Not all validation is AluVM. Seals, commitments, consignment structure and state types are checked in Rust. Scripts run on top of that.
- v0.11 (rgb-cmd)
- v0.12 (rgb-wallet)
Each genesis or transition type in a schema can name a validator: Option<LibSite>. rgb-consensus runs it last, after checking the operation against the schema (src/validation/logic.rs). It puts the operation type in a16[0] and reads an error code from a8[0] on failure.
This is the real NIA script from rgb-schemas 0.11.1 (src/nia.rs):
rgbasm! {
// SUBROUTINE Transfer validation
put a8[0],ERRNO_NON_EQUAL_IN_OUT;
svs OS_ASSET; // sum of inputs == sum of outputs for the asset
test;
ret;
// SUBROUTINE Genesis validation
put a8[0],ERRNO_ISSUED_MISMATCH;
put a8[1],0;
put a16[0],0;
ldg GS_ISSUED_SUPPLY,a8[1],s16[0]; // read global state into s16[0]
extr s16[0],a64[0],a16[0]; // take a u64 from its start
sas OS_ASSET; // sum of outputs == a64[0]
test;
ret;
}
The VM is created with Vm::<Instr<RgbIsa<_>>>::new() and run with vm.exec(site, |id| scripts.get(&id), &context), which returns a bool.
A codex has one verifier entry point per contract method (verifiers: TinyOrdMap<CallId, LibSite>), plus per-cell lock scripts (CellLock) that are checked for each spent input (ultrasonic 0.12.0 src/codex.rs, src/state.rs). The VM is built with Vm::<Instr<LibId>>::with(CoreConfig, GfaConfig) and run with vm.exec(entry_point, &context, resolver), which returns the final CK status.
A lock-script test from ultrasonic 0.12.0 (src/codex.rs):
uasm! {
stop;
put E1, 48; // secret value
ldi auth; // load the input's auth token into EA
eq EA, E1; // must match
put E8, 1; // failure code #1
chk CO;
// ...
}
With the default CoreConfig { halt: true, .. }, the first failed check stops the program.
Ownership doesn't come from the VM​
Scripts don't decide who may move state. In RGB, the right to move owned state comes from spending the Bitcoin output that is its single-use seal. Validators check this in Rust, outside AluVM. Scripts can only add conditions on top of the seal: Line B's PFA schema uses vts to require an issuer signature on each transfer, and Line A's lock scripts can check a cell's CellLock against the spending input's witness.
What doesn't exist​
No compiler from a high-level language to AluVM ships in either line. Contractum (RGB-WG/contractum-lang, last pushed 2022-12-24) was never released. Contracts are written in Rust with the assembler macros above. There is no AluVM debugger, simulator, profiler or JavaScript implementation. The tools that do exist are the macro assemblers and Lib::assemble / Lib::disassemble.
Further reading​
- AluVM and zk-AluVM (Line A),
rgb-aluvm(Line B). - AluVM site.