Skip to content

Commit f1df4bd

Browse files
apollo_storage: add state_commitment_infos table
1 parent c64de43 commit f1df4bd

7 files changed

Lines changed: 218 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_storage/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license-file.workspace = true
77
description = "A storage implementation for a Starknet node."
88

99
[features]
10-
os_input = ["dep:blockifier"]
10+
os_input = ["dep:blockifier", "dep:starknet_committer"]
1111
storage_cli = ["clap", "reqwest"]
1212
testing = ["reqwest", "starknet_api/testing", "tempfile", "tower"]
1313

@@ -40,6 +40,7 @@ serde = { workspace = true, features = ["derive"] }
4040
serde_json = { workspace = true, features = ["arbitrary_precision"] }
4141
starknet-types-core = { workspace = true, features = ["papyrus-serialization"] }
4242
starknet_api.workspace = true
43+
starknet_committer = { workspace = true, optional = true }
4344
tempfile = { workspace = true, optional = true }
4445
thiserror.workspace = true
4546
tokio = { workspace = true, features = ["rt-multi-thread"] }
@@ -69,6 +70,7 @@ rstest.workspace = true
6970
schemars.workspace = true
7071
simple_logger.workspace = true
7172
starknet_api = { workspace = true, features = ["testing"] }
73+
starknet_committer = { workspace = true, features = ["testing"] }
7274
tempfile = { workspace = true }
7375
test-case.workspace = true
7476
test-log.workspace = true

crates/apollo_storage/src/db/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use crate::db::table_types::TableType;
4646
#[cfg(not(feature = "os_input"))]
4747
const MAX_DBS: u64 = 25;
4848
#[cfg(feature = "os_input")]
49-
const MAX_DBS: u64 = 26;
49+
const MAX_DBS: u64 = 27;
5050

5151
// Note that NO_TLS mode is used by default.
5252
type EnvironmentKind = WriteMap;

crates/apollo_storage/src/lib.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ pub mod global_root_marker;
9191
#[allow(missing_docs)]
9292
pub mod metrics;
9393
pub mod partial_block_hash;
94+
#[cfg(feature = "os_input")]
95+
pub mod state_commitment_infos;
9496
pub mod storage_metrics;
9597
// TODO(yair): Make the compression_utils module pub(crate) or extract it from the crate.
9698
#[doc(hidden)]
@@ -184,6 +186,8 @@ use crate::header::StorageBlockHeader;
184186
use crate::metrics::{register_metrics, STORAGE_COMMIT_LATENCY};
185187
use crate::mmap_file::MMapFileStats;
186188
use crate::state::data::IndexedDeprecatedContractClass;
189+
#[cfg(feature = "os_input")]
190+
use crate::state_commitment_infos::StateCommitmentInfos;
187191
use crate::storage_reader_server::{
188192
create_storage_reader_server,
189193
ServerConfig,
@@ -278,6 +282,8 @@ fn open_storage_internal(
278282
.create_simple_table("stateless_compiled_class_hash_v2")?,
279283
#[cfg(feature = "os_input")]
280284
accessed_keys: db_writer.create_simple_table("accessed_keys")?,
285+
#[cfg(feature = "os_input")]
286+
state_commitment_infos: db_writer.create_simple_table("state_commitment_infos")?,
281287
});
282288
let (file_writers, file_readers) = open_storage_files(
283289
&storage_config.db_config,
@@ -712,7 +718,9 @@ struct_field_names! {
712718
stateless_compiled_class_hash_v2: TableIdentifier<ClassHash, NoVersionValueWrapper<CompiledClassHash>, SimpleTable>,
713719

714720
#[cfg(feature = "os_input")]
715-
accessed_keys: TableIdentifier<BlockNumber, VersionZeroWrapper<LocationInFile>, SimpleTable>
721+
accessed_keys: TableIdentifier<BlockNumber, VersionZeroWrapper<LocationInFile>, SimpleTable>,
722+
#[cfg(feature = "os_input")]
723+
state_commitment_infos: TableIdentifier<BlockNumber, VersionZeroWrapper<LocationInFile>, SimpleTable>
716724
}
717725
}
718726

@@ -879,6 +887,8 @@ struct FileHandlers<Mode: TransactionKind> {
879887
transaction: FileHandler<VersionZeroWrapper<Transaction>, Mode>,
880888
#[cfg(feature = "os_input")]
881889
accessed_keys: FileHandler<VersionZeroWrapper<AccessedKeys>, Mode>,
890+
#[cfg(feature = "os_input")]
891+
state_commitment_infos: FileHandler<VersionZeroWrapper<StateCommitmentInfos>, Mode>,
882892
}
883893

884894
impl FileHandlers<RW> {
@@ -921,6 +931,14 @@ impl FileHandlers<RW> {
921931
self.clone().accessed_keys.append(accessed_keys)
922932
}
923933

934+
#[cfg(feature = "os_input")]
935+
fn append_state_commitment_infos(
936+
&self,
937+
state_commitment_infos: &StateCommitmentInfos,
938+
) -> LocationInFile {
939+
self.clone().state_commitment_infos.append(state_commitment_infos)
940+
}
941+
924942
// TODO(dan): Consider 1. flushing only the relevant files, 2. flushing concurrently.
925943
#[latency_histogram("storage_file_handler_flush_latency_seconds", false)]
926944
fn flush(&self) {
@@ -933,6 +951,8 @@ impl FileHandlers<RW> {
933951
self.transaction.flush();
934952
#[cfg(feature = "os_input")]
935953
self.accessed_keys.flush();
954+
#[cfg(feature = "os_input")]
955+
self.state_commitment_infos.flush();
936956
}
937957
}
938958

@@ -948,6 +968,8 @@ impl<Mode: TransactionKind> FileHandlers<Mode> {
948968
("transaction".to_string(), self.transaction.stats()),
949969
#[cfg(feature = "os_input")]
950970
("accessed_keys".to_string(), self.accessed_keys.stats()),
971+
#[cfg(feature = "os_input")]
972+
("state_commitment_infos".to_string(), self.state_commitment_infos.stats()),
951973
])
952974
}
953975

@@ -1017,6 +1039,17 @@ impl<Mode: TransactionKind> FileHandlers<Mode> {
10171039
msg: format!("AccessedKeys at location {location:?} not found."),
10181040
})
10191041
}
1042+
1043+
#[cfg(feature = "os_input")]
1044+
// Returns the commitment infos at the given location or an error in case they don't exist.
1045+
pub(crate) fn get_state_commitment_infos_unchecked(
1046+
&self,
1047+
location: LocationInFile,
1048+
) -> StorageResult<StateCommitmentInfos> {
1049+
self.state_commitment_infos.get(location)?.ok_or(StorageError::DBInconsistency {
1050+
msg: format!("StateCommitmentInfos at location {location:?} not found."),
1051+
})
1052+
}
10201053
}
10211054

10221055
fn open_storage_files(
@@ -1055,6 +1088,9 @@ fn open_storage_files(
10551088
#[cfg(feature = "os_input")]
10561089
let (accessed_keys_writer, accessed_keys_reader) =
10571090
open_storage_file!("accessed_keys", AccessedKeys)?;
1091+
#[cfg(feature = "os_input")]
1092+
let (state_commitment_infos_writer, state_commitment_infos_reader) =
1093+
open_storage_file!("state_commitment_infos", StateCommitmentInfos)?;
10581094

10591095
Ok((
10601096
FileHandlers {
@@ -1066,6 +1102,8 @@ fn open_storage_files(
10661102
transaction: transaction_writer,
10671103
#[cfg(feature = "os_input")]
10681104
accessed_keys: accessed_keys_writer,
1105+
#[cfg(feature = "os_input")]
1106+
state_commitment_infos: state_commitment_infos_writer,
10691107
},
10701108
FileHandlers {
10711109
thin_state_diff: thin_state_diff_reader,
@@ -1076,6 +1114,8 @@ fn open_storage_files(
10761114
transaction: transaction_reader,
10771115
#[cfg(feature = "os_input")]
10781116
accessed_keys: accessed_keys_reader,
1117+
#[cfg(feature = "os_input")]
1118+
state_commitment_infos: state_commitment_infos_reader,
10791119
},
10801120
))
10811121
}
@@ -1098,4 +1138,7 @@ pub enum OffsetKind {
10981138
/// An accessed-keys file.
10991139
#[cfg(feature = "os_input")]
11001140
AccessedKeys,
1141+
/// A state-commitment-infos file.
1142+
#[cfg(feature = "os_input")]
1143+
StateCommitmentInfos,
11011144
}

crates/apollo_storage/src/serialization/serializers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ auto_storage_serde! {
372372
Transaction = 5,
373373
#[cfg(feature = "os_input")]
374374
AccessedKeys = 6,
375+
#[cfg(feature = "os_input")]
376+
StateCommitmentInfos = 7,
375377
}
376378
pub struct PartialBlockHashComponents {
377379
pub header_commitments: BlockHeaderCommitments,
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//! Storage for the per-block OS-input commitment infos (state-trie commitment data for the OS).
2+
3+
use starknet_api::block::BlockNumber;
4+
pub use starknet_committer::patricia_merkle_tree::types::StateCommitmentInfos;
5+
6+
#[cfg(test)]
7+
#[path = "state_commitment_infos_test.rs"]
8+
mod state_commitment_infos_test;
9+
10+
use crate::compression_utils::{compress, decompress};
11+
use crate::db::serialization::{StorageSerde, StorageSerdeError};
12+
use crate::db::table_types::Table;
13+
use crate::db::{TransactionKind, RW};
14+
use crate::{OffsetKind, StorageResult, StorageTxn};
15+
16+
impl StorageSerde for StateCommitmentInfos {
17+
fn serialize_into(&self, res: &mut impl std::io::Write) -> Result<(), StorageSerdeError> {
18+
let bytes = serde_json::to_vec(self)?;
19+
let compressed = compress(bytes.as_slice())?;
20+
compressed.serialize_into(res)
21+
}
22+
23+
fn deserialize_from(bytes: &mut impl std::io::Read) -> Option<Self> {
24+
let compressed = Vec::<u8>::deserialize_from(bytes)?;
25+
let data = decompress(compressed.as_slice()).ok()?;
26+
serde_json::from_slice(&data).ok()
27+
}
28+
}
29+
30+
/// Interface for reading the OS-input commitment infos from storage.
31+
pub trait StateCommitmentInfosStorageReader<Mode: TransactionKind> {
32+
/// Returns the commitment infos for the given block, or `None` if not stored.
33+
fn get_state_commitment_infos(
34+
&self,
35+
block_number: BlockNumber,
36+
) -> StorageResult<Option<StateCommitmentInfos>>;
37+
}
38+
39+
/// Interface for writing the OS-input commitment infos to storage.
40+
pub trait StateCommitmentInfosStorageWriter
41+
where
42+
Self: Sized,
43+
{
44+
/// Appends the commitment infos for the given block to storage.
45+
fn append_state_commitment_infos(
46+
self,
47+
block_number: BlockNumber,
48+
state_commitment_infos: &StateCommitmentInfos,
49+
) -> StorageResult<Self>;
50+
51+
/// Removes the commitment infos for the given block from storage.
52+
/// If no entry exists for the block, returns without error.
53+
fn revert_state_commitment_infos(self, block_number: BlockNumber) -> StorageResult<Self>;
54+
}
55+
56+
impl<Mode: TransactionKind> StateCommitmentInfosStorageReader<Mode> for StorageTxn<'_, Mode> {
57+
fn get_state_commitment_infos(
58+
&self,
59+
block_number: BlockNumber,
60+
) -> StorageResult<Option<StateCommitmentInfos>> {
61+
let table = self.open_table(&self.tables.state_commitment_infos)?;
62+
let Some(location) = table.get(&self.txn, &block_number)? else {
63+
return Ok(None);
64+
};
65+
Ok(Some(self.file_handlers.get_state_commitment_infos_unchecked(location)?))
66+
}
67+
}
68+
69+
impl StateCommitmentInfosStorageWriter for StorageTxn<'_, RW> {
70+
fn append_state_commitment_infos(
71+
self,
72+
block_number: BlockNumber,
73+
state_commitment_infos: &StateCommitmentInfos,
74+
) -> StorageResult<Self> {
75+
let file_offset_table = self.txn.open_table(&self.tables.file_offsets)?;
76+
let state_commitment_infos_table = self.open_table(&self.tables.state_commitment_infos)?;
77+
78+
let location = self.file_handlers.append_state_commitment_infos(state_commitment_infos);
79+
state_commitment_infos_table.upsert(&self.txn, &block_number, &location)?;
80+
file_offset_table.upsert(
81+
&self.txn,
82+
&OffsetKind::StateCommitmentInfos,
83+
&location.next_offset(),
84+
)?;
85+
86+
Ok(self)
87+
}
88+
89+
fn revert_state_commitment_infos(self, block_number: BlockNumber) -> StorageResult<Self> {
90+
let state_commitment_infos_table = self.open_table(&self.tables.state_commitment_infos)?;
91+
state_commitment_infos_table.delete(&self.txn, &block_number)?;
92+
Ok(self)
93+
}
94+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::collections::HashMap;
2+
3+
use starknet_api::block::BlockNumber;
4+
use starknet_committer::patricia_merkle_tree::types::{CommitmentInfo, StateCommitmentInfos};
5+
6+
use crate::state_commitment_infos::{
7+
StateCommitmentInfosStorageReader,
8+
StateCommitmentInfosStorageWriter,
9+
};
10+
use crate::test_utils::get_test_storage;
11+
12+
fn sample_state_commitment_infos() -> StateCommitmentInfos {
13+
StateCommitmentInfos {
14+
contracts_trie_commitment_info: CommitmentInfo::default(),
15+
classes_trie_commitment_info: CommitmentInfo::default(),
16+
storage_tries_commitment_infos: HashMap::new(),
17+
}
18+
}
19+
20+
#[test]
21+
fn append_and_get_state_commitment_infos() {
22+
let (reader, mut writer) = get_test_storage().0;
23+
let height = BlockNumber(5);
24+
let state_commitment_infos = sample_state_commitment_infos();
25+
26+
// No infos stored for the height yet.
27+
assert_eq!(reader.begin_ro_txn().unwrap().get_state_commitment_infos(height).unwrap(), None);
28+
29+
writer
30+
.begin_rw_txn()
31+
.unwrap()
32+
.append_state_commitment_infos(height, &state_commitment_infos)
33+
.unwrap()
34+
.commit()
35+
.unwrap();
36+
37+
assert_eq!(
38+
reader.begin_ro_txn().unwrap().get_state_commitment_infos(height).unwrap(),
39+
Some(state_commitment_infos)
40+
);
41+
// A different height is still empty.
42+
assert_eq!(
43+
reader.begin_ro_txn().unwrap().get_state_commitment_infos(BlockNumber(6)).unwrap(),
44+
None
45+
);
46+
}
47+
48+
#[test]
49+
fn revert_state_commitment_infos() {
50+
let (reader, mut writer) = get_test_storage().0;
51+
let height = BlockNumber(5);
52+
53+
writer
54+
.begin_rw_txn()
55+
.unwrap()
56+
.append_state_commitment_infos(height, &sample_state_commitment_infos())
57+
.unwrap()
58+
.revert_state_commitment_infos(height)
59+
.unwrap()
60+
.commit()
61+
.unwrap();
62+
63+
assert_eq!(reader.begin_ro_txn().unwrap().get_state_commitment_infos(height).unwrap(), None);
64+
65+
// Reverting a height with no stored infos is a no-op.
66+
writer
67+
.begin_rw_txn()
68+
.unwrap()
69+
.revert_state_commitment_infos(BlockNumber(99))
70+
.unwrap()
71+
.commit()
72+
.unwrap();
73+
}

0 commit comments

Comments
 (0)