Skip to content

Commit 6ad5d8c

Browse files
committed
cherry pick trie node fix
1 parent 7bc17a2 commit 6ad5d8c

File tree

7 files changed

+164
-9
lines changed

7 files changed

+164
-9
lines changed

Cargo.toml.gpu

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ metrics-util = "0.17"
6363
metrics-tracing-context = "0.16.0"
6464
p3-field = { git = "ssh://[email protected]/scroll-tech/plonky3-gpu.git", tag = "v0.2.0" }
6565
rayon = "1.10"
66+
reqwest = "0.12"
6667
rkyv = "0.8"
6768
serde = { version = "1", default-features = false, features = ["derive"] }
6869
serde_json = { version = "1.0" }

crates/integration/tests/chunk_circuit.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ fn exec_chunk(task: &ChunkProvingTask) -> eyre::Result<(ExecutionResult, u64)> {
2323
);
2424
let stats = task.stats();
2525
println!("chunk stats {:#?}", stats);
26-
ChunkProverType::metadata_with_prechecks(task)?;
26+
let mut task = task.clone();
27+
let task = &mut task;
28+
scroll_zkvm_prover::ChunkProver::metadata_with_prechecks(task)?;
2729
let stdin = task.build_guest_input()?;
2830
let exec_result = utils::vm::execute_guest(config, app_exe, &stdin, &Default::default())?;
2931
let cycle_count = exec_result.total_cycle as u64;

crates/prover/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,21 @@ openvm-native-recursion = { workspace = true }
2626
openvm-sdk = { workspace = true }
2727
openvm-stark-sdk = { workspace = true, default-features = false }
2828

29+
backon = { version = "1.5", features = ["std-blocking-sleep"] }
2930
base64 = "0.22"
3031
git-version = "0.3.5"
3132
hex = "0.4"
3233
munge = "=0.4.1"
3334
once_cell = "1.20"
35+
reqwest = { workspace = true, features = ["json", "blocking"] }
3436
serde = "1.0"
3537
serde_json = "1.0"
3638
serde_stacker = "0.1"
3739
thiserror = "2.0"
3840
toml = "0.8"
3941
revm = "19.0"
4042
c-kzg = { version = "1.0", features = ["serde"] }
43+
regex = "1.11.1"
4144

4245
[dev-dependencies]
4346
eyre = "0.6"

crates/prover/src/prover/chunk.rs

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use scroll_zkvm_circuit_input_types::chunk::{ArchivedChunkWitness, ChunkWitness, execute};
2-
31
use crate::{
42
Error, Prover, ProverType,
53
commitments::{chunk, chunk_rv32},
@@ -8,7 +6,9 @@ use crate::{
86
};
97

108
use super::Commitments;
11-
9+
use alloy_primitives::B256;
10+
use scroll_zkvm_circuit_input_types::chunk::{ArchivedChunkWitness, ChunkWitness, execute};
11+
use std::{sync::LazyLock, time::Duration};
1212
pub struct ChunkCircuit;
1313
pub struct ChunkCircuitRv32;
1414

@@ -32,6 +32,13 @@ pub type ChunkProverRv32 = Prover<ChunkProverTypeRv32>;
3232

3333
pub struct GenericChunkProverType<C: Commitments>(std::marker::PhantomData<C>);
3434

35+
static PROVER_DB_GET_ENDPOINT: LazyLock<Option<String>> = LazyLock::new(|| {
36+
std::env::var("PROVER_DB_GET_ENDPOINT")
37+
.as_deref()
38+
.ok()
39+
.map(|s| s.trim_end_matches("/").to_string())
40+
});
41+
3542
impl<C: Commitments> ProverType for GenericChunkProverType<C> {
3643
const NAME: &'static str = "chunk";
3744

@@ -82,9 +89,100 @@ impl<C: Commitments> ProverType for GenericChunkProverType<C> {
8289
))
8390
})?;
8491

85-
let chunk_info = execute(chunk_witness)
86-
.map_err(|e| Error::GenProof(format!("{}: {}", err_prefix, e)))?;
92+
let chunk_info = execute(chunk_witness).map_err(|e| {
93+
println!("display {}", e);
94+
println!("debug {:?}", e);
95+
Error::GenProof(format!("{}: {}", err_prefix, e))
96+
})?;
8797

8898
Ok(ChunkProofMetadata { chunk_info })
8999
}
90100
}
101+
102+
#[tracing::instrument]
103+
pub(crate) fn fetch_missing_node(hash: B256) -> Result<sbv_primitives::Bytes, String> {
104+
use backon::{BlockingRetryableWithContext, ExponentialBuilder};
105+
106+
const RETRY_POLICY: ExponentialBuilder = ExponentialBuilder::new()
107+
.with_min_delay(Duration::from_millis(100))
108+
.with_max_delay(Duration::from_secs(10))
109+
.with_max_times(3);
110+
111+
static CLIENT: LazyLock<reqwest::blocking::Client> = LazyLock::new(|| {
112+
reqwest::blocking::ClientBuilder::new()
113+
.timeout(Duration::from_secs(10))
114+
.build()
115+
.expect("Failed to create reqwest client")
116+
});
117+
118+
let body = serde_json::json!({
119+
"jsonrpc": "2.0",
120+
"id": 1,
121+
"method": "debug_dbGet",
122+
"params": [hash],
123+
});
124+
tracing::debug!("request_body: {body:?}");
125+
126+
#[derive(serde::Deserialize)]
127+
//#[serde(untagged)]
128+
enum Response {
129+
result(sbv_primitives::Bytes),
130+
error { code: i64, message: String },
131+
}
132+
// Define the JSON-RPC response structure
133+
#[derive(serde::Deserialize)]
134+
struct JsonRpcResponse {
135+
jsonrpc: String,
136+
id: serde_json::Value, // Use Value to handle numeric or string IDs
137+
#[serde(flatten)]
138+
result: Response, // Use flatten to handle result or error
139+
}
140+
141+
// struct Response {
142+
// jsonrpc: String,
143+
// id: u32,
144+
// result: sbv_primitives::Bytes,
145+
//}
146+
147+
let client = reqwest::blocking::ClientBuilder::new()
148+
.timeout(Duration::from_secs(10))
149+
.build()
150+
.expect("Failed to create reqwest client");
151+
let endpoint = "https://ancient-smart-sunset.scroll-mainnet.quiknode.pro/310e39a5b18fbd648f52b6e7f763fd40ec94d93f";
152+
let (_, result) = {
153+
|body| {
154+
let result = client
155+
.post(endpoint)
156+
.json(body)
157+
.send()
158+
.and_then(|r| r.error_for_status())
159+
.and_then(|r| {
160+
// println!("r: {:?}", r.text());
161+
r.json::<JsonRpcResponse>()
162+
// panic!();
163+
});
164+
(body, result)
165+
}
166+
}
167+
.retry(RETRY_POLICY)
168+
.notify(|err, dur| {
169+
tracing::debug!("retrying {err:?} after {dur:?}");
170+
})
171+
.context(&body)
172+
.call();
173+
174+
match result {
175+
Ok(r) => {
176+
match r.result {
177+
// 1. success: {"jsonrpc":"2.0","id":1,"result":"0xe19f3c8dfeea98e16e7fcafcce43929240f3ff23ad27fc7a81d5368b0e9eb6876a32"}
178+
Response::result(bytes) => Ok(bytes),
179+
// 2. method not supported: {"jsonrpc":"2.0","id":1,"error":{"code":-32604,"message":"this request method is not supported"}}
180+
// 3. node not found: {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"leveldb: not found"}}
181+
Response::error { code, message } => {
182+
Err(format!("error from RPC: {code} {message}"))
183+
}
184+
}
185+
}
186+
Err(e) => Err(format!("failed after max retries, last err: {e}")),
187+
}
188+
}

crates/prover/src/prover/mod.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ impl<Type: ProverType> Prover<Type> {
210210
&self,
211211
task: &Type::ProvingTask,
212212
) -> Result<WrappedProof<Type::ProofMetadata>, Error> {
213+
let mut task = task.clone();
214+
let task = &mut task;
213215
let task_id = task.identifier();
214216

215217
// Try reading proof from cache if available, and early return in that case.
@@ -225,6 +227,7 @@ impl<Type: ProverType> Prover<Type> {
225227

226228
// Generate a new proof.
227229
assert!(!Type::EVM, "Prover::gen_proof not for EVM-prover");
230+
228231
let metadata = Self::metadata_with_prechecks(task)?;
229232
let proof = self.gen_proof_stark(task)?;
230233
let wrapped_proof = WrappedProof::new(metadata, proof, Some(self.get_app_vk().as_slice()));
@@ -249,6 +252,8 @@ impl<Type: ProverType> Prover<Type> {
249252
&self,
250253
task: &Type::ProvingTask,
251254
) -> Result<WrappedProof<Type::ProofMetadata>, Error> {
255+
let mut task = task.clone();
256+
let task = &mut task;
252257
let task_id = task.identifier();
253258

254259
// Try reading proof from cache if available, and early return in that case.
@@ -283,8 +288,47 @@ impl<Type: ProverType> Prover<Type> {
283288

284289
/// Validate some pre-checks on the proving task and construct proof metadata.
285290
#[instrument("Prover::metadata_with_prechecks", skip_all, fields(?task_id = task.identifier()))]
286-
pub fn metadata_with_prechecks(task: &Type::ProvingTask) -> Result<Type::ProofMetadata, Error> {
287-
Type::metadata_with_prechecks(task)
291+
pub fn metadata_with_prechecks(
292+
task: &mut Type::ProvingTask,
293+
) -> Result<Type::ProofMetadata, Error> {
294+
const MAX_FETCH_NODES_ATTEMPTS: usize = 15;
295+
let mut attempts = 0;
296+
loop {
297+
let result = Type::metadata_with_prechecks(task);
298+
match result {
299+
Ok(metadata) => {
300+
return Ok(metadata);
301+
}
302+
Err(e) => {
303+
let pattern = r"SparseTrieError\(BlindedNode \{ path: Nibbles\((0x[0-9a-fA-F]+)\), hash: (0x[0-9a-fA-F]+) \}\)";
304+
let re = regex::Regex::new(pattern).unwrap();
305+
match re.captures(format!("{e}").as_str()) {
306+
None => {
307+
return Err(e);
308+
}
309+
Some(caps) => {
310+
let hash = caps[2].to_string();
311+
println!("missing trie hash {hash}");
312+
313+
attempts += 1;
314+
if attempts >= MAX_FETCH_NODES_ATTEMPTS {
315+
return Err(Error::GenProof(format!(
316+
"failed to fetch nodes after {MAX_FETCH_NODES_ATTEMPTS} attempts: {e}"
317+
)));
318+
}
319+
320+
let b256 = hash.parse::<sbv_primitives::B256>().expect("should be hex");
321+
let node =
322+
crate::prover::chunk::fetch_missing_node(b256).map_err(|e| {
323+
Error::GenProof(format!("failed to fetch missing node: {e}",))
324+
})?;
325+
tracing::warn!("missing node fetched: {node}");
326+
task.insert_state(node);
327+
}
328+
}
329+
}
330+
}
331+
}
288332
}
289333

290334
/// Verify a [root proof][root_proof].

crates/prover/src/task/chunk.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ impl ProvingTask for ChunkProvingTask {
7575
ForkName::from(self.fork_name.as_str())
7676
}
7777

78+
fn insert_state(&mut self, node: alloy_primitives::Bytes) {
79+
self.block_witnesses[0].states.push(node);
80+
}
81+
7882
fn build_guest_input(&self) -> Result<StdIn, rkyv::rancor::Error> {
7983
let witness = ChunkWitness {
8084
blocks: self.block_witnesses.to_vec(),

crates/prover/src/task/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ pub mod bundle;
1515

1616
/// Every proving task must have an identifier. The identifier will be appended to a prefix while
1717
/// storing/reading proof to/from disc.
18-
pub trait ProvingTask: serde::de::DeserializeOwned {
18+
pub trait ProvingTask: serde::de::DeserializeOwned + Clone {
1919
fn identifier(&self) -> String;
2020

2121
fn build_guest_input(&self) -> Result<StdIn, rkyv::rancor::Error>;
2222

2323
fn fork_name(&self) -> ForkName;
24+
25+
// adhoc
26+
fn insert_state(&mut self, witness: sbv_primitives::Bytes) {}
2427
}
2528

2629
/// Flatten a [`WrappedProof`] and split the proof from the public values. We also split out the

0 commit comments

Comments
 (0)