Skip to content

Commit 98a1a4e

Browse files
authored
Merge pull request #228 from zkMIPS/split-large-seg
feat: add support for splitting large segment into small segments
2 parents 262318f + 30d27eb commit 98a1a4e

File tree

8 files changed

+191
-3
lines changed

8 files changed

+191
-3
lines changed

emulator/src/memory.rs

+19
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,25 @@ impl Memory {
322322
self.count = count;
323323
}
324324

325+
pub fn init_memory<'a>(&mut self, addr: u32, v: u32) {
326+
let page_index = addr >> PAGE_ADDR_SIZE;
327+
let page_addr = (addr as usize) & PAGE_ADDR_MASK;
328+
let cached_page = match self.page_lookup(page_index) {
329+
None => {
330+
// allocate the page if we have not already
331+
// Golang may mmap relatively large ranges, but we only allocate just in time.
332+
self.alloc_page(page_index)
333+
}
334+
Some(cached_page) => {
335+
// self.invalidate(addr);
336+
cached_page
337+
}
338+
};
339+
340+
let mut cached_page = cached_page.borrow_mut();
341+
cached_page.data[page_addr..page_addr + 4].copy_from_slice(&v.to_le_bytes());
342+
}
343+
325344
pub fn set_memory_range<'a>(
326345
&mut self,
327346
mut addr: u32,

emulator/src/state.rs

+68
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize};
88
use std::collections::BTreeMap;
99
use std::fmt::{Display, Formatter};
1010
use std::fs;
11+
use std::fs::File;
12+
use std::io::BufReader;
1113
use std::io::{stderr, stdout, Read, Write};
1214
use std::path::Path;
1315

@@ -138,6 +140,72 @@ impl State {
138140
})
139141
}
140142

143+
pub fn load_seg(seg_path: &str) -> (Box<Self>, u64) {
144+
let reader = BufReader::new(File::open(seg_path).unwrap());
145+
let segment: Segment = serde_json::from_reader(reader).unwrap();
146+
let mut s = Box::new(Self {
147+
memory: Box::new(Memory::new()),
148+
registers: Default::default(),
149+
pc: segment.pc,
150+
next_pc: 0,
151+
hi: 0,
152+
lo: 0,
153+
heap: 0,
154+
local_user: 0,
155+
step: 0,
156+
total_step: 0,
157+
cycle: 0,
158+
total_cycle: 0,
159+
brk: 0,
160+
input_stream: segment.input_stream,
161+
input_stream_ptr: segment.input_stream_ptr,
162+
public_values_stream: segment.public_values_stream,
163+
public_values_stream_ptr: segment.public_values_stream_ptr,
164+
exited: false,
165+
exit_code: 0,
166+
dump_info: false,
167+
});
168+
169+
let image = segment.mem_image;
170+
171+
for i in 0..32 {
172+
let data = image.get(&(REGISTERS_START + (i << 2) as u32)).unwrap();
173+
s.registers[i] = data.to_be();
174+
}
175+
176+
s.lo = image
177+
.get(&(REGISTERS_START + (32 << 2) as u32))
178+
.unwrap()
179+
.to_be();
180+
s.hi = image
181+
.get(&(REGISTERS_START + (33 << 2) as u32))
182+
.unwrap()
183+
.to_be();
184+
s.heap = image
185+
.get(&(REGISTERS_START + (34 << 2) as u32))
186+
.unwrap()
187+
.to_be();
188+
s.next_pc = image
189+
.get(&(REGISTERS_START + (36 << 2) as u32))
190+
.unwrap()
191+
.to_be();
192+
193+
s.brk = image
194+
.get(&(REGISTERS_START + (37 << 2) as u32))
195+
.unwrap()
196+
.to_be();
197+
198+
s.local_user = image
199+
.get(&(REGISTERS_START + (38 << 2) as u32))
200+
.unwrap()
201+
.to_be();
202+
203+
for (addr, data) in image {
204+
s.memory.init_memory(addr, data);
205+
}
206+
(s, segment.step)
207+
}
208+
141209
pub fn load_elf(f: &elf::ElfBytes<AnyEndian>) -> Box<Self> {
142210
let mut s = Box::new(Self {
143211
memory: Box::new(Memory::new()),

emulator/src/utils.rs

+49-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ pub fn split_prog_into_segs(
3232
instrumented_state.split_segment(false, seg_path, new_writer);
3333
let new_writer = |name: &str| -> Option<std::fs::File> { File::create(name).ok() };
3434
loop {
35+
let cycles = instrumented_state.step();
3536
if instrumented_state.state.exited {
3637
break;
3738
}
38-
let cycles = instrumented_state.step();
3939
if cycles > (seg_size as isize - 1) as u64 {
4040
instrumented_state.split_segment(true, seg_path, new_writer);
4141
}
@@ -54,3 +54,51 @@ pub fn split_prog_into_segs(
5454
instrumented_state.state,
5555
)
5656
}
57+
58+
pub fn load_segment(seg_file: &str) -> (Box<State>, u64) {
59+
State::load_seg(seg_file)
60+
}
61+
62+
pub fn split_seg_into_segs(
63+
seg_file: &str,
64+
seg_path: &str,
65+
block_path: &str,
66+
seg_size: usize,
67+
) -> (usize, usize, Box<State>) {
68+
let (state, final_step) = load_segment(seg_file);
69+
let mut instrumented_state = InstrumentedState::new(state, block_path.to_string());
70+
log::debug!("start pc: {:X} {}", instrumented_state.state.pc, final_step);
71+
std::fs::create_dir_all(seg_path).unwrap();
72+
let new_writer = |_: &str| -> Option<std::fs::File> { None };
73+
instrumented_state.split_segment(false, seg_path, new_writer);
74+
let new_writer = |name: &str| -> Option<std::fs::File> { File::create(name).ok() };
75+
loop {
76+
let cycles = instrumented_state.step();
77+
if instrumented_state.state.total_step + instrumented_state.state.step == final_step {
78+
break;
79+
}
80+
if cycles > (seg_size as isize - 1) as u64 {
81+
instrumented_state.split_segment(true, seg_path, new_writer);
82+
log::debug!(
83+
"Split at {} : {} into {}",
84+
instrumented_state.state.total_step,
85+
instrumented_state.state.total_cycle,
86+
instrumented_state.pre_segment_id
87+
);
88+
}
89+
}
90+
instrumented_state.split_segment(true, seg_path, new_writer);
91+
log::info!(
92+
"Split done {} : {} into {}",
93+
instrumented_state.state.total_step,
94+
instrumented_state.state.total_cycle,
95+
instrumented_state.pre_segment_id
96+
);
97+
98+
instrumented_state.dump_memory();
99+
(
100+
instrumented_state.state.total_step as usize,
101+
instrumented_state.pre_segment_id as usize,
102+
instrumented_state.state,
103+
)
104+
}

prover/examples/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ members = [
77
"sha2-syscall/host",
88
"keccak/host",
99
"split-seg",
10-
"prove-seg"
10+
"prove-seg",
11+
"prove-large-seg"
1112
]
1213
resolver = "2"
1314

prover/examples/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ BASEDIR=../../../emulator/test-vectors RUST_LOG=info BLOCK_NO=13284491 SEG_FILE_
3333
cargo run --release
3434
```
3535

36+
* Generate proof for specific large segment (Split large segment into small segments)
37+
38+
```
39+
cd ../prove-large-seg
40+
BASEDIR=../../../emulator/test-vectors RUST_LOG=info SEG_FILE=/tmp/output/8 BLOCK_NO=13284491 SEG_OUTPUT=/tmp/output1 SEG_SIZE=16384 cargo run --release
41+
42+
```
43+
3644
### Prove Go sdk code
3745
The SDK provide Read and Commit interface to read input and commit output.
3846
Take sha2-go for example:
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "prove-large-seg"
3+
version = { workspace = true }
4+
edition = { workspace = true }
5+
publish = false
6+
7+
[dependencies]
8+
zkm-prover = { workspace = true }
9+
zkm-emulator = { workspace = true }
10+
zkm-utils = { path = "../utils" }
11+
12+
log = { version = "0.4.14", default-features = false }
13+
serde = { version = "1.0.144", features = ["derive"] }
14+
serde_json = "1.0"
15+
byteorder = "1.5.0"
16+
hex = "0.4"
17+
env_logger = "0.11.5"
18+
anyhow = "1.0.75"
19+
20+
[build-dependencies]
21+
zkm-build = { workspace = true }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use std::env;
2+
use zkm_emulator::utils::{get_block_path, split_seg_into_segs};
3+
use zkm_utils::utils;
4+
5+
fn prove_large_segment() {
6+
let basedir = env::var("BASEDIR").unwrap_or("/tmp/cannon".to_string());
7+
let block = env::var("BLOCK_NO").unwrap_or("".to_string());
8+
let file = env::var("BLOCK_FILE").unwrap_or(String::from(""));
9+
let seg_file = env::var("SEG_FILE").expect("big segment file is missing");
10+
let seg_dir = env::var("SEG_OUTPUT").expect("segment output dir is missing");
11+
let seg_size = env::var("SEG_SIZE").unwrap_or("1024".to_string());
12+
let seg_size = seg_size.parse::<_>().unwrap_or(1usize);
13+
14+
let block_path = get_block_path(&basedir, &block, "");
15+
let (_, seg_num, _) = split_seg_into_segs(&seg_file, &seg_dir, &block_path, seg_size);
16+
17+
let _ = utils::prove_segments(&seg_dir, &basedir, &block, &file, seg_num, 0, vec![]);
18+
}
19+
20+
fn main() {
21+
env_logger::try_init().unwrap_or_default();
22+
prove_large_segment();
23+
}

prover/examples/prove-seg/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::env;
2-
use zkm_prover::utils;
2+
use zkm_utils::utils;
33

44
fn prove_segments() {
55
let basedir = env::var("BASEDIR").unwrap_or("/tmp/cannon".to_string());

0 commit comments

Comments
 (0)