sui_rpc_store/schema/
tx_seq_by_digest.rs1use bytes::Buf;
15use bytes::BufMut;
16use sui_consistent_store::Decode;
17use sui_consistent_store::Encode;
18use sui_consistent_store::error::DecodeError;
19use sui_consistent_store::error::EncodeError;
20use sui_consistent_store::error::Error;
21use sui_consistent_store::reader::Reader;
22use sui_types::digests::TransactionDigest;
23
24use crate::schema::primitives::U64Varint;
25
26pub const NAME: &str = "tx_seq_by_digest";
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct Key(pub TransactionDigest);
32
33pub type Value = U64Varint;
34
35impl Encode for Key {
36 fn encode_into<B: BufMut>(&self, buf: &mut B) -> Result<(), EncodeError> {
37 buf.put_slice(self.0.inner());
38 Ok(())
39 }
40}
41
42impl Decode for Key {
43 fn decode<B: Buf>(buf: &mut B) -> Result<Self, DecodeError> {
44 if buf.remaining() != 32 {
45 return Err(DecodeError::msg(format!(
46 "expected 32 bytes for {NAME} Key, got {}",
47 buf.remaining(),
48 )));
49 }
50 let mut bytes = [0u8; 32];
51 buf.copy_to_slice(&mut bytes);
52 Ok(Key(TransactionDigest::new(bytes)))
53 }
54}
55
56pub fn options(resolver: &sui_consistent_store::CfOptionsResolver) -> rocksdb::Options {
57 resolver.options(NAME)
58}
59
60impl<R: Reader> super::RpcStoreSchema<R> {
61 pub fn get_tx_seq_by_digest(&self, digest: &TransactionDigest) -> Result<Option<u64>, Error> {
64 Ok(self.tx_seq_by_digest.get(&Key(*digest))?.map(|v| v.0))
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use sui_consistent_store::Db;
71 use sui_consistent_store::DbOptions;
72
73 use super::*;
74 use crate::RpcStoreSchema;
75
76 fn fresh_db() -> (tempfile::TempDir, sui_consistent_store::Db, RpcStoreSchema) {
77 let dir = tempfile::tempdir().unwrap();
78 let (db, schema) = Db::open::<RpcStoreSchema>(dir.path(), DbOptions::default()).unwrap();
79 (dir, db, schema)
80 }
81
82 #[test]
83 fn get_returns_none_for_unknown_digest() {
84 let (_dir, _db, schema) = fresh_db();
85 let digest = TransactionDigest::random();
86 assert!(schema.get_tx_seq_by_digest(&digest).unwrap().is_none());
87 }
88
89 #[test]
90 fn put_then_get_round_trips() {
91 let (_dir, db, schema) = fresh_db();
92 let digest = TransactionDigest::random();
93
94 let mut batch = db.batch();
95 batch
96 .put(&schema.tx_seq_by_digest, &Key(digest), &U64Varint(42))
97 .unwrap();
98 batch.commit().unwrap();
99
100 let seq = schema
101 .get_tx_seq_by_digest(&digest)
102 .unwrap()
103 .expect("digest present");
104 assert_eq!(seq, 42);
105 }
106
107 #[test]
108 fn distinct_digests_dont_collide() {
109 let (_dir, db, schema) = fresh_db();
110 let d1 = TransactionDigest::random();
111 let d2 = TransactionDigest::random();
112
113 let mut batch = db.batch();
114 batch
115 .put(&schema.tx_seq_by_digest, &Key(d1), &U64Varint(1))
116 .unwrap();
117 batch
118 .put(&schema.tx_seq_by_digest, &Key(d2), &U64Varint(2))
119 .unwrap();
120 batch.commit().unwrap();
121
122 assert_eq!(schema.get_tx_seq_by_digest(&d1).unwrap(), Some(1));
123 assert_eq!(schema.get_tx_seq_by_digest(&d2).unwrap(), Some(2));
124 }
125}