@@ -9,6 +9,11 @@ use apollo_committer_types::committer_types::{
99 RevertBlockRequest ,
1010 RevertBlockResponse ,
1111} ;
12+ #[ cfg( feature = "os_input" ) ]
13+ use apollo_committer_types:: committer_types:: {
14+ ReadPathsAndCommitBlockRequest ,
15+ ReadPathsAndCommitBlockResponse ,
16+ } ;
1217use apollo_committer_types:: errors:: { CommitterError , CommitterResult } ;
1318use apollo_infra:: component_definitions:: { default_component_start_fn, ComponentStarter } ;
1419use async_trait:: async_trait;
@@ -27,21 +32,34 @@ use starknet_committer::block_committer::measurements_util::{
2732 MeasurementsTrait ,
2833 SingleBlockMeasurements ,
2934} ;
35+ #[ cfg( feature = "os_input" ) ]
36+ use starknet_committer:: db:: forest_trait:: ForestStorageWithWitnesses ;
37+ #[ cfg( feature = "os_input" ) ]
38+ use starknet_committer:: db:: forest_trait:: PatriciaProofsUpdates ;
3039use starknet_committer:: db:: forest_trait:: {
3140 EmptyInitialReadContext ,
3241 ForestMetadataType ,
3342 ForestStorageWithEmptyReadContext ,
43+ ForestWriterWithMetadataAndWitnesses ,
3444} ;
3545use starknet_committer:: db:: index_db:: IndexDb ;
46+ #[ cfg( feature = "os_input" ) ]
47+ use starknet_committer:: db:: serde_db_utils:: accessed_keys_digest;
3648use starknet_committer:: db:: serde_db_utils:: {
3749 deserialize_felt_no_packing,
3850 serialize_felt_no_packing,
3951 DbBlockNumber ,
4052} ;
4153use starknet_committer:: forest:: deleted_nodes:: DeletedNodes ;
4254use starknet_committer:: forest:: filled_forest:: FilledForest ;
55+ #[ cfg( feature = "os_input" ) ]
56+ use starknet_committer:: patricia_merkle_tree:: tree:: get_leaf_indices;
57+ #[ cfg( feature = "os_input" ) ]
58+ use starknet_patricia_storage:: errors:: SerializationError ;
4359use starknet_patricia_storage:: map_storage:: CachedStorage ;
4460use starknet_patricia_storage:: rocksdb_storage:: RocksDbStorage ;
61+ #[ cfg( feature = "os_input" ) ]
62+ use starknet_patricia_storage:: storage_trait:: ImmutableReadOnlyStorage ;
4563use starknet_patricia_storage:: storage_trait:: { DbValue , Storage } ;
4664use tracing:: { debug, error, info, warn} ;
4765
@@ -443,6 +461,167 @@ where
443461 }
444462}
445463
464+ #[ cfg( feature = "os_input" ) ]
465+ impl < S , ForestDB > Committer < S , ForestDB >
466+ where
467+ S : StorageConstructor + ImmutableReadOnlyStorage + ' static ,
468+ ForestDB : ForestStorageWithWitnesses < Storage = S > ,
469+ {
470+ async fn load_witnesses_digest (
471+ & mut self ,
472+ block_number : BlockNumber ,
473+ ) -> CommitterResult < Option < [ u8 ; 32 ] > > {
474+ Ok (
475+ match self
476+ . forest_storage
477+ . read_metadata ( ForestMetadataType :: OsInputWitnessDigest ( DbBlockNumber (
478+ block_number,
479+ ) ) )
480+ . await
481+ . map_err ( |e| self . map_internal_error ( e) ) ?
482+ {
483+ None => None ,
484+ Some ( v) => {
485+ let s = v. 0 . as_slice ( ) ;
486+ let arr: [ u8 ; 32 ] = s. try_into ( ) . map_err ( |_| CommitterError :: Internal {
487+ height : block_number,
488+ message : format ! (
489+ "Invalid OS-input witness digest length {} (expected 32)" ,
490+ s. len( )
491+ ) ,
492+ } ) ?;
493+ Some ( arr)
494+ }
495+ } ,
496+ )
497+ }
498+
499+ /// Commits the next block and returns merged Patricia witness facts for OS input, persisting
500+ /// digest + payload for idempotent replay.
501+ pub async fn read_paths_and_commit_block (
502+ & mut self ,
503+ ReadPathsAndCommitBlockRequest {
504+ commit : CommitBlockRequest { state_diff, state_diff_commitment, height } ,
505+ class_hashes,
506+ contract_addresses,
507+ contract_storage_keys,
508+ } : ReadPathsAndCommitBlockRequest ,
509+ ) -> CommitterResult < ReadPathsAndCommitBlockResponse > {
510+ let digest =
511+ accessed_keys_digest ( & class_hashes, & contract_addresses, & contract_storage_keys) ;
512+ info ! (
513+ "read_paths_and_commit_block: block {height} with {} class hashes, {} contract \
514+ addresses",
515+ class_hashes. len( ) ,
516+ contract_addresses. len( )
517+ ) ;
518+
519+ match self . commit_or_load ( & state_diff, state_diff_commitment, height) . await ? {
520+ CommitBlockHeightPlan :: Historical { global_root } => {
521+ let stored_digest = self . load_witnesses_digest ( height) . await ?;
522+ if stored_digest != Some ( digest) {
523+ return Err ( CommitterError :: AccessedKeysDigestMismatch {
524+ height,
525+ stored : stored_digest,
526+ expected : digest,
527+ } ) ;
528+ }
529+ let proofs = self
530+ . forest_storage
531+ . read_witnesses ( height)
532+ . await
533+ . map_err ( |e| self . map_internal_error ( e) ) ?;
534+ let proofs = proofs. ok_or ( CommitterError :: MissingPatriciaPaths { height } ) ?;
535+ Ok ( ReadPathsAndCommitBlockResponse { global_root, patricia_proofs : proofs } )
536+ }
537+ CommitBlockHeightPlan :: CommitTip { state_diff_commitment } => {
538+ let mut leaves_request =
539+ get_leaf_indices ( & class_hashes, & contract_addresses, & contract_storage_keys) ;
540+
541+ let pre_roots = self
542+ . forest_storage
543+ . read_roots ( ForestDB :: InitialReadContext :: create_empty ( ) )
544+ . await
545+ . map_err ( |e| self . map_internal_error ( e) ) ?;
546+ let ( class_sorted, contract_sorted, storage_sorted) = leaves_request. get_stored ( ) ;
547+
548+ let proof_before = self
549+ . forest_storage
550+ . fetch_patricia_witnesses (
551+ pre_roots. classes_trie_root_hash ,
552+ pre_roots. contracts_trie_root_hash ,
553+ class_sorted,
554+ contract_sorted,
555+ & storage_sorted,
556+ None ,
557+ )
558+ . await
559+ . map_err ( |e| CommitterError :: PatriciaPathsCollectionFailed {
560+ height,
561+ message : format ! ( "pre-commit witness paths: {e:?}" ) ,
562+ } ) ?;
563+
564+ let mut block_measurements = SingleBlockMeasurements :: default ( ) ;
565+ block_measurements. start_measurement ( Action :: EndToEnd ) ;
566+ let CommitStateDiffOutput { filled_forest, global_root, deleted_nodes } =
567+ self . commit_state_diff ( state_diff. clone ( ) , & mut block_measurements) . await ?;
568+ let post_roots = filled_forest. state_roots ( ) ;
569+
570+ let forest_updates = ForestDB :: serialize_forest ( & filled_forest)
571+ . map_err ( |e| self . map_internal_error ( e) ) ?;
572+
573+ let proof_after = self
574+ . forest_storage
575+ . fetch_patricia_witnesses (
576+ post_roots. classes_trie_root_hash ,
577+ post_roots. contracts_trie_root_hash ,
578+ class_sorted,
579+ contract_sorted,
580+ & storage_sorted,
581+ Some ( forest_updates) ,
582+ )
583+ . await
584+ . map_err ( |e| CommitterError :: PatriciaPathsCollectionFailed {
585+ height,
586+ message : format ! ( "post-commit witness paths: {e:?}" ) ,
587+ } ) ?;
588+
589+ let mut merged_proofs = proof_before;
590+ merged_proofs. extend ( proof_after) ;
591+ let patricia_proofs = merged_proofs. clone ( ) ;
592+
593+ let ( metadata, next_offset) =
594+ commit_tip_metadata_bundle ( height, global_root, state_diff_commitment) ;
595+ info ! (
596+ "For block number {height}, writing filled forest and witnesses to storage \
597+ with metadata: {metadata:?}, delete {} nodes",
598+ deleted_nodes. len( )
599+ ) ;
600+ block_measurements. start_measurement ( Action :: Write ) ;
601+ let n_write_entries = self
602+ . forest_storage
603+ . write_with_metadata_and_witnesses (
604+ & filled_forest,
605+ metadata,
606+ deleted_nodes,
607+ PatriciaProofsUpdates :: Set {
608+ height,
609+ keys_digest : digest,
610+ witnesses : merged_proofs,
611+ } ,
612+ )
613+ . await
614+ . map_err ( |e : SerializationError | self . map_internal_error ( e) ) ?;
615+ block_measurements. attempt_to_stop_measurement ( Action :: Write , n_write_entries) . ok ( ) ;
616+ block_measurements. attempt_to_stop_measurement ( Action :: EndToEnd , 0 ) . ok ( ) ;
617+ update_metrics ( height, & block_measurements. block_measurement ) ;
618+ self . update_offset ( next_offset) ;
619+ Ok ( ReadPathsAndCommitBlockResponse { global_root, patricia_proofs } )
620+ }
621+ }
622+ }
623+ }
624+
446625#[ async_trait]
447626impl ComponentStarter for ApolloCommitter {
448627 async fn start ( & mut self ) {
0 commit comments