sui_rpc_store/schema/
checkpoint_contents.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! `checkpoint_seq` → `StoredCheckpointContents`.
5//!
6//! Holds the ordered list of executed transaction digests for each
7//! checkpoint.
8
9use 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
28/// Build a `StoredCheckpointContents` row from canonical
29/// `CheckpointContents`.
30///
31/// BCS-encode failures here would indicate either OOM or a bug in
32/// the type's `Serialize` impl; we panic rather than thread a
33/// `Result` through every call site.
34pub 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    /// Look up the contents of a checkpoint by sequence number.
41    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}