@@ -39,17 +39,24 @@ where
3939 votes : VoteTally ,
4040 // Gateway caller for IPC gateway interactions
4141 gateway_caller : GatewayCaller < DB > ,
42+ // Proof cache for F3-based parent finality (optional for gradual rollout)
43+ proof_cache : Option < std:: sync:: Arc < fendermint_vm_topdown_proof_service:: ProofCache > > ,
4244}
4345
4446impl < DB > TopDownManager < DB >
4547where
4648 DB : Blockstore + Clone + ' static + Send + Sync ,
4749{
48- pub fn new ( provider : TopDownFinalityProvider , votes : VoteTally ) -> Self {
50+ pub fn new (
51+ provider : TopDownFinalityProvider ,
52+ votes : VoteTally ,
53+ proof_cache : Option < std:: sync:: Arc < fendermint_vm_topdown_proof_service:: ProofCache > > ,
54+ ) -> Self {
4955 Self {
5056 provider,
5157 votes,
5258 gateway_caller : GatewayCaller :: default ( ) ,
59+ proof_cache,
5360 }
5461 }
5562
@@ -117,6 +124,76 @@ where
117124 } ) ) )
118125 }
119126
127+ /// Query proof cache for next uncommitted proof and create a chain message with proof bundle.
128+ ///
129+ /// This is the v2 proof-based approach that replaces voting with cryptographic verification.
130+ ///
131+ /// Returns `None` if:
132+ /// - Proof cache is not configured
133+ /// - No proof available for next height
134+ /// - Cache is temporarily empty (graceful degradation)
135+ pub async fn chain_message_from_proof_cache ( & self ) -> Option < ChainMessage > {
136+ let cache = self . proof_cache . as_ref ( ) ?;
137+
138+ // Get next uncommitted proof (instance after last_committed)
139+ let entry = cache. get_next_uncommitted ( ) ?;
140+
141+ tracing:: debug!(
142+ instance_id = entry. instance_id,
143+ epochs = ?entry. finalized_epochs,
144+ "found proof in cache for proposal"
145+ ) ;
146+
147+ // Extract highest epoch as the finality height
148+ let height = entry. highest_epoch ( ) ? as ChainEpoch ;
149+
150+ // Extract block hash from the proof bundle
151+ // The proof bundle contains the parent tipset information
152+ // For now, we use an empty block hash as a placeholder
153+ // TODO: Extract actual block hash from certificate or proof bundle
154+ let block_hash = vec ! [ ] ;
155+
156+ Some ( ChainMessage :: Ipc ( IpcMessage :: ParentFinalityWithProof (
157+ fendermint_vm_message:: ipc:: ParentFinalityProofBundle {
158+ finality : ParentFinality { height, block_hash } ,
159+ certificate : entry. certificate ,
160+ proof_bundle : entry. proof_bundle ,
161+ } ,
162+ ) ) )
163+ }
164+
165+ /// Deterministically verify a proof bundle against F3 certificate.
166+ ///
167+ /// This performs cryptographic verification of:
168+ /// 1. Storage proofs (contract state at parent height - completeness via topDownNonce)
169+ /// 2. Event proofs (emitted events at parent height)
170+ /// 3. Certificate chain continuity (validates against F3CertManager state)
171+ ///
172+ /// All correct validators will reach the same decision (deterministic).
173+ pub async fn verify_proof_bundle (
174+ & self ,
175+ bundle : & fendermint_vm_message:: ipc:: ParentFinalityProofBundle ,
176+ ) -> anyhow:: Result < ( ) > {
177+ use fendermint_vm_topdown_proof_service:: verify_proof_bundle;
178+
179+ // Step 1: Verify cryptographic proofs (storage + events)
180+ verify_proof_bundle ( & bundle. proof_bundle , & bundle. certificate )
181+ . context ( "proof bundle cryptographic verification failed" ) ?;
182+
183+ // Step 2: TODO - Verify certificate chain continuity
184+ // Query F3CertManager for last committed instance
185+ // Ensure bundle.certificate.instance_id == last_committed + 1
186+ // This requires querying the F3CertManager actor state
187+
188+ tracing:: debug!(
189+ instance_id = bundle. certificate. instance_id,
190+ height = bundle. finality. height,
191+ "proof bundle verified successfully"
192+ ) ;
193+
194+ Ok ( ( ) )
195+ }
196+
120197 pub async fn update_voting_power_table ( & self , power_updates : & PowerUpdates ) {
121198 let power_updates_mapped: Vec < _ > = power_updates
122199 . 0
@@ -127,6 +204,127 @@ where
127204 atomically ( || self . votes . update_power_table ( power_updates_mapped. clone ( ) ) ) . await
128205 }
129206
207+ /// Execute proof-based topdown finality (v2).
208+ ///
209+ /// Steps:
210+ /// 1. Commit parent finality to gateway
211+ /// 2. Update F3CertManager actor with new certificate (TODO)
212+ /// 3. Extract and execute topdown effects (messages + validator changes)
213+ /// 4. Mark instance as committed in cache
214+ /// 5. Update local state (provider + votes)
215+ pub async fn execute_proof_based_topdown (
216+ & self ,
217+ state : & mut FvmExecState < DB > ,
218+ bundle : fendermint_vm_message:: ipc:: ParentFinalityProofBundle ,
219+ ) -> anyhow:: Result < AppliedMessage > {
220+ if !self . provider . is_enabled ( ) {
221+ bail ! ( "cannot execute IPC top-down message: parent provider disabled" ) ;
222+ }
223+
224+ // Convert to IPCParentFinality
225+ let finality =
226+ IPCParentFinality :: new ( bundle. finality . height , bundle. finality . block_hash . clone ( ) ) ;
227+
228+ tracing:: debug!(
229+ finality = finality. to_string( ) ,
230+ instance = bundle. certificate. instance_id,
231+ "executing proof-based topdown finality"
232+ ) ;
233+
234+ // Step 1: Commit parent finality (same as v1)
235+ let ( prev_height, _prev_finality) = self
236+ . commit_finality ( state, finality. clone ( ) )
237+ . await
238+ . context ( "failed to commit finality" ) ?;
239+
240+ tracing:: debug!(
241+ previous_height = prev_height,
242+ current_height = finality. height,
243+ "committed parent finality"
244+ ) ;
245+
246+ // Step 2: TODO - Update F3CertManager actor
247+ // self.update_f3_cert_manager(state, &bundle.certificate)?;
248+
249+ // Step 3: Execute topdown effects
250+ // For now, we use the existing v1 path to fetch messages/changes from the provider
251+ // TODO: Extract from proof bundle instead
252+ let ( execution_fr, execution_to) = ( prev_height + 1 , finality. height ) ;
253+
254+ let validator_changes = self
255+ . provider
256+ . validator_changes_from ( execution_fr, execution_to)
257+ . await
258+ . context ( "failed to fetch validator changes" ) ?;
259+
260+ tracing:: debug!(
261+ from = execution_fr,
262+ to = execution_to,
263+ change_count = validator_changes. len( ) ,
264+ "fetched validator changes"
265+ ) ;
266+
267+ self . gateway_caller
268+ . store_validator_changes ( state, validator_changes)
269+ . context ( "failed to store validator changes" ) ?;
270+
271+ let msgs = self
272+ . provider
273+ . top_down_msgs_from ( execution_fr, execution_to)
274+ . await
275+ . context ( "failed to fetch top down messages" ) ?;
276+
277+ tracing:: debug!(
278+ message_count = msgs. len( ) ,
279+ start = execution_fr,
280+ end = execution_to,
281+ "fetched topdown messages"
282+ ) ;
283+
284+ let ret = self
285+ . execute_topdown_msgs ( state, msgs)
286+ . await
287+ . context ( "failed to execute top down messages" ) ?;
288+
289+ tracing:: debug!( "applied topdown messages" ) ;
290+
291+ // Step 4: Mark instance as committed in cache
292+ if let Some ( cache) = & self . proof_cache {
293+ cache. mark_committed ( bundle. certificate . instance_id ) ;
294+ tracing:: debug!(
295+ instance = bundle. certificate. instance_id,
296+ "marked instance as committed in cache"
297+ ) ;
298+ }
299+
300+ // Step 5: Update state (same as v1)
301+ let local_block_height = state. block_height ( ) as u64 ;
302+ let proposer = state
303+ . block_producer ( )
304+ . map ( |id| hex:: encode ( id. serialize_compressed ( ) ) ) ;
305+ let proposer_ref = proposer. as_deref ( ) ;
306+
307+ atomically ( || {
308+ self . provider . set_new_finality ( finality. clone ( ) ) ?;
309+ self . votes . set_finalized (
310+ finality. height ,
311+ finality. block_hash . clone ( ) ,
312+ proposer_ref,
313+ Some ( local_block_height) ,
314+ ) ?;
315+ Ok ( ( ) )
316+ } )
317+ . await ;
318+
319+ tracing:: info!(
320+ instance = bundle. certificate. instance_id,
321+ height = finality. height,
322+ "proof-based topdown finality executed successfully"
323+ ) ;
324+
325+ Ok ( ret)
326+ }
327+
130328 // TODO Karel - separate this huge function and clean up
131329 pub async fn execute_topdown_msg (
132330 & self ,
0 commit comments