Skip to content

Commit 39bdc11

Browse files
authored
fix(raiko): double check if cached file is valid. (#271)
* Double check if cached file is valid. * fix review comments * fix review comments Signed-off-by: smtmfft <[email protected]> --------- Signed-off-by: smtmfft <[email protected]>
1 parent a90476d commit 39bdc11

File tree

4 files changed

+129
-23
lines changed

4 files changed

+129
-23
lines changed

Diff for: core/src/provider/rpc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{
1515
MerkleProof,
1616
};
1717

18+
#[derive(Clone)]
1819
pub struct RpcBlockDataProvider {
1920
pub provider: ReqwestProvider,
2021
pub client: RpcClient<Http<Client>>,

Diff for: host/src/server/api/v1/proof.rs

+125-22
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use std::{fs::File, path::PathBuf};
33
use axum::{debug_handler, extract::State, routing::post, Json, Router};
44
use raiko_core::{
55
interfaces::{ProofRequest, RaikoError},
6-
provider::rpc::RpcBlockDataProvider,
6+
provider::{rpc::RpcBlockDataProvider, BlockDataProvider},
77
Raiko,
88
};
99
use raiko_lib::{
1010
input::{get_input_path, GuestInput},
11+
utils::{to_header, HeaderHasher},
1112
Measurement,
1213
};
1314
use serde_json::Value;
@@ -51,16 +52,45 @@ fn set_cached_input(
5152
};
5253

5354
let path = get_input_path(dir, block_number, network);
54-
55-
if path.exists() {
56-
return Ok(());
57-
}
55+
info!("caching input for {path:?}");
5856

5957
let file = File::create(&path).map_err(<std::io::Error as Into<HostError>>::into)?;
58+
bincode::serialize_into(file, input).map_err(|e| HostError::Anyhow(e.into()))
59+
}
6060

61-
info!("caching input for {path:?}");
61+
async fn validate_cache_input(
62+
cached_input: Option<GuestInput>,
63+
provider: &RpcBlockDataProvider,
64+
) -> HostResult<GuestInput> {
65+
if let Some(cache_input) = cached_input {
66+
debug!("Using cached input");
67+
let blocks = provider
68+
.get_blocks(&[(cache_input.block_number, false)])
69+
.await?;
70+
let block = blocks
71+
.first()
72+
.ok_or_else(|| RaikoError::RPC("No block data for the requested block".to_owned()))?;
6273

63-
bincode::serialize_into(file, input).map_err(|e| HostError::Anyhow(e.into()))
74+
let cached_block_hash = cache_input.block_hash_reference;
75+
let real_block_hash = block.header.hash.unwrap_or(to_header(&block.header).hash());
76+
debug!(
77+
"cache_block_hash={:?}, real_block_hash={:?}",
78+
cached_block_hash, real_block_hash
79+
);
80+
81+
// double check if cache is valid
82+
if cached_block_hash == real_block_hash {
83+
return Ok(cache_input);
84+
} else {
85+
Err(HostError::InvalidRequestConfig(
86+
"Cached input is not valid".to_owned(),
87+
))
88+
}
89+
} else {
90+
Err(HostError::InvalidRequestConfig(
91+
"Cached input is not enabled".to_owned(),
92+
))
93+
}
6494
}
6595

6696
async fn handle_proof(
@@ -108,21 +138,22 @@ async fn handle_proof(
108138
taiko_chain_spec.clone(),
109139
proof_request.clone(),
110140
);
111-
let input = if let Some(cached_input) = cached_input {
112-
debug!("Using cached input");
113-
cached_input
114-
} else {
115-
memory::reset_stats();
116-
let measurement = Measurement::start("Generating input...", false);
117-
let provider = RpcBlockDataProvider::new(
118-
&taiko_chain_spec.rpc.clone(),
119-
proof_request.block_number - 1,
120-
)?;
121-
let input = raiko.generate_input(provider).await?;
122-
let input_time = measurement.stop_with("=> Input generated");
123-
observe_prepare_input_time(proof_request.block_number, input_time, true);
124-
memory::print_stats("Input generation peak memory used: ");
125-
input
141+
let provider = RpcBlockDataProvider::new(
142+
&taiko_chain_spec.rpc.clone(),
143+
proof_request.block_number - 1,
144+
)?;
145+
let input = match validate_cache_input(cached_input, &provider).await {
146+
Ok(cache_input) => cache_input,
147+
Err(_) => {
148+
// no valid cache
149+
memory::reset_stats();
150+
let measurement = Measurement::start("Generating input...", false);
151+
let input = raiko.generate_input(provider).await?;
152+
let input_time = measurement.stop_with("=> Input generated");
153+
observe_prepare_input_time(proof_request.block_number, input_time, true);
154+
memory::print_stats("Input generation peak memory used: ");
155+
input
156+
}
126157
};
127158
memory::reset_stats();
128159
let output = raiko.get_output(&input)?;
@@ -206,3 +237,75 @@ pub fn create_docs() -> utoipa::openapi::OpenApi {
206237
pub fn create_router() -> Router<ProverState> {
207238
Router::new().route("/", post(proof_handler))
208239
}
240+
241+
#[cfg(test)]
242+
mod test {
243+
use super::*;
244+
use alloy_primitives::{Address, B256};
245+
use raiko_core::interfaces::ProofType;
246+
use raiko_lib::consts::{Network, SupportedChainSpecs};
247+
248+
async fn create_cache_input(
249+
l1_network: &String,
250+
network: &String,
251+
block_number: u64,
252+
) -> (GuestInput, RpcBlockDataProvider) {
253+
let l1_chain_spec = SupportedChainSpecs::default()
254+
.get_chain_spec(&l1_network)
255+
.unwrap();
256+
let taiko_chain_spec = SupportedChainSpecs::default()
257+
.get_chain_spec(network)
258+
.unwrap();
259+
let proof_request = ProofRequest {
260+
block_number,
261+
network: network.to_string(),
262+
l1_network: l1_network.to_string(),
263+
graffiti: B256::ZERO,
264+
prover: Address::ZERO,
265+
proof_type: ProofType::Native,
266+
prover_args: Default::default(),
267+
};
268+
let raiko = Raiko::new(
269+
l1_chain_spec.clone(),
270+
taiko_chain_spec.clone(),
271+
proof_request.clone(),
272+
);
273+
let provider = RpcBlockDataProvider::new(
274+
&taiko_chain_spec.rpc.clone(),
275+
proof_request.block_number - 1,
276+
)
277+
.expect("provider init ok");
278+
279+
let input = raiko
280+
.generate_input(provider.clone())
281+
.await
282+
.expect("input generation failed");
283+
(input, provider.clone())
284+
}
285+
286+
#[tokio::test]
287+
async fn test_generate_input_from_cache() {
288+
let l1 = &Network::Holesky.to_string();
289+
let l2 = &Network::TaikoA7.to_string();
290+
let block_number: u64 = 7;
291+
let (input, provider) = create_cache_input(l1, l2, block_number).await;
292+
let cache_path = Some("./".into());
293+
assert!(set_cached_input(&cache_path, block_number, l2, &input).is_ok());
294+
let cached_input = get_cached_input(&cache_path, block_number, l2).expect("load cache");
295+
assert!(validate_cache_input(Some(cached_input), &provider)
296+
.await
297+
.is_ok());
298+
299+
let new_l1 = &Network::Ethereum.to_string();
300+
let new_l2 = &Network::TaikoMainnet.to_string();
301+
let (new_input, _) = create_cache_input(new_l1, new_l2, block_number).await;
302+
// save to old l2 cache slot
303+
assert!(set_cached_input(&cache_path, block_number, l2, &new_input).is_ok());
304+
let inv_cached_input = get_cached_input(&cache_path, block_number, l2).expect("load cache");
305+
306+
// should fail with old provider
307+
assert!(validate_cache_input(Some(inv_cached_input), &provider)
308+
.await
309+
.is_err());
310+
}
311+
}

Diff for: lib/src/consts.rs

+3
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ pub enum Network {
243243
Holesky,
244244
/// Taiko A7 tesnet
245245
TaikoA7,
246+
/// Taiko Mainnet
247+
TaikoMainnet,
246248
}
247249

248250
impl ToString for Network {
@@ -251,6 +253,7 @@ impl ToString for Network {
251253
Network::Ethereum => "ethereum".to_string(),
252254
Network::Holesky => "holesky".to_string(),
253255
Network::TaikoA7 => "taiko_a7".to_string(),
256+
Network::TaikoMainnet => "taiko_mainnet".to_string(),
254257
}
255258
}
256259
}

Diff for: provers/sp1/driver/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use raiko_lib::{
99
prover::{to_proof, Proof, Prover, ProverConfig, ProverResult},
1010
};
1111
use serde::{Deserialize, Serialize};
12-
use sha3::{self, Digest};
1312
use sp1_sdk::{ProverClient, SP1Stdin};
1413

1514
const ELF: &[u8] = include_bytes!("../../guest/elf/sp1-guest");

0 commit comments

Comments
 (0)