Skip to content

Commit 54a037d

Browse files
authored
feat: Untrusted/Trusted Advice (#987)
* Add private inputs * Add private_input to the memory * Changed private input region * Update tracer * Update zkvm to include private input * Left 4 sumchecks to complete the verification * Verification passes with cheating (Giving the private_input value to the verifier) * Commitment matches with the evaluation * Verify the commitment * More changes * Refactor code and move evaluation proof and verification to the latest stage * Refactoring state_manager * Add dory commit with no hint * Refactor jolt_dag * Generate only one evaluation point proof for advice commitment * Refactor provable macro * Refactor code * Add TrustedAdvice to macros and tracer * Add TrustedAdvice to jolt zkvm * Commit only to a vector of length equal to untrusted_advice * Increase untrusted_advice_len by 1 * Fixing untrusted_advice issue when remapping to trusted_advice_start * More log * All sumchecks pass correctly * Add more logs * Using selectors * Pass all tests * Combine advice_openning proofs * Remove transcript.clone() for advice prove/verify * Use specifc dory params for each advice type
1 parent 3812be1 commit 54a037d

File tree

39 files changed

+1730
-131
lines changed

39 files changed

+1730
-131
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ members = [
7373
"examples/random/guest",
7474
"examples/malloc",
7575
"examples/malloc/guest",
76+
"examples/merkle-tree",
77+
"examples/merkle-tree/guest",
7678
]
7779

7880
[features]

common/src/attributes.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use syn::{Lit, Meta, MetaNameValue, NestedMeta};
55

66
#[cfg(feature = "std")]
77
use crate::constants::{
8-
DEFAULT_MAX_INPUT_SIZE, DEFAULT_MAX_OUTPUT_SIZE, DEFAULT_MAX_TRACE_LENGTH, DEFAULT_MEMORY_SIZE,
8+
DEFAULT_MAX_INPUT_SIZE, DEFAULT_MAX_OUTPUT_SIZE, DEFAULT_MAX_TRACE_LENGTH,
9+
DEFAULT_MAX_TRUSTED_ADVICE_SIZE, DEFAULT_MAX_UNTRUSTED_ADVICE_SIZE, DEFAULT_MEMORY_SIZE,
910
DEFAULT_STACK_SIZE,
1011
};
1112

@@ -17,6 +18,8 @@ pub struct Attributes {
1718
pub stack_size: u64,
1819
pub max_input_size: u64,
1920
pub max_output_size: u64,
21+
pub max_trusted_advice_size: u64,
22+
pub max_untrusted_advice_size: u64,
2023
pub max_trace_length: u64,
2124
}
2225

@@ -40,6 +43,12 @@ pub fn parse_attributes(attr: &Vec<NestedMeta>) -> Attributes {
4043
"stack_size" => attributes.insert("stack_size", value),
4144
"max_input_size" => attributes.insert("max_input_size", value),
4245
"max_output_size" => attributes.insert("max_output_size", value),
46+
"max_trusted_advice_size" => {
47+
attributes.insert("max_trusted_advice_size", value)
48+
}
49+
"max_untrusted_advice_size" => {
50+
attributes.insert("max_untrusted_advice_size", value)
51+
}
4352
"max_trace_length" => attributes.insert("max_trace_length", value),
4453
_ => panic!("invalid attribute"),
4554
};
@@ -67,6 +76,12 @@ pub fn parse_attributes(attr: &Vec<NestedMeta>) -> Attributes {
6776
let max_output_size = *attributes
6877
.get("max_output_size")
6978
.unwrap_or(&DEFAULT_MAX_OUTPUT_SIZE);
79+
let max_trusted_advice_size = *attributes
80+
.get("max_trusted_advice_size")
81+
.unwrap_or(&DEFAULT_MAX_TRUSTED_ADVICE_SIZE);
82+
let max_untrusted_advice_size = *attributes
83+
.get("max_untrusted_advice_size")
84+
.unwrap_or(&DEFAULT_MAX_UNTRUSTED_ADVICE_SIZE);
7085
let max_trace_length = *attributes
7186
.get("max_trace_length")
7287
.unwrap_or(&DEFAULT_MAX_TRACE_LENGTH);
@@ -79,6 +94,8 @@ pub fn parse_attributes(attr: &Vec<NestedMeta>) -> Attributes {
7994
stack_size,
8095
max_input_size,
8196
max_output_size,
97+
max_trusted_advice_size,
98+
max_untrusted_advice_size,
8299
max_trace_length,
83100
}
84101
}

common/src/constants.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ pub const DEFAULT_MEMORY_SIZE: u64 = 32 * 1024 * 1024;
1616
pub const DEFAULT_STACK_SIZE: u64 = 4096;
1717
// 64 byte stack canary. 4 word protection for 32-bit and 2 word for 64-bit
1818
pub const STACK_CANARY_SIZE: u64 = 128;
19+
pub const DEFAULT_MAX_TRUSTED_ADVICE_SIZE: u64 = 4096;
20+
pub const DEFAULT_MAX_UNTRUSTED_ADVICE_SIZE: u64 = 4096;
1921
pub const DEFAULT_MAX_INPUT_SIZE: u64 = 4096;
2022
pub const DEFAULT_MAX_OUTPUT_SIZE: u64 = 4096;
2123
pub const DEFAULT_MAX_TRACE_LENGTH: u64 = 1 << 24;
2224

2325
// Layout of the witness (where || denotes concatenation):
24-
// inputs || outputs || panic || termination || padding || RAM
26+
// trusted_advice || untrusted_advice || inputs || outputs || panic || termination || padding || RAM
2527
// Layout of VM memory:
26-
// peripheral devices || inputs || outputs || panic || termination || padding || RAM
28+
// peripheral devices || trusted_advice || untrusted_advice || inputs || outputs || panic || termination || padding || RAM
2729
// Notably, we want to be able to map the VM memory address space to witness indices
2830
// using a constant shift, namely (RAM_WITNESS_OFFSET + RAM_START_ADDRESS)

common/src/jolt_device.rs

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use alloc::vec::Vec;
77
use std::vec::Vec;
88

99
use crate::constants::{
10-
DEFAULT_MAX_INPUT_SIZE, DEFAULT_MAX_OUTPUT_SIZE, DEFAULT_MEMORY_SIZE, DEFAULT_STACK_SIZE,
11-
RAM_START_ADDRESS,
10+
DEFAULT_MAX_INPUT_SIZE, DEFAULT_MAX_OUTPUT_SIZE, DEFAULT_MAX_TRUSTED_ADVICE_SIZE,
11+
DEFAULT_MAX_UNTRUSTED_ADVICE_SIZE, DEFAULT_MEMORY_SIZE, DEFAULT_STACK_SIZE, RAM_START_ADDRESS,
1212
};
1313

1414
#[allow(clippy::too_long_first_doc_paragraph)]
@@ -28,6 +28,8 @@ use crate::constants::{
2828
)]
2929
pub struct JoltDevice {
3030
pub inputs: Vec<u8>,
31+
pub trusted_advice: Vec<u8>,
32+
pub untrusted_advice: Vec<u8>,
3133
pub outputs: Vec<u8>,
3234
pub panic: bool,
3335
pub memory_layout: MemoryLayout,
@@ -37,6 +39,8 @@ impl JoltDevice {
3739
pub fn new(memory_config: &MemoryConfig) -> Self {
3840
Self {
3941
inputs: Vec::new(),
42+
trusted_advice: Vec::new(),
43+
untrusted_advice: Vec::new(),
4044
outputs: Vec::new(),
4145
panic: false,
4246
memory_layout: MemoryLayout::new(memory_config),
@@ -55,6 +59,20 @@ impl JoltDevice {
5559
} else {
5660
self.inputs[internal_address]
5761
}
62+
} else if self.is_trusted_advice(address) {
63+
let internal_address = self.convert_trusted_advice_read_address(address);
64+
if self.trusted_advice.len() <= internal_address {
65+
0
66+
} else {
67+
self.trusted_advice[internal_address]
68+
}
69+
} else if self.is_untrusted_advice(address) {
70+
let internal_address = self.convert_untrusted_advice_read_address(address);
71+
if self.untrusted_advice.len() <= internal_address {
72+
0
73+
} else {
74+
self.untrusted_advice[internal_address]
75+
}
5876
} else if self.is_output(address) {
5977
let internal_address = self.convert_write_address(address);
6078
if self.outputs.len() <= internal_address {
@@ -91,6 +109,16 @@ impl JoltDevice {
91109
address >= self.memory_layout.input_start && address < self.memory_layout.input_end
92110
}
93111

112+
pub fn is_trusted_advice(&self, address: u64) -> bool {
113+
address >= self.memory_layout.trusted_advice_start
114+
&& address < self.memory_layout.trusted_advice_end
115+
}
116+
117+
pub fn is_untrusted_advice(&self, address: u64) -> bool {
118+
address >= self.memory_layout.untrusted_advice_start
119+
&& address < self.memory_layout.untrusted_advice_end
120+
}
121+
94122
pub fn is_output(&self, address: u64) -> bool {
95123
address >= self.memory_layout.output_start && address < self.memory_layout.termination
96124
}
@@ -107,6 +135,14 @@ impl JoltDevice {
107135
(address - self.memory_layout.input_start) as usize
108136
}
109137

138+
fn convert_trusted_advice_read_address(&self, address: u64) -> usize {
139+
(address - self.memory_layout.trusted_advice_start) as usize
140+
}
141+
142+
fn convert_untrusted_advice_read_address(&self, address: u64) -> usize {
143+
(address - self.memory_layout.untrusted_advice_start) as usize
144+
}
145+
110146
fn convert_write_address(&self, address: u64) -> usize {
111147
(address - self.memory_layout.output_start) as usize
112148
}
@@ -115,6 +151,8 @@ impl JoltDevice {
115151
#[derive(Debug, Copy, Clone)]
116152
pub struct MemoryConfig {
117153
pub max_input_size: u64,
154+
pub max_trusted_advice_size: u64,
155+
pub max_untrusted_advice_size: u64,
118156
pub max_output_size: u64,
119157
pub stack_size: u64,
120158
pub memory_size: u64,
@@ -125,6 +163,8 @@ impl Default for MemoryConfig {
125163
fn default() -> Self {
126164
Self {
127165
max_input_size: DEFAULT_MAX_INPUT_SIZE,
166+
max_trusted_advice_size: DEFAULT_MAX_TRUSTED_ADVICE_SIZE,
167+
max_untrusted_advice_size: DEFAULT_MAX_UNTRUSTED_ADVICE_SIZE,
128168
max_output_size: DEFAULT_MAX_OUTPUT_SIZE,
129169
stack_size: DEFAULT_STACK_SIZE,
130170
memory_size: DEFAULT_MEMORY_SIZE,
@@ -139,6 +179,12 @@ impl Default for MemoryConfig {
139179
pub struct MemoryLayout {
140180
/// The total size of the elf's sections, including the .text, .data, .rodata, and .bss sections.
141181
pub program_size: u64,
182+
pub max_trusted_advice_size: u64,
183+
pub trusted_advice_start: u64,
184+
pub trusted_advice_end: u64,
185+
pub max_untrusted_advice_size: u64,
186+
pub untrusted_advice_start: u64,
187+
pub untrusted_advice_end: u64,
142188
pub max_input_size: u64,
143189
pub max_output_size: u64,
144190
pub input_start: u64,
@@ -163,7 +209,25 @@ impl core::fmt::Debug for MemoryLayout {
163209
f.debug_struct("MemoryLayout")
164210
.field("program_size", &self.program_size)
165211
.field("max_input_size", &self.max_input_size)
212+
.field("max_trusted_advice_size", &self.max_trusted_advice_size)
213+
.field("max_untrusted_advice_size", &self.max_untrusted_advice_size)
166214
.field("max_output_size", &self.max_output_size)
215+
.field(
216+
"trusted_advice_start",
217+
&format_args!("{:#X}", self.trusted_advice_start),
218+
)
219+
.field(
220+
"trusted_advice_end",
221+
&format_args!("{:#X}", self.trusted_advice_end),
222+
)
223+
.field(
224+
"untrusted_advice_start",
225+
&format_args!("{:#X}", self.untrusted_advice_start),
226+
)
227+
.field(
228+
"untrusted_advice_end",
229+
&format_args!("{:#X}", self.untrusted_advice_end),
230+
)
167231
.field("input_start", &format_args!("{:#X}", self.input_start))
168232
.field("input_end", &format_args!("{:#X}", self.input_end))
169233
.field("output_start", &format_args!("{:#X}", self.output_start))
@@ -200,6 +264,8 @@ impl MemoryLayout {
200264
}
201265
} // Must be 8-byte aligned
202266

267+
let max_trusted_advice_size = align_up(config.max_trusted_advice_size, 8);
268+
let max_untrusted_advice_size = align_up(config.max_untrusted_advice_size, 8);
203269
let max_input_size = align_up(config.max_input_size, 8);
204270
let max_output_size = align_up(config.max_output_size, 8);
205271
let stack_size = align_up(config.stack_size, 8);
@@ -208,7 +274,9 @@ impl MemoryLayout {
208274
// Adds 16 to account for panic bit and termination bit
209275
// (they each occupy one full 8-byte word)
210276
let io_region_bytes = max_input_size
211-
.checked_add(max_output_size)
277+
.checked_add(max_trusted_advice_size)
278+
.and_then(|s| s.checked_add(max_untrusted_advice_size))
279+
.and_then(|s| s.checked_add(max_output_size))
212280
.and_then(|s| s.checked_add(16))
213281
.expect("I/O region size overflow");
214282

@@ -220,9 +288,20 @@ impl MemoryLayout {
220288
let io_bytes = io_region_words
221289
.checked_mul(8)
222290
.expect("I/O region byte count overflow");
223-
let input_start = RAM_START_ADDRESS
291+
292+
let trusted_advice_start = RAM_START_ADDRESS
224293
.checked_sub(io_bytes)
225294
.expect("I/O region exceeds RAM_START_ADDRESS");
295+
let trusted_advice_end = trusted_advice_start
296+
.checked_add(max_trusted_advice_size)
297+
.expect("trusted_advice_end overflow");
298+
299+
let untrusted_advice_start = trusted_advice_end;
300+
let untrusted_advice_end = untrusted_advice_start
301+
.checked_add(max_untrusted_advice_size)
302+
.expect("untrusted_advice_end overflow");
303+
304+
let input_start = untrusted_advice_end;
226305
let input_end = input_start
227306
.checked_add(max_input_size)
228307
.expect("input_end overflow");
@@ -235,6 +314,7 @@ impl MemoryLayout {
235314
let io_end = termination.checked_add(8).expect("io_end overflow");
236315

237316
let program_size = config.program_size.unwrap();
317+
238318
// stack grows downwards (decreasing addresses) from the bytecode_end + stack_size up to bytecode_end
239319
let stack_end = RAM_START_ADDRESS
240320
.checked_add(program_size)
@@ -250,8 +330,14 @@ impl MemoryLayout {
250330

251331
Self {
252332
program_size,
333+
max_trusted_advice_size,
334+
max_untrusted_advice_size,
253335
max_input_size,
254336
max_output_size,
337+
trusted_advice_start,
338+
trusted_advice_end,
339+
untrusted_advice_start,
340+
untrusted_advice_end,
255341
input_start,
256342
input_end,
257343
output_start,

examples/merkle-tree/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "merkle-tree"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
jolt-sdk = { path = "../../jolt-sdk", features = ["host"] }
8+
tracing-subscriber = "0.3"
9+
tracing = "0.1"
10+
jolt-inlines-sha2 = { path = "../../jolt-inlines/sha2", features = ["host"] }
11+
guest = { package = "merkle-tree-guest", path = "./guest" }
12+
13+
hex = "0.4.3"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "merkle-tree-guest"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[features]
7+
guest = []
8+
9+
[dependencies]
10+
jolt = { package = "jolt-sdk", path = "../../../jolt-sdk", features = [] }
11+
jolt-inlines-sha2 = { package = "jolt-inlines-sha2", path = "../../../jolt-inlines/sha2" }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#![cfg_attr(feature = "guest", no_std)]
2+
3+
use core::ops::Deref;
4+
5+
#[jolt::provable(memory_size = 10240, max_trace_length = 65536)]
6+
fn merkle_tree(
7+
first_input: &[u8],
8+
second_input: jolt::TrustedAdvice<[u8; 32]>,
9+
third_input: jolt::UntrustedAdvice<[u8; 32]>,
10+
) -> [u8; 32] {
11+
// Compute hash of first input
12+
let hash1 = jolt_inlines_sha2::Sha256::digest(first_input);
13+
let hash2 = jolt_inlines_sha2::Sha256::digest(second_input.deref());
14+
let hash3 = jolt_inlines_sha2::Sha256::digest(third_input.deref());
15+
16+
// Concatenate all three hashes
17+
let mut concatenated = [0u8; 96];
18+
concatenated[..32].copy_from_slice(&hash1);
19+
concatenated[32..64].copy_from_slice(&hash2);
20+
concatenated[64..].copy_from_slice(&hash3);
21+
22+
// Hash the concatenated result and return
23+
jolt_inlines_sha2::Sha256::digest(&concatenated)
24+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![cfg_attr(feature = "guest", no_std)]
2+
#![no_main]
3+
4+
#[allow(unused_imports)]
5+
use merkle_tree_guest::*;

0 commit comments

Comments
 (0)