sui_rpc_store/reader/
object_store.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! [`ObjectStore`] adapter — point lookups by id or `(id, version)`.
5//!
6//! Delegates to the inherent helpers
7//! [`RpcStoreSchema::get_object`] (latest live version via a reverse
8//! scan of the `objects` CF) and
9//! [`RpcStoreSchema::get_object_by_key`] (direct version lookup).
10//! The trait returns `Option<Object>`, so storage errors are
11//! logged and surfaced as `None`; callers that need to distinguish
12//! "missing" from "errored" should go through the inherent helpers
13//! directly.
14
15use sui_consistent_store::reader::Reader;
16use sui_types::base_types::ObjectID;
17use sui_types::base_types::VersionNumber;
18use sui_types::object::Object;
19use sui_types::storage::ObjectStore;
20use tracing::error;
21
22use crate::reader::RpcStoreReader;
23
24impl<R: Reader + Send + Sync> ObjectStore for RpcStoreReader<R> {
25    fn get_object(&self, object_id: &ObjectID) -> Option<Object> {
26        match self.schema().get_object(*object_id) {
27            Ok(object) => object,
28            Err(e) => {
29                error!(?object_id, "get_object: {e:#}");
30                None
31            }
32        }
33    }
34
35    fn get_object_by_key(&self, object_id: &ObjectID, version: VersionNumber) -> Option<Object> {
36        match self.schema().get_object_by_key(*object_id, version) {
37            Ok(object) => object,
38            Err(e) => {
39                error!(?object_id, ?version, "get_object_by_key: {e:#}");
40                None
41            }
42        }
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use std::sync::Arc;
49
50    use sui_consistent_store::Db;
51    use sui_consistent_store::DbOptions;
52    use sui_types::base_types::ObjectID;
53    use sui_types::base_types::SuiAddress;
54    use sui_types::object::Object;
55    use sui_types::storage::ObjectKey;
56    use sui_types::storage::ObjectStore;
57
58    use crate::RpcStoreSchema;
59    use crate::reader::RpcStoreReader;
60    use crate::schema::objects;
61
62    fn open() -> (tempfile::TempDir, Db, RpcStoreSchema) {
63        let dir = tempfile::tempdir().unwrap();
64        let (db, schema) = Db::open::<RpcStoreSchema>(dir.path(), DbOptions::default()).unwrap();
65        (dir, db, schema)
66    }
67
68    fn dummy(id: ObjectID) -> Object {
69        Object::with_id_owner_for_testing(id, SuiAddress::ZERO)
70    }
71
72    fn seed(db: &Db, schema: &RpcStoreSchema, object: &Object) {
73        // The latest-version read reverse-scans `objects`, so a single
74        // version row is all the live lookup needs.
75        let mut batch = db.batch();
76        batch
77            .put(
78                &schema.objects,
79                &objects::Key {
80                    id: object.id(),
81                    version: object.version(),
82                },
83                &objects::store(object),
84            )
85            .unwrap();
86        batch.commit().unwrap();
87    }
88
89    #[test]
90    fn get_object_returns_latest_via_checkpoint_index() {
91        let (_dir, db, schema) = open();
92        let object = dummy(ObjectID::from_single_byte(1));
93        seed(&db, &schema, &object);
94
95        let reader = RpcStoreReader::new(db.clone(), Arc::new(schema));
96        let read = reader
97            .get_object(&object.id())
98            .expect("object present via checkpoint index");
99        assert_eq!(read, object);
100    }
101
102    #[test]
103    fn get_object_returns_none_for_missing_id() {
104        let (_dir, db, schema) = open();
105        let reader = RpcStoreReader::new(db.clone(), Arc::new(schema));
106        assert!(reader.get_object(&ObjectID::from_single_byte(7)).is_none());
107    }
108
109    #[test]
110    fn get_object_by_key_returns_exact_version() {
111        let (_dir, db, schema) = open();
112        let object = dummy(ObjectID::from_single_byte(2));
113        seed(&db, &schema, &object);
114
115        let reader = RpcStoreReader::new(db.clone(), Arc::new(schema));
116        let read = reader
117            .get_object_by_key(&object.id(), object.version())
118            .expect("object present at exact version");
119        assert_eq!(read, object);
120    }
121
122    #[test]
123    fn get_object_by_key_returns_none_for_unknown_version() {
124        let (_dir, db, schema) = open();
125        let object = dummy(ObjectID::from_single_byte(3));
126        seed(&db, &schema, &object);
127
128        let reader = RpcStoreReader::new(db.clone(), Arc::new(schema));
129        let missing_version = sui_types::base_types::SequenceNumber::from_u64(99);
130        assert!(
131            reader
132                .get_object_by_key(&object.id(), missing_version)
133                .is_none()
134        );
135    }
136
137    #[test]
138    fn multi_get_objects_uses_default_delegate() {
139        let (_dir, db, schema) = open();
140        let o1 = dummy(ObjectID::from_single_byte(4));
141        let o2 = dummy(ObjectID::from_single_byte(5));
142        seed(&db, &schema, &o1);
143        // Note: o2 is intentionally not seeded.
144
145        let reader = RpcStoreReader::new(db.clone(), Arc::new(schema));
146        let results = reader.multi_get_objects(&[o1.id(), o2.id()]);
147        assert_eq!(results.len(), 2);
148        assert_eq!(results[0].as_ref(), Some(&o1));
149        assert!(results[1].is_none());
150    }
151
152    #[test]
153    fn multi_get_objects_by_key_uses_default_delegate() {
154        let (_dir, db, schema) = open();
155        let o1 = dummy(ObjectID::from_single_byte(6));
156        let o2 = dummy(ObjectID::from_single_byte(7));
157        seed(&db, &schema, &o1);
158        seed(&db, &schema, &o2);
159
160        let reader = RpcStoreReader::new(db.clone(), Arc::new(schema));
161        let keys = vec![
162            ObjectKey(o1.id(), o1.version()),
163            ObjectKey(o2.id(), o2.version()),
164        ];
165        let results = reader.multi_get_objects_by_key(&keys);
166        assert_eq!(results.len(), 2);
167        assert_eq!(results[0].as_ref(), Some(&o1));
168        assert_eq!(results[1].as_ref(), Some(&o2));
169    }
170}