Skip to content

Commit 5ac5933

Browse files
committed
starknet_committer: underlying logic of the new read witnesses and commit endpoint
1 parent 3cbcedb commit 5ac5933

4 files changed

Lines changed: 203 additions & 5 deletions

File tree

crates/apollo_committer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ description = "State root commitment computation component for the Starknet sequ
88

99
[features]
1010
testing = []
11+
os_input = ["apollo_committer_types/os_input", "starknet_committer/os_input"]
1112

1213
[dependencies]
1314
apollo_committer_config.workspace = true

crates/apollo_committer/src/committer.rs

Lines changed: 195 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ use std::error::Error;
33
use std::path::PathBuf;
44

55
use 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+
};
612
use 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;
3040
use starknet_committer::db::forest_trait::{
3141
EmptyInitialReadContext,
3242
ForestMetadataType,
3343
ForestStorageWithEmptyReadContext,
3444
};
3545
use starknet_committer::db::index_db::IndexDb;
46+
#[cfg(feature = "os_input")]
47+
use starknet_committer::db::serde_db_utils::accessed_keys_digest;
3648
use starknet_committer::db::serde_db_utils::{
3749
deserialize_felt_no_packing,
3850
serialize_felt_no_packing,
3951
DbBlockNumber,
4052
};
4153
use starknet_committer::forest::deleted_nodes::DeletedNodes;
4254
use 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;
4359
use starknet_patricia_storage::map_storage::CachedStorage;
4460
use starknet_patricia_storage::rocksdb_storage::RocksDbStorage;
61+
#[cfg(feature = "os_input")]
62+
use starknet_patricia_storage::storage_trait::ImmutableReadOnlyStorage;
4563
use starknet_patricia_storage::storage_trait::{DbValue, Storage};
4664
use tracing::{debug, error, info, warn};
4765

@@ -447,9 +465,184 @@ where
447465
}
448466

449467
fn map_internal_error<E: Error>(&self, err: E) -> CommitterError {
468+
self.map_internal_error_at_height(self.offset, err)
469+
}
470+
471+
fn map_internal_error_at_height<E: Error>(
472+
&self,
473+
height: BlockNumber,
474+
err: E,
475+
) -> CommitterError {
450476
let error_message = format!("{err:?}: {err}");
451-
error!("Error committing block number {0}. {error_message}.", self.offset);
452-
CommitterError::Internal { height: self.offset, message: error_message }
477+
error!("Error committing block number {height}. {error_message}.");
478+
CommitterError::Internal { height, message: error_message }
479+
}
480+
}
481+
482+
#[cfg(feature = "os_input")]
483+
impl<S, ForestDB> Committer<S, ForestDB>
484+
where
485+
S: StorageConstructor + ImmutableReadOnlyStorage + 'static,
486+
ForestDB: ForestStorageWithWitnesses<Storage = S>,
487+
{
488+
/// Commits the next block and returns merged Patricia witness facts for OS input, persisting
489+
/// digest + payload for idempotent replay.
490+
pub async fn read_paths_and_commit_block(
491+
&mut self,
492+
ReadPathsAndCommitBlockRequest {
493+
commit: CommitBlockRequest { state_diff, state_diff_commitment, height },
494+
accessed_keys: AccessedKeys { class_hashes, contract_addresses, contract_storage_keys },
495+
}: ReadPathsAndCommitBlockRequest,
496+
) -> CommitterResult<ReadPathsAndCommitBlockResponse> {
497+
let mut leaves_request = LeavesRequest::from_accessed_leaves(
498+
&class_hashes,
499+
&contract_addresses,
500+
&contract_storage_keys,
501+
);
502+
let sorted_leaves: SortedLeavesRequest<'_> = (&mut leaves_request).into();
503+
let digest = accessed_keys_digest(&sorted_leaves);
504+
info!(
505+
"read_paths_and_commit_block: block {height} with {} class hashes, {} contract \
506+
addresses",
507+
class_hashes.len(),
508+
contract_addresses.len()
509+
);
510+
511+
match self.commit_or_load(&state_diff, state_diff_commitment, height).await? {
512+
CommitBlockHeightPlan::Historical { global_root } => {
513+
let stored_digest = self.load_witnesses_digest(height).await?;
514+
if stored_digest != Some(digest) {
515+
return Err(CommitterError::AccessedKeysDigestMismatch {
516+
height,
517+
stored: stored_digest,
518+
expected: digest,
519+
});
520+
}
521+
let proofs = self
522+
.forest_storage
523+
.read_witnesses(height)
524+
.await
525+
.map_err(|error| self.map_internal_error_at_height(height, error))?;
526+
let proofs = proofs.ok_or(CommitterError::MissingPatriciaPaths { height })?;
527+
Ok(ReadPathsAndCommitBlockResponse { global_root, patricia_proofs: proofs })
528+
}
529+
// Flow overview:
530+
// 1. Fetch patricia paths for the accessed keys
531+
// 2. Compute the updates from the state diff (commit) but avoid updating the underlying
532+
// DB in order to guarantee atomicity.
533+
// 3. Fetch patricia paths for the post-commit tries, via running step 1 against a two
534+
// layer storage composed from the underlying storage and the modifications from 2.
535+
// 4. Merge the two sets of patricia paths and write the result to the storage.
536+
// 5. Update the commitment offset and return the global root and the patricia proofs.
537+
CommitBlockHeightPlan::CommitTip { state_diff_commitment } => {
538+
let pre_roots = self
539+
.forest_storage
540+
.read_roots(ForestDB::InitialReadContext::create_empty())
541+
.await
542+
.map_err(|e| self.map_internal_error(e))?;
543+
let mut patricia_proofs = self
544+
.forest_storage
545+
.fetch_patricia_witnesses(
546+
pre_roots.classes_trie_root_hash,
547+
pre_roots.contracts_trie_root_hash,
548+
sorted_leaves.class_sorted,
549+
sorted_leaves.contract_sorted,
550+
&sorted_leaves.storage_sorted,
551+
None,
552+
)
553+
.await
554+
.map_err(|e| CommitterError::PatriciaPathsCollectionFailed {
555+
height,
556+
message: format!("pre-commit witness paths: {e:?}"),
557+
})?;
558+
559+
let mut block_measurements = SingleBlockMeasurements::default();
560+
block_measurements.start_measurement(Action::EndToEnd);
561+
let CommitStateDiffOutput { filled_forest, global_root, deleted_nodes } =
562+
self.commit_state_diff(state_diff, &mut block_measurements).await?;
563+
let post_roots = filled_forest.state_roots();
564+
565+
let forest_updates = ForestDB::serialize_forest(&filled_forest)
566+
.map_err(|e| self.map_internal_error(e))?;
567+
568+
let proof_after = self
569+
.forest_storage
570+
.fetch_patricia_witnesses(
571+
post_roots.classes_trie_root_hash,
572+
post_roots.contracts_trie_root_hash,
573+
sorted_leaves.class_sorted,
574+
sorted_leaves.contract_sorted,
575+
&sorted_leaves.storage_sorted,
576+
Some(forest_updates),
577+
)
578+
.await
579+
.map_err(|e| CommitterError::PatriciaPathsCollectionFailed {
580+
height,
581+
message: format!("post-commit witness paths: {e:?}"),
582+
})?;
583+
584+
patricia_proofs.extend(proof_after);
585+
586+
let (metadata, next_offset) =
587+
commit_tip_metadata_bundle(height, global_root, state_diff_commitment);
588+
let witness_node_count = patricia_proofs.classes_trie_proof.len()
589+
+ patricia_proofs.contracts_trie_proof.nodes.len()
590+
+ patricia_proofs.contracts_trie_proof.leaves.len()
591+
+ patricia_proofs
592+
.contracts_trie_storage_proofs
593+
.values()
594+
.map(|proof| proof.len())
595+
.sum::<usize>();
596+
info!(
597+
"For block number {height}, writing filled forest and {witness_node_count} \
598+
witness nodes to storage with metadata: {metadata:?}, delete {} nodes",
599+
deleted_nodes.len()
600+
);
601+
block_measurements.start_measurement(Action::Write);
602+
let n_write_entries = self
603+
.forest_storage
604+
.write_with_metadata_and_witnesses(
605+
&filled_forest,
606+
metadata,
607+
deleted_nodes,
608+
PatriciaProofsUpdates::Set {
609+
height,
610+
keys_digest: digest,
611+
witnesses: patricia_proofs.clone(),
612+
},
613+
)
614+
.await
615+
.map_err(|e: SerializationError| self.map_internal_error(e))?;
616+
block_measurements.attempt_to_stop_measurement(Action::Write, n_write_entries).ok();
617+
block_measurements.attempt_to_stop_measurement(Action::EndToEnd, 0).ok();
618+
update_metrics(height, &block_measurements.block_measurement);
619+
self.update_offset(next_offset);
620+
Ok(ReadPathsAndCommitBlockResponse { global_root, patricia_proofs })
621+
}
622+
}
623+
}
624+
625+
async fn load_witnesses_digest(
626+
&mut self,
627+
block_number: BlockNumber,
628+
) -> CommitterResult<Option<[u8; 32]>> {
629+
let digest_raw = self
630+
.forest_storage
631+
.read_metadata(ForestMetadataType::AccessedKeysDigest(DbBlockNumber(block_number)))
632+
.await
633+
.map_err(|error| self.map_internal_error_at_height(block_number, error))?;
634+
635+
digest_raw
636+
.map(|digest_raw| {
637+
digest_raw.0.as_slice().try_into().map_err(|_| CommitterError::Internal {
638+
height: block_number,
639+
message: format!(
640+
"Invalid OS witnesses digest length {} (expected 32)",
641+
digest_raw.0.len()
642+
),
643+
})
644+
})
645+
.transpose()
453646
}
454647
}
455648

crates/apollo_committer/src/metrics.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use apollo_committer_types::communication::COMMITTER_REQUEST_LABELS;
22
use apollo_infra::metrics::{
3-
InfraMetrics, LocalClientMetrics, LocalServerMetrics, RemoteClientMetrics, RemoteServerMetrics,
3+
InfraMetrics,
4+
LocalClientMetrics,
5+
LocalServerMetrics,
6+
RemoteClientMetrics,
7+
RemoteServerMetrics,
48
};
59
use apollo_metrics::{define_infra_metrics, define_metrics};
610
use starknet_api::block::BlockNumber;

crates/starknet_committer/src/patricia_merkle_tree/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ pub type ClassesTrie = FilledTreeImpl<CompiledClassHash>;
3636
pub type ContractsTrie = FilledTreeImpl<ContractState>;
3737
pub type StorageTrieMap = HashMap<ContractAddress, StorageTrie>;
3838

39-
#[derive(Debug, Clone, PartialEq)]
39+
#[derive(Clone, Debug, PartialEq)]
4040
pub struct ContractsTrieProof {
4141
pub nodes: PreimageMap,
4242
pub leaves: HashMap<ContractAddress, ContractState>,
4343
}
4444

45-
#[derive(Debug, Clone, PartialEq)]
45+
#[derive(Clone, Debug, PartialEq)]
4646
pub struct StarknetForestProofs {
4747
pub classes_trie_proof: PreimageMap,
4848
pub contracts_trie_proof: ContractsTrieProof,

0 commit comments

Comments
 (0)