sui_rpc_store/schema/
checkpoint_contents.rs1use sui_consistent_store::Protobuf;
10use sui_consistent_store::error::DecodeError;
11use sui_consistent_store::error::Error;
12use sui_consistent_store::reader::Reader;
13use sui_types::messages_checkpoint::CheckpointContents;
14use sui_types::messages_checkpoint::CheckpointSequenceNumber;
15
16use crate::proto::StoredCheckpointContents;
17use crate::schema::primitives::U64Be;
18
19pub const NAME: &str = "checkpoint_contents";
20
21pub type Key = U64Be;
22pub type Value = Protobuf<StoredCheckpointContents>;
23
24pub fn options(resolver: &sui_consistent_store::CfOptionsResolver) -> rocksdb::Options {
25 resolver.options(NAME)
26}
27
28pub fn store(contents: &CheckpointContents) -> Value {
35 let bcs = bcs::to_bytes(contents).expect("bcs encode CheckpointContents");
36 Protobuf(StoredCheckpointContents { bcs: bcs.into() })
37}
38
39impl<R: Reader> super::RpcStoreSchema<R> {
40 pub fn get_checkpoint_contents(
42 &self,
43 seq: CheckpointSequenceNumber,
44 ) -> Result<Option<CheckpointContents>, Error> {
45 let Some(stored) = self.checkpoint_contents.get(&U64Be(seq))? else {
46 return Ok(None);
47 };
48 let contents: CheckpointContents = bcs::from_bytes(&stored.into_inner().bcs)
49 .map_err(|e| DecodeError::with_source("bcs decode CheckpointContents", e))?;
50 Ok(Some(contents))
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use sui_consistent_store::Db;
57 use sui_consistent_store::DbOptions;
58 use sui_types::base_types::ExecutionDigests;
59
60 use super::*;
61 use crate::RpcStoreSchema;
62
63 fn fresh_db() -> (tempfile::TempDir, sui_consistent_store::Db, RpcStoreSchema) {
64 let dir = tempfile::tempdir().unwrap();
65 let (db, schema) = Db::open::<RpcStoreSchema>(dir.path(), DbOptions::default()).unwrap();
66 (dir, db, schema)
67 }
68
69 fn dummy_contents(n: usize) -> CheckpointContents {
70 let digests: Vec<_> = (0..n).map(|_| ExecutionDigests::random()).collect();
71 CheckpointContents::new_with_digests_only_for_tests(digests)
72 }
73
74 #[test]
75 fn get_returns_none_for_unknown_seq() {
76 let (_dir, _db, schema) = fresh_db();
77 assert!(schema.get_checkpoint_contents(7).unwrap().is_none());
78 }
79
80 #[test]
81 fn store_then_get_round_trips() {
82 let (_dir, db, schema) = fresh_db();
83 let contents = dummy_contents(3);
84
85 let mut batch = db.batch();
86 batch
87 .put(&schema.checkpoint_contents, &U64Be(42), &store(&contents))
88 .unwrap();
89 batch.commit().unwrap();
90
91 let read = schema
92 .get_checkpoint_contents(42)
93 .unwrap()
94 .expect("contents present");
95 assert_eq!(read, contents);
96 }
97
98 #[test]
99 fn overwrite_replaces_previous() {
100 let (_dir, db, schema) = fresh_db();
101 let first = dummy_contents(1);
102 let later = dummy_contents(2);
103
104 let mut batch = db.batch();
105 batch
106 .put(&schema.checkpoint_contents, &U64Be(42), &store(&first))
107 .unwrap();
108 batch
109 .put(&schema.checkpoint_contents, &U64Be(42), &store(&later))
110 .unwrap();
111 batch.commit().unwrap();
112
113 let read = schema
114 .get_checkpoint_contents(42)
115 .unwrap()
116 .expect("contents present");
117 assert_eq!(read, later);
118 }
119}