sui_rpc_store/schema/
transactions.rs1use sui_consistent_store::Protobuf;
7use sui_consistent_store::error::DecodeError;
8use sui_consistent_store::error::Error;
9use sui_consistent_store::reader::Reader;
10use sui_types::signature::GenericSignature;
11use sui_types::transaction::TransactionData;
12
13use crate::proto::StoredTransaction;
14use crate::schema::primitives::U64Be;
15
16pub const NAME: &str = "transactions";
17
18pub type Key = U64Be;
19pub type Value = Protobuf<StoredTransaction>;
20
21pub fn options(resolver: &sui_consistent_store::CfOptionsResolver) -> rocksdb::Options {
22 resolver.options(NAME)
23}
24
25pub fn store(transaction: &TransactionData, signatures: &[GenericSignature]) -> Value {
32 let transaction_bcs = bcs::to_bytes(transaction).expect("bcs encode TransactionData");
33 let signatures_bcs = bcs::to_bytes(signatures).expect("bcs encode Vec<GenericSignature>");
34 Protobuf(StoredTransaction {
35 transaction_bcs: transaction_bcs.into(),
36 signatures_bcs: signatures_bcs.into(),
37 })
38}
39
40impl<R: Reader> super::RpcStoreSchema<R> {
41 pub fn get_transaction(
47 &self,
48 tx_seq: u64,
49 ) -> Result<Option<(TransactionData, Vec<GenericSignature>)>, Error> {
50 let Some(stored) = self.transactions.get(&U64Be(tx_seq))? else {
51 return Ok(None);
52 };
53 let stored = stored.into_inner();
54 let transaction: TransactionData = bcs::from_bytes(&stored.transaction_bcs)
55 .map_err(|e| DecodeError::with_source("bcs decode TransactionData", e))?;
56 let signatures: Vec<GenericSignature> = bcs::from_bytes(&stored.signatures_bcs)
57 .map_err(|e| DecodeError::with_source("bcs decode Vec<GenericSignature>", e))?;
58 Ok(Some((transaction, signatures)))
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use sui_consistent_store::Db;
65 use sui_consistent_store::DbOptions;
66 use sui_types::base_types::FullObjectRef;
67 use sui_types::base_types::SuiAddress;
68 use sui_types::base_types::random_object_ref;
69
70 use super::*;
71 use crate::RpcStoreSchema;
72
73 fn fresh_db() -> (tempfile::TempDir, sui_consistent_store::Db, RpcStoreSchema) {
74 let dir = tempfile::tempdir().unwrap();
75 let (db, schema) = Db::open::<RpcStoreSchema>(dir.path(), DbOptions::default()).unwrap();
76 (dir, db, schema)
77 }
78
79 fn dummy_data() -> TransactionData {
80 TransactionData::new_transfer(
81 SuiAddress::ZERO,
82 FullObjectRef::from_fastpath_ref(random_object_ref()),
83 SuiAddress::ZERO,
84 random_object_ref(),
85 1_000_000,
86 1_000,
87 )
88 }
89
90 #[test]
91 fn get_returns_none_for_unknown_seq() {
92 let (_dir, _db, schema) = fresh_db();
93 assert!(schema.get_transaction(7).unwrap().is_none());
94 }
95
96 #[test]
97 fn store_then_get_round_trips() {
98 let (_dir, db, schema) = fresh_db();
99 let data = dummy_data();
100 let sigs: Vec<GenericSignature> = vec![];
101 let expected_data_bcs = bcs::to_bytes(&data).unwrap();
102 let expected_sigs_bcs = bcs::to_bytes(&sigs).unwrap();
103
104 let mut batch = db.batch();
105 batch
106 .put(&schema.transactions, &U64Be(42), &store(&data, &sigs))
107 .unwrap();
108 batch.commit().unwrap();
109
110 let (read_data, read_sigs) = schema
111 .get_transaction(42)
112 .unwrap()
113 .expect("transaction present");
114 assert_eq!(bcs::to_bytes(&read_data).unwrap(), expected_data_bcs);
115 assert_eq!(bcs::to_bytes(&read_sigs).unwrap(), expected_sigs_bcs);
116 }
117
118 #[test]
119 fn overwrite_replaces_previous() {
120 let (_dir, db, schema) = fresh_db();
121 let first = dummy_data();
122 let later = dummy_data();
123 let later_bcs = bcs::to_bytes(&later).unwrap();
124
125 let mut batch = db.batch();
126 batch
127 .put(&schema.transactions, &U64Be(42), &store(&first, &[]))
128 .unwrap();
129 batch
130 .put(&schema.transactions, &U64Be(42), &store(&later, &[]))
131 .unwrap();
132 batch.commit().unwrap();
133
134 let (read_data, _) = schema
135 .get_transaction(42)
136 .unwrap()
137 .expect("transaction present");
138 assert_eq!(bcs::to_bytes(&read_data).unwrap(), later_bcs);
139 }
140}