@@ -3,6 +3,12 @@ use std::error::Error;
33use std:: path:: PathBuf ;
44
55use apollo_committer_config:: config:: { ApolloStorage , CommitterConfig } ;
6+ #[ cfg( feature = "os_input" ) ]
7+ use apollo_committer_types:: committer_types:: {
8+ AccessedKeys ,
9+ ReadPathsAndCommitBlockRequest ,
10+ ReadPathsAndCommitBlockResponse ,
11+ } ;
612use apollo_committer_types:: committer_types:: {
713 CommitBlockRequest ,
814 CommitBlockResponse ,
@@ -27,21 +33,33 @@ use starknet_committer::block_committer::measurements_util::{
2733 MeasurementsTrait ,
2834 SingleBlockMeasurements ,
2935} ;
36+ #[ cfg( feature = "os_input" ) ]
37+ use starknet_committer:: db:: forest_trait:: ForestStorageWithWitnesses ;
38+ #[ cfg( feature = "os_input" ) ]
39+ use starknet_committer:: db:: forest_trait:: PatriciaProofsUpdates ;
3040use starknet_committer:: db:: forest_trait:: {
3141 EmptyInitialReadContext ,
3242 ForestMetadataType ,
3343 ForestStorageWithEmptyReadContext ,
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:: { LeavesRequest , SortedLeavesRequest } ;
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
@@ -453,6 +471,162 @@ where
453471 }
454472}
455473
474+ #[ cfg( feature = "os_input" ) ]
475+ impl < S , ForestDB > Committer < S , ForestDB >
476+ where
477+ S : StorageConstructor + ImmutableReadOnlyStorage + ' static ,
478+ ForestDB : ForestStorageWithWitnesses < Storage = S > ,
479+ {
480+ async fn load_witnesses_digest (
481+ & mut self ,
482+ block_number : BlockNumber ,
483+ ) -> CommitterResult < Option < [ u8 ; 32 ] > > {
484+ self . forest_storage
485+ . read_metadata ( ForestMetadataType :: AccessedKeysDigest ( DbBlockNumber ( block_number) ) )
486+ . await
487+ . map_err ( |error| self . map_internal_error ( error) ) ?
488+ . map ( |digest_raw| {
489+ digest_raw. 0 . as_slice ( ) . try_into ( ) . map_err ( |_| CommitterError :: Internal {
490+ height : block_number,
491+ message : format ! (
492+ "Invalid OS witnesses digest length {} (expected 32)" ,
493+ digest_raw. 0 . len( )
494+ ) ,
495+ } )
496+ } )
497+ . transpose ( )
498+ }
499+
500+ /// Commits the next block and returns merged Patricia witness facts for OS input, persisting
501+ /// digest + payload for idempotent replay.
502+ pub async fn read_paths_and_commit_block (
503+ & mut self ,
504+ ReadPathsAndCommitBlockRequest {
505+ commit : CommitBlockRequest { state_diff, state_diff_commitment, height } ,
506+ accessed_keys : AccessedKeys { class_hashes, contract_addresses, contract_storage_keys } ,
507+ } : ReadPathsAndCommitBlockRequest ,
508+ ) -> CommitterResult < ReadPathsAndCommitBlockResponse > {
509+ let mut leaves_request = LeavesRequest :: from_accessed_leaves (
510+ & class_hashes,
511+ & contract_addresses,
512+ & contract_storage_keys,
513+ ) ;
514+ let sorted_leaves: SortedLeavesRequest < ' _ > = ( & mut leaves_request) . into ( ) ;
515+ let digest = accessed_keys_digest ( & sorted_leaves) ;
516+ info ! (
517+ "read_paths_and_commit_block: block {height} with {} class hashes, {} contract \
518+ addresses",
519+ class_hashes. len( ) ,
520+ contract_addresses. len( )
521+ ) ;
522+
523+ match self . commit_or_load ( & state_diff, state_diff_commitment, height) . await ? {
524+ CommitBlockHeightPlan :: Historical { global_root } => {
525+ let stored_digest = self . load_witnesses_digest ( height) . await ?;
526+ if stored_digest != Some ( digest) {
527+ return Err ( CommitterError :: AccessedKeysDigestMismatch {
528+ height,
529+ stored : stored_digest,
530+ expected : digest,
531+ } ) ;
532+ }
533+ let proofs = self
534+ . forest_storage
535+ . read_witnesses ( height)
536+ . await
537+ . map_err ( |e| self . map_internal_error ( e) ) ?;
538+ let proofs = proofs. ok_or ( CommitterError :: MissingPatriciaPaths { height } ) ?;
539+ Ok ( ReadPathsAndCommitBlockResponse { global_root, patricia_proofs : proofs } )
540+ }
541+ // Flow overview:
542+ // 1. Fetch patricia paths for the accessed keys
543+ // 2. Compute the updates from the state diff (commit) but avoid updating the underlying
544+ // DB in order to guarantee atomicity.
545+ // 3. Fetch patricia paths for the post-commit tries, via running step 1 against a two
546+ // layer storage composed from the underlying storage and the modifications from 2.
547+ // 4. Merge the two sets of patricia paths and write the result to the storage.
548+ // 5. Update the commitment offset and return the global root and the patricia proofs.
549+ CommitBlockHeightPlan :: CommitTip { state_diff_commitment } => {
550+ let pre_roots = self
551+ . forest_storage
552+ . read_roots ( ForestDB :: InitialReadContext :: create_empty ( ) )
553+ . await
554+ . map_err ( |e| self . map_internal_error ( e) ) ?;
555+ let mut patricia_proofs = self
556+ . forest_storage
557+ . fetch_patricia_witnesses (
558+ pre_roots. classes_trie_root_hash ,
559+ pre_roots. contracts_trie_root_hash ,
560+ sorted_leaves. class_sorted ,
561+ sorted_leaves. contract_sorted ,
562+ & sorted_leaves. storage_sorted ,
563+ None ,
564+ )
565+ . await
566+ . map_err ( |e| CommitterError :: PatriciaPathsCollectionFailed {
567+ height,
568+ message : format ! ( "pre-commit witness paths: {e:?}" ) ,
569+ } ) ?;
570+
571+ let mut block_measurements = SingleBlockMeasurements :: default ( ) ;
572+ block_measurements. start_measurement ( Action :: EndToEnd ) ;
573+ let CommitStateDiffOutput { filled_forest, global_root, deleted_nodes } =
574+ self . commit_state_diff ( state_diff, & mut block_measurements) . await ?;
575+ let post_roots = filled_forest. state_roots ( ) ;
576+
577+ let forest_updates = ForestDB :: serialize_forest ( & filled_forest)
578+ . map_err ( |e| self . map_internal_error ( e) ) ?;
579+
580+ let proof_after = self
581+ . forest_storage
582+ . fetch_patricia_witnesses (
583+ post_roots. classes_trie_root_hash ,
584+ post_roots. contracts_trie_root_hash ,
585+ sorted_leaves. class_sorted ,
586+ sorted_leaves. contract_sorted ,
587+ & sorted_leaves. storage_sorted ,
588+ Some ( forest_updates) ,
589+ )
590+ . await
591+ . map_err ( |e| CommitterError :: PatriciaPathsCollectionFailed {
592+ height,
593+ message : format ! ( "post-commit witness paths: {e:?}" ) ,
594+ } ) ?;
595+
596+ patricia_proofs. extend ( proof_after) ;
597+
598+ let ( metadata, next_offset) =
599+ commit_tip_metadata_bundle ( height, global_root, state_diff_commitment) ;
600+ info ! (
601+ "For block number {height}, writing filled forest and witnesses to storage \
602+ with metadata: {metadata:?}, delete {} nodes",
603+ deleted_nodes. len( )
604+ ) ;
605+ block_measurements. start_measurement ( Action :: Write ) ;
606+ let n_write_entries = self
607+ . forest_storage
608+ . write_with_metadata_and_witnesses (
609+ & filled_forest,
610+ metadata,
611+ deleted_nodes,
612+ PatriciaProofsUpdates :: Set {
613+ height,
614+ keys_digest : digest,
615+ witnesses : patricia_proofs. clone ( ) ,
616+ } ,
617+ )
618+ . await
619+ . map_err ( |e : SerializationError | self . map_internal_error ( e) ) ?;
620+ block_measurements. attempt_to_stop_measurement ( Action :: Write , n_write_entries) . ok ( ) ;
621+ block_measurements. attempt_to_stop_measurement ( Action :: EndToEnd , 0 ) . ok ( ) ;
622+ update_metrics ( height, & block_measurements. block_measurement ) ;
623+ self . update_offset ( next_offset) ;
624+ Ok ( ReadPathsAndCommitBlockResponse { global_root, patricia_proofs } )
625+ }
626+ }
627+ }
628+ }
629+
456630#[ async_trait]
457631impl ComponentStarter for ApolloCommitter {
458632 async fn start ( & mut self ) {
0 commit comments