Skip to content

Commit 794e7e1

Browse files
authored
vm core: setup implementation (#8)
1 parent 8d4f27d commit 794e7e1

File tree

5 files changed

+170
-30
lines changed

5 files changed

+170
-30
lines changed

crates/leanVm/src/bytecode/hint.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,16 @@ impl Hint {
7979
// Store the current vectorized allocation pointer (`ap_vectorized`) at `addr`.
8080
memory_manager
8181
.memory
82-
.insert(addr, F::from_usize(run_context.ap_vectorized))?;
82+
.insert(addr, run_context.ap_vectorized)?;
8383

8484
// Increase the vectorized allocation pointer by `size` (number of vectors).
85-
run_context.ap_vectorized += size;
85+
run_context.ap_vectorized.offset += size;
8686
} else {
8787
// Store the current scalar allocation pointer (`ap`) at `addr`.
88-
memory_manager
89-
.memory
90-
.insert(addr, F::from_usize(run_context.ap))?;
88+
memory_manager.memory.insert(addr, run_context.ap)?;
9189

9290
// Increase the scalar allocation pointer by `size` (number of scalars).
93-
run_context.ap += size;
91+
run_context.ap.offset += size;
9492
}
9593
}
9694
Self::DecomposeBits {

crates/leanVm/src/bytecode/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct Bytecode {
2626
pub hints: BTreeMap<usize, Vec<Hint>>,
2727

2828
/// The memory offset from the frame pointer (fp) where the public input for the program begins.
29-
pub public_input_start: usize,
29+
pub starting_frame_memory: usize,
3030

3131
/// The program counter (pc) value at which the program execution is considered complete.
3232
pub ending_pc: usize,

crates/leanVm/src/constant.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
use p3_field::extension::BinomialExtensionField;
22
use p3_koala_bear::KoalaBear;
33

4-
pub(crate) const DIMENSION: usize = 8;
4+
use crate::memory::address::MemoryAddress;
5+
6+
/// The degree of the extension field.
7+
pub const DIMENSION: usize = 8;
8+
9+
/// The base field of the zkVM.
510
pub(crate) type F = KoalaBear;
11+
12+
/// The extension field of the zkVM.
613
pub(crate) type EF = BinomialExtensionField<F, DIMENSION>;
14+
15+
// Memory segment IDs
16+
17+
/// Segment for public inputs and global constants.
18+
pub const PUBLIC_DATA_SEGMENT: usize = 0;
19+
/// Segment for the main stack (used by fp and ap).
20+
pub const MAIN_STACK_SEGMENT: usize = 1;
21+
/// Segment for runtime vector memory (for Poseidon, EF multiplication, etc.).
22+
pub const VEC_RUNTIME_SEGMENT: usize = 2;
23+
/// Segment for the compiled bytecode (where `pc` points).
24+
pub const CODE_SEGMENT: usize = 3;
25+
26+
// Convention-based virtual memory pointers.
27+
28+
/// Points to `[0; DIMENSION]` in the vectorized memory segment.
29+
pub const ZERO_VEC_PTR: MemoryAddress = MemoryAddress::new(VEC_RUNTIME_SEGMENT, 0);
30+
/// Points to the result of `Poseidon2([0; 16])`, stored as 2 vector elements.
31+
pub const POSEIDON_16_NULL_HASH_PTR: MemoryAddress = MemoryAddress::new(VEC_RUNTIME_SEGMENT, 1);
32+
/// Points to the last 8 elements of `Poseidon2([0; 24])`, stored as 1 vector element.
33+
pub const POSEIDON_24_NULL_HASH_PTR: MemoryAddress = MemoryAddress::new(VEC_RUNTIME_SEGMENT, 3);
34+
35+
/// Start of the public input memory region within the PUBLIC_DATA_SEGMENT.
36+
pub const PUBLIC_INPUT_START: MemoryAddress = MemoryAddress::new(PUBLIC_DATA_SEGMENT, 0);

crates/leanVm/src/context/run_context.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct RunContext {
2525
/// While `ap` determines where memory is written at runtime, its usage is opaque to the verifier.
2626
/// Instead, memory layout is statically determined and reflected through hint instructions that
2727
/// record where allocations were made.
28-
pub(crate) ap: usize,
28+
pub(crate) ap: MemoryAddress,
2929

3030
/// Runtime allocation pointer (vectorized, chunked by `DIMENSION` field elements).
3131
///
@@ -38,16 +38,16 @@ pub struct RunContext {
3838
///
3939
/// Like `ap`, this value is **not exposed to the verifier**. Its sole role is to guide prover-side
4040
/// allocation logic during execution.
41-
pub(crate) ap_vectorized: usize,
41+
pub(crate) ap_vectorized: MemoryAddress,
4242
}
4343

4444
impl RunContext {
4545
#[must_use]
4646
pub const fn new(
4747
pc: MemoryAddress,
4848
fp: MemoryAddress,
49-
ap: usize,
50-
ap_vectorized: usize,
49+
ap: MemoryAddress,
50+
ap_vectorized: MemoryAddress,
5151
) -> Self {
5252
Self {
5353
pc,
@@ -150,8 +150,8 @@ mod tests {
150150
segment_index: 1,
151151
offset: 0,
152152
},
153-
0,
154-
0,
153+
MemoryAddress::default(),
154+
MemoryAddress::default(),
155155
);
156156

157157
// A constant operand with field element 42.
@@ -189,8 +189,8 @@ mod tests {
189189
offset: 0,
190190
}, // dummy pc
191191
fp,
192-
0,
193-
0,
192+
MemoryAddress::default(),
193+
MemoryAddress::default(),
194194
);
195195

196196
// The operand asks to read memory at fp + 2.
@@ -220,8 +220,8 @@ mod tests {
220220
offset: 0,
221221
}, // dummy pc
222222
fp,
223-
0,
224-
0,
223+
MemoryAddress::default(),
224+
MemoryAddress::default(),
225225
);
226226

227227
// Calling value_from_mem_or_constant should return a VirtualMachineError::MemoryError::UninitializedMemory.
@@ -240,7 +240,12 @@ mod tests {
240240

241241
#[test]
242242
fn test_get_value_from_mem_or_fp_or_constant_is_constant() {
243-
let ctx = RunContext::new(MemoryAddress::new(0, 0), MemoryAddress::new(1, 0), 0, 0);
243+
let ctx = RunContext::new(
244+
MemoryAddress::new(0, 0),
245+
MemoryAddress::new(1, 0),
246+
MemoryAddress::default(),
247+
MemoryAddress::default(),
248+
);
244249
let operand = MemOrFpOrConstant::Constant(F::from_u64(123));
245250
let memory = MemoryManager::default();
246251
let result = ctx
@@ -252,7 +257,12 @@ mod tests {
252257
#[test]
253258
fn test_get_value_from_mem_or_fp_or_constant_is_fp() {
254259
let fp_addr = MemoryAddress::new(1, 10);
255-
let ctx = RunContext::new(MemoryAddress::new(0, 0), fp_addr, 0, 0);
260+
let ctx = RunContext::new(
261+
MemoryAddress::new(0, 0),
262+
fp_addr,
263+
MemoryAddress::default(),
264+
MemoryAddress::default(),
265+
);
256266
let operand = MemOrFpOrConstant::Fp;
257267
let memory = MemoryManager::default();
258268
let result = ctx
@@ -269,7 +279,12 @@ mod tests {
269279
let expected_val = MemoryValue::Address(MemoryAddress::new(5, 5));
270280
memory.memory.insert(addr_to_read, expected_val).unwrap();
271281

272-
let ctx = RunContext::new(MemoryAddress::new(0, 0), fp, 0, 0);
282+
let ctx = RunContext::new(
283+
MemoryAddress::new(0, 0),
284+
fp,
285+
MemoryAddress::default(),
286+
MemoryAddress::default(),
287+
);
273288
let operand = MemOrFpOrConstant::MemoryAfterFp { shift: 7 };
274289
let result = ctx
275290
.value_from_mem_or_fp_or_constant(&operand, &memory)

crates/leanVm/src/core.rs

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use p3_field::{Field, PrimeField64};
1+
use p3_field::{Field, PrimeCharacteristicRing, PrimeField64};
22
use p3_symmetric::Permutation;
33

44
use crate::{
@@ -7,10 +7,13 @@ use crate::{
77
operand::MemOrFp,
88
program::Program,
99
},
10-
constant::{DIMENSION, F},
10+
constant::{
11+
CODE_SEGMENT, DIMENSION, F, POSEIDON_16_NULL_HASH_PTR, POSEIDON_24_NULL_HASH_PTR,
12+
PUBLIC_INPUT_START, ZERO_VEC_PTR,
13+
},
1114
context::run_context::RunContext,
1215
errors::{memory::MemoryError, vm::VirtualMachineError},
13-
memory::manager::MemoryManager,
16+
memory::{address::MemoryAddress, manager::MemoryManager},
1417
};
1518

1619
#[derive(Debug)]
@@ -22,7 +25,11 @@ pub struct VirtualMachine<Perm16, Perm24> {
2225
pub(crate) program: Program,
2326
}
2427

25-
impl<Perm16, Perm24> VirtualMachine<Perm16, Perm24> {
28+
impl<Perm16, Perm24> VirtualMachine<Perm16, Perm24>
29+
where
30+
Perm16: Permutation<[F; 2 * DIMENSION]>,
31+
Perm24: Permutation<[F; 3 * DIMENSION]>,
32+
{
2633
pub fn new(poseidon2_16: Perm16, poseidon2_24: Perm24) -> Self {
2734
Self {
2835
run_context: RunContext::default(),
@@ -33,6 +40,97 @@ impl<Perm16, Perm24> VirtualMachine<Perm16, Perm24> {
3340
}
3441
}
3542

43+
/// Initializes the virtual machine with a given program.
44+
///
45+
/// This function performs all the necessary setup before execution:
46+
///
47+
/// - Allocates the required memory segments
48+
/// - Loads public and private inputs into stack memory
49+
/// - Fills in convention-based memory slots (e.g., zero vector, null hashes)
50+
/// - Computes and aligns allocation pointers
51+
/// - Sets up the initial execution context (program counter, frame pointer, etc.)
52+
pub fn setup(
53+
&mut self,
54+
program: Program,
55+
no_vec_runtime_memory: usize,
56+
) -> Result<(), VirtualMachineError> {
57+
// Save the program internally.
58+
self.program = program;
59+
60+
// Extract the public and private inputs from the program.
61+
let public_input = &self.program.public_input;
62+
let private_input = &self.program.private_input;
63+
64+
// Allocate required memory segments: PUBLIC, STACK, VEC, CODE.
65+
// We ensure all necessary segments are present.
66+
while self.memory_manager.num_segments() <= CODE_SEGMENT {
67+
self.memory_manager.add();
68+
}
69+
70+
// Convention-Based Memory Initialization
71+
72+
// Write [0; DIMENSION] to the vector memory at ZERO_VEC_PTR.
73+
self.memory_manager
74+
.load_data(ZERO_VEC_PTR, &[F::ZERO; DIMENSION])?;
75+
76+
// Write Poseidon2([0; 16]) to POSEIDON_16_NULL_HASH_PTR.
77+
let hash16 = self.poseidon2_16.permute([F::ZERO; DIMENSION * 2]);
78+
self.memory_manager
79+
.load_data(POSEIDON_16_NULL_HASH_PTR, &hash16)?;
80+
81+
// Write the last 8 elements of Poseidon2([0; 24]) to POSEIDON_24_NULL_HASH_PTR.
82+
let hash24 = self.poseidon2_24.permute([F::ZERO; DIMENSION * 3]);
83+
self.memory_manager
84+
.load_data(POSEIDON_24_NULL_HASH_PTR, &hash24[16..])?;
85+
86+
// Load Public Inputs
87+
88+
// Place public input values starting at PUBLIC_INPUT_START in the public data segment.
89+
self.memory_manager
90+
.load_data(PUBLIC_INPUT_START, public_input)?;
91+
92+
// Compute the initial `fp` (frame pointer) just after the public inputs.
93+
let mut fp = (PUBLIC_INPUT_START + public_input.len())?;
94+
// Align the `fp` offset to the next power of two.
95+
fp.offset = fp.offset.next_power_of_two();
96+
97+
// Load Private Inputs
98+
99+
// Write private inputs starting at the aligned `fp`.
100+
self.memory_manager.load_data(fp, private_input)?;
101+
102+
// Advance `fp` past the private inputs.
103+
fp.offset += private_input.len();
104+
// Ensure `fp` is aligned to `DIMENSION` for vector operations.
105+
fp.offset = fp.offset.next_multiple_of(DIMENSION);
106+
107+
// Compute Allocation Pointers
108+
109+
// Compute the initial allocation pointer for stack memory.
110+
let initial_ap = (fp + self.program.bytecode.starting_frame_memory)?;
111+
112+
// Compute the vectorized allocation pointer, skipping past the non-vector memory.
113+
let mut initial_ap_vec = (initial_ap + no_vec_runtime_memory)?;
114+
// Align the vector allocation to the next multiple of `DIMENSION`.
115+
initial_ap_vec.offset = initial_ap_vec.offset.next_multiple_of(DIMENSION) / DIMENSION;
116+
117+
// Set Initial Registers
118+
119+
// Set the program counter to the start of the code segment.
120+
self.run_context.pc = MemoryAddress::new(CODE_SEGMENT, 0);
121+
122+
// Set the frame pointer to the aligned `fp`.
123+
self.run_context.fp = fp;
124+
125+
// Set the allocation pointer for non-vector memory.
126+
self.run_context.ap = initial_ap;
127+
128+
// Set the allocation pointer for vector memory (in vector units, not field elements).
129+
self.run_context.ap_vectorized = initial_ap_vec;
130+
131+
Ok(())
132+
}
133+
36134
/// Advances the program counter (`pc`) to the next instruction.
37135
///
38136
/// This function embodies the control flow logic of the zkVM. For most instructions,
@@ -147,11 +245,10 @@ impl<Perm16, Perm24> VirtualMachine<Perm16, Perm24> {
147245
/// 2. **Register Update:** If the execution phase completes successfully, this function then
148246
/// calls `update_registers` to advance the program counter (`pc`) and frame pointer (`fp`)
149247
/// to prepare for the next instruction.
150-
pub fn run_instruction(&mut self, instruction: &Instruction) -> Result<(), VirtualMachineError>
151-
where
152-
Perm16: Permutation<[F; 2 * DIMENSION]>,
153-
Perm24: Permutation<[F; 3 * DIMENSION]>,
154-
{
248+
pub fn run_instruction(
249+
&mut self,
250+
instruction: &Instruction,
251+
) -> Result<(), VirtualMachineError> {
155252
// Execute the instruction.
156253
instruction.execute(
157254
&self.run_context,

0 commit comments

Comments
 (0)