@@ -3,11 +3,12 @@ use std::{fs::File, path::PathBuf};
3
3
use axum:: { debug_handler, extract:: State , routing:: post, Json , Router } ;
4
4
use raiko_core:: {
5
5
interfaces:: { ProofRequest , RaikoError } ,
6
- provider:: rpc:: RpcBlockDataProvider ,
6
+ provider:: { rpc:: RpcBlockDataProvider , BlockDataProvider } ,
7
7
Raiko ,
8
8
} ;
9
9
use raiko_lib:: {
10
10
input:: { get_input_path, GuestInput } ,
11
+ utils:: { to_header, HeaderHasher } ,
11
12
Measurement ,
12
13
} ;
13
14
use serde_json:: Value ;
@@ -51,16 +52,45 @@ fn set_cached_input(
51
52
} ;
52
53
53
54
let path = get_input_path ( dir, block_number, network) ;
54
-
55
- if path. exists ( ) {
56
- return Ok ( ( ) ) ;
57
- }
55
+ info ! ( "caching input for {path:?}" ) ;
58
56
59
57
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
+ }
60
60
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 ( ) ) ) ?;
62
73
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
+ }
64
94
}
65
95
66
96
async fn handle_proof (
@@ -108,21 +138,22 @@ async fn handle_proof(
108
138
taiko_chain_spec. clone ( ) ,
109
139
proof_request. clone ( ) ,
110
140
) ;
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
+ }
126
157
} ;
127
158
memory:: reset_stats ( ) ;
128
159
let output = raiko. get_output ( & input) ?;
@@ -206,3 +237,75 @@ pub fn create_docs() -> utoipa::openapi::OpenApi {
206
237
pub fn create_router ( ) -> Router < ProverState > {
207
238
Router :: new ( ) . route ( "/" , post ( proof_handler) )
208
239
}
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
+ }
0 commit comments