@@ -2,89 +2,184 @@ use std::collections::BTreeMap;
22
33type Label = String ;
44
5+ /// Represents the compiled bytecode of a program for the zkVM.
56#[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
67pub struct Bytecode < F > {
8+ /// A vector of instructions that form the executable part of the program.
9+ ///
10+ /// The Program Counter (pc) iterates over this vector.
711 pub instructions : Vec < Instruction < F > > ,
8- pub hints : BTreeMap < usize , Vec < Hint < F > > > , // pc -> hints
12+
13+ /// A map from a program counter (pc) value to a list of `Hint`s.
14+ ///
15+ /// Hints are auxiliary, non-deterministic instructions executed only by the prover.
16+ ///
17+ /// In this zkVM, they are crucial for managing memory allocations in the absence
18+ /// of an `ap` register.
19+ pub hints : BTreeMap < usize , Vec < Hint < F > > > ,
20+
21+ /// The memory offset from the frame pointer (fp) where the public input for the program begins.
922 pub public_input_start : usize ,
23+
24+ /// The program counter (pc) value at which the program execution is considered complete.
1025 pub ending_pc : usize ,
1126}
1227
28+ /// Represents a value that can either be a constant or a value from memory.
1329#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
1430pub enum MemOrConstant < F > {
31+ /// A constant value (a field element).
1532 Constant ( F ) ,
16- MemoryAfterFp { shift : usize } , // m[fp + shift]
33+ /// A memory location specified by a positive offset from the frame pointer (`fp`).
34+ ///
35+ /// Represents the scalar value at `m[fp + shift]`.
36+ MemoryAfterFp {
37+ /// The offset from `fp` where the memory location is located.
38+ shift : usize ,
39+ } ,
1740}
1841
42+ /// Represents a value that can be a memory location, the `fp` register itself, or a constant.
1943#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
2044pub enum MemOrFpOrConstant < F > {
21- MemoryAfterFp { shift : usize } , // m[fp + shift]
45+ /// A memory location specified by a positive offset from `fp`. Represents `m[fp + shift]`.
46+ MemoryAfterFp {
47+ /// The offset from `fp` where the memory location is located.
48+ shift : usize ,
49+ } ,
50+ /// The value of the frame pointer (`fp`) register itself.
2251 Fp ,
52+ /// A constant value (a field element).
2353 Constant ( F ) ,
2454}
2555
56+ /// Represents a value that is either a memory location or the `fp` register itself.
2657#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
2758pub enum MemOrFp {
28- MemoryAfterFp { shift : usize } , // m[fp + shift]
59+ /// A memory location specified by a positive offset from `fp`. Represents `m[fp + shift]`.
60+ MemoryAfterFp {
61+ /// The offset from `fp` where the memory location is located.
62+ shift : usize ,
63+ } ,
64+ /// The value of the frame pointer (`fp`) register itself.
2965 Fp ,
3066}
3167
68+ /// The basic arithmetic operations supported by the VM's `Computation` instruction.
3269#[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
3370pub enum Operation {
71+ /// Field addition in the base field.
3472 Add ,
73+ /// Field multiplication in the base field.
3574 Mul ,
3675}
3776
77+ /// Defines the instruction set for this zkVM, specialized for the `AggregateMerge` logic.
78+ ///
79+ /// The ISA is minimal and includes basic arithmetic, memory operations, control flow,
80+ /// and powerful precompiles for hashing and extension field arithmetic.
3881#[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
3982pub enum Instruction < F > {
83+ /// Performs a basic arithmetic computation: `res = arg_a op arg_b`.
84+ ///
85+ /// This corresponds to the `ADD` and `MUL` opcodes in the design document.
4086 Computation {
87+ /// The arithmetic operation to perform (`Add` or `Mul`).
4188 operation : Operation ,
89+ /// The first operand of the computation.
4290 arg_a : MemOrConstant < F > ,
91+ /// The second operand of the computation.
4392 arg_b : MemOrFp ,
93+ /// The memory location or constant that the result must be equal to.
4494 res : MemOrConstant < F > ,
4595 } ,
96+ /// Performs a memory dereference: `res = m[m[fp + shift_0] + shift_1]`.
97+ ///
98+ /// This corresponds to the `DEREF` opcode.
4699 Deref {
100+ /// The offset from `fp` for the first memory access, which yields a pointer.
47101 shift_0 : usize ,
102+ /// The offset added to the pointer from the first access to get the final address.
48103 shift_1 : usize ,
104+ /// The value that the result of the double dereference must be equal to.
49105 res : MemOrFpOrConstant < F > ,
50- } , // res = m[m[fp + shift_0] + shift_1]
106+ } ,
107+ /// A conditional jump, called `JUZ` (Jump Unless Zero)
108+ /// .
109+ /// Changes the `pc` if `condition` is non-zero.
51110 JumpIfNotZero {
111+ /// The value to check. The jump is taken if this value is not zero.
52112 condition : MemOrConstant < F > ,
113+ /// The destination `pc` for the jump.
53114 dest : MemOrConstant < F > ,
115+ /// The new value for the frame pointer (`fp`) after the instruction.
54116 updated_fp : MemOrFp ,
55117 } ,
118+ /// **Precompile** for a Poseidon2 permutation over 16 base field elements.
119+ ///
120+ /// This is used for hashing operations within the `AggregateMerge` algorithm.
121+ /// The precompile performs: `Poseidon2(m'[m[fp+s]], m'[m[fp+s+1]]) = (m'[m[fp+s+2]], m'[m[fp+s+3]])`,
122+ /// where:
123+ /// - `s` is the shift,
124+ /// - `m` is scalar memory,
125+ /// - `m'` is vectorized memory access (a chunk of 8 base field elements, representing a degree-8 extension field element).
56126 Poseidon2_16 {
127+ /// The starting offset `s` from `fp`. The instruction reads 4 pointers from `m[fp+s]` to `m[fp+s+3]`.
57128 shift : usize ,
58- } , /*
59- Read 4 vectorized pointers from stack:
60- Poseidon2(m[8 * m[fp + shift]] .. 8 * (1 + m[fp + shift])] | m[8 * m[fp + shift + 1]] .. 8 * (1 + m[fp + shift + 1])])
61- = m[8 * m[fp + shift + 2]] .. 8 * (1 + m[fp + shift + 2])] | m[8 * m[fp + shift + 3]] .. 8 * (1 + m[fp + shift + 3])]
62- */
129+ } ,
130+ /// **Precompile** for a Poseidon2 permutation over 24 base field elements.
131+ ///
132+ /// This operates similarly to `Poseidon2_16` but on 3 concatenated input vectors and 3 output vectors.
133+ ///
134+ /// It reads 6 pointers from memory, starting at `m[fp+shift]`.
63135 Poseidon2_24 {
136+ /// The starting offset from `fp`. The instruction reads 6 pointers from `m[fp+shift]` to `m[fp+shift+5]`.
64137 shift : usize ,
65- } , // same as above, but with 24 field elements
138+ } ,
139+ /// **Precompile** for multiplication in the degree-8 extension field.
140+ ///
141+ /// This is important for speeding up recursive proof verification (`snark_verify`).
66142 ExtensionMul {
67- args : [ usize ; 3 ] , // offset after fp
143+ /// An array of three offsets from `fp`. These point to the start of the 8-cell memory blocks
144+ /// for the two input extension field elements and the resulting output element.
145+ args : [ usize ; 3 ] ,
68146 } ,
69147}
70148
71- /// Hints does not appear in the verified bytecode, but are useful during execution
149+ /// Hints are special instructions for the prover to resolve non-determinism.
150+ ///
151+ /// They are not part of the verified computation trace.
72152#[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
73153pub enum Hint < F > {
154+ /// A hint for the prover to allocate a new memory segment for a function's stack frame.
155+ ///
156+ /// This is the core mechanism for memory management in a VM without an `ap` (allocation pointer)
157+ /// register. The compiler pre-calculates the required memory size for each function.
74158 RequestMemory {
75- offset : usize , // the pointer to the allocated memory range will be stored at m[fp + offset]
159+ /// The offset from `fp` where the pointer to the newly allocated segment will be stored.
160+ offset : usize ,
161+ /// The requested size of the memory segment in scalar field elements.
76162 size : MemOrConstant < F > ,
77- /// if vectorized == true, the start of the allocated memory will be aligned to 8 field elements
78- /// m[8X...] and we set m[fp + offset] = X
163+ /// If true, the start of the allocated memory is aligned to an 8-element boundary
164+ /// to facilitate vectorized memory access for extension field operations.
165+ /// The value stored at `m[fp + offset]` will be the aligned address divided by 8.
79166 vectorized : bool ,
80167 } ,
168+ /// A hint for the prover to compute the bit decomposition of a base field element.
169+ ///
170+ /// This is a non-deterministic operation used for operations like range checks
171+ /// or other logic required by the XMSS signature scheme.
81172 DecomposeBits {
82- res_offset : usize , // m[fp + res_offset..fp + res_offset + 31] will contain the decomposed bits
173+ /// The starting offset from `fp` where the resulting bits will be stored.
174+ res_offset : usize ,
175+ /// The field element that needs to be decomposed into its bits.
83176 to_decompose : MemOrConstant < F > ,
84177 } ,
85- // Debug purpose
178+ /// A hint used for debugging to print values from memory during execution.
86179 Print {
180+ /// A string containing line information (e.g., file and line number) for context.
87181 line_info : String ,
182+ /// A list of memory locations or constants whose values should be printed.
88183 content : Vec < MemOrConstant < F > > ,
89184 } ,
90185}
0 commit comments