Skip to content

Commit 17a9d25

Browse files
naureAurélien Nicolas
andauthored
feat/private-input: Unconstrained memory init (#617)
_Issues #614 and #607_ Initialize memory with unconstrained prover hints. - It is practically identical to the zero-init circuit; just without the zero-init part. - The address range must be contiguous, but its size is dynamic. - The `u32` range check is already done by the LOAD and STORE circuits. e2e integration will be done in another PR. Need #608 to configure the address space. --------- Co-authored-by: Aurélien Nicolas <[email protected]>
1 parent 12ed922 commit 17a9d25

File tree

5 files changed

+48
-31
lines changed

5 files changed

+48
-31
lines changed

ceno_zkvm/src/instructions/riscv/memory/load.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ impl<E: ExtensionField, I: RIVInstruction> Instruction<E> for LoadInstruction<E,
8484
) -> Result<Self::InstructionConfig, ZKVMError> {
8585
let rs1_read = UInt::new_unchecked(|| "rs1_read", circuit_builder)?; // unsigned 32-bit value
8686
let imm = circuit_builder.create_witin(|| "imm"); // signed 12-bit value
87+
// Memory initialization is not guaranteed to contain u32. Range-check it here.
8788
let memory_read = UInt::new(|| "memory_read", circuit_builder)?;
8889

8990
let memory_addr = match I::INST_KIND {

ceno_zkvm/src/instructions/riscv/memory/store.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ impl<E: ExtensionField, I: RIVInstruction, const N_ZEROS: usize> Instruction<E>
6565
) -> Result<Self::InstructionConfig, ZKVMError> {
6666
let rs1_read = UInt::new_unchecked(|| "rs1_read", circuit_builder)?; // unsigned 32-bit value
6767
let rs2_read = UInt::new_unchecked(|| "rs2_read", circuit_builder)?;
68+
// Memory initialization is not guaranteed to contain u32. Range-check it here.
6869
let prev_memory_value = UInt::new(|| "prev_memory_value", circuit_builder)?;
6970
let imm = circuit_builder.create_witin(|| "imm"); // signed 12-bit value
7071

ceno_zkvm/src/tables/ram.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ceno_emul::{Addr, VMState, WORD_SIZE};
1+
use ceno_emul::{Addr, VMState};
22
use ram_circuit::{DynVolatileRamCircuit, NonVolatileRamCircuit, PubIORamCircuit};
33

44
use crate::{
@@ -11,11 +11,12 @@ mod ram_impl;
1111
pub use ram_circuit::{DynVolatileRamTable, MemFinalRecord, MemInitRecord, NonVolatileTable};
1212

1313
#[derive(Clone)]
14-
pub struct MemTable;
14+
pub struct DynMemTable;
1515

16-
impl DynVolatileRamTable for MemTable {
16+
impl DynVolatileRamTable for DynMemTable {
1717
const RAM_TYPE: RAMType = RAMType::Memory;
1818
const V_LIMBS: usize = 1; // See `MemoryExpr`.
19+
const ZERO_INIT: bool = true;
1920

2021
fn offset_addr(params: &ProgramParams) -> Addr {
2122
params.platform.ram_start()
@@ -26,17 +27,32 @@ impl DynVolatileRamTable for MemTable {
2627
}
2728

2829
fn name() -> &'static str {
29-
"MemTable"
30+
"DynMemTable"
31+
}
32+
}
33+
34+
pub type DynMemCircuit<E> = DynVolatileRamCircuit<E, DynMemTable>;
35+
36+
#[derive(Clone)]
37+
pub struct PrivateMemTable;
38+
impl DynVolatileRamTable for PrivateMemTable {
39+
const RAM_TYPE: RAMType = RAMType::Memory;
40+
const V_LIMBS: usize = 1; // See `MemoryExpr`.
41+
const ZERO_INIT: bool = false;
42+
43+
fn offset_addr(params: &ProgramParams) -> Addr {
44+
params.platform.ram_start()
45+
}
46+
47+
fn end_addr(params: &ProgramParams) -> Addr {
48+
params.platform.ram_end()
3049
}
3150

32-
fn max_len(params: &ProgramParams) -> usize {
33-
let max_size =
34-
(Self::end_addr(params) - Self::offset_addr(params)).div_ceil(WORD_SIZE as u32) as Addr;
35-
1 << (u32::BITS - 1 - max_size.leading_zeros()) // prev_power_of_2
51+
fn name() -> &'static str {
52+
"PrivateMemTable"
3653
}
3754
}
38-
39-
pub type MemCircuit<E> = DynVolatileRamCircuit<E, MemTable>;
55+
pub type PrivateMemCircuit<E> = DynVolatileRamCircuit<E, PrivateMemTable>;
4056

4157
/// RegTable, fix size without offset
4258
#[derive(Clone)]

ceno_zkvm/src/tables/ram/ram_circuit.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,18 @@ impl<E: ExtensionField, NVRAM: NonVolatileTable + Send + Sync + Clone> TableCirc
141141
pub trait DynVolatileRamTable {
142142
const RAM_TYPE: RAMType;
143143
const V_LIMBS: usize;
144+
const ZERO_INIT: bool;
144145

145146
fn offset_addr(params: &ProgramParams) -> Addr;
146147
fn end_addr(params: &ProgramParams) -> Addr;
147148

148149
fn name() -> &'static str;
149150

150-
fn max_len(params: &ProgramParams) -> usize;
151+
fn max_len(params: &ProgramParams) -> usize {
152+
let max_size =
153+
(Self::end_addr(params) - Self::offset_addr(params)).div_ceil(WORD_SIZE as u32) as Addr;
154+
1 << (u32::BITS - 1 - max_size.leading_zeros()) // prev_power_of_2
155+
}
151156

152157
fn addr(params: &ProgramParams, entry_index: usize) -> Addr {
153158
Self::offset_addr(params) + (entry_index * WORD_SIZE) as Addr
@@ -156,8 +161,12 @@ pub trait DynVolatileRamTable {
156161

157162
/// DynVolatileRamCircuit initializes and finalizes memory
158163
/// - at witnessed addresses, in a contiguous range chosen by the prover,
159-
/// - with zeros as initial content,
164+
/// - with zeros as initial content if ZERO_INIT,
160165
/// - with witnessed final content that the program wrote.
166+
///
167+
/// If not ZERO_INIT:
168+
/// - The initial content is an unconstrained prover hint.
169+
/// - The final content is equal to this initial content.
161170
pub struct DynVolatileRamCircuit<E, R>(PhantomData<(E, R)>);
162171

163172
impl<E: ExtensionField, DVRAM: DynVolatileRamTable + Send + Sync + Clone> TableCircuit<E>

ceno_zkvm/src/tables/ram/ram_impl.rs

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,17 @@ impl<DVRAM: DynVolatileRamTable + Send + Sync + Clone> DynVolatileRamTableConfig
303303
.collect::<Vec<WitIn>>();
304304
let final_cycle = cb.create_witin(|| "final_cycle");
305305

306+
let final_expr = final_v.iter().map(|v| v.expr()).collect_vec();
307+
let init_expr = if DVRAM::ZERO_INIT {
308+
vec![Expression::ZERO; DVRAM::V_LIMBS]
309+
} else {
310+
final_expr.clone()
311+
};
312+
306313
let init_table = [
307314
vec![(DVRAM::RAM_TYPE as usize).into()],
308315
vec![addr.expr()],
309-
vec![Expression::ZERO],
316+
init_expr,
310317
vec![Expression::ZERO], // Initial cycle.
311318
]
312319
.concat();
@@ -315,7 +322,7 @@ impl<DVRAM: DynVolatileRamTable + Send + Sync + Clone> DynVolatileRamTableConfig
315322
// a v t
316323
vec![(DVRAM::RAM_TYPE as usize).into()],
317324
vec![addr.expr()],
318-
final_v.iter().map(|v| v.expr()).collect_vec(),
325+
final_expr,
319326
vec![final_cycle.expr()],
320327
]
321328
.concat();
@@ -409,20 +416,3 @@ impl<DVRAM: DynVolatileRamTable + Send + Sync + Clone> DynVolatileRamTableConfig
409416
Ok(final_table)
410417
}
411418
}
412-
413-
#[allow(dead_code)]
414-
/// DynUnConstrainRamTableConfig with unconstrain init value and final value
415-
/// dynamic address as witin, relied on augment of knowledge to prove address form
416-
/// do not check init_value
417-
/// TODO implement DynUnConstrainRamTableConfig
418-
#[derive(Clone, Debug)]
419-
pub struct DynUnConstrainRamTableConfig<RAM: DynVolatileRamTable + Send + Sync + Clone> {
420-
addr: WitIn,
421-
422-
init_v: Vec<WitIn>,
423-
424-
final_v: Vec<WitIn>,
425-
final_cycle: WitIn,
426-
427-
phantom: PhantomData<RAM>,
428-
}

0 commit comments

Comments
 (0)