iota_storage/
write_path_pending_tx_log.rs1use std::path::PathBuf;
12
13use iota_sdk_types::{SenderSignedTransaction, TransactionDigest};
14use iota_types::{
15 crypto::EmptySignInfo,
16 error::{IotaError, IotaResult},
17 message_envelope::TrustedEnvelope,
18 transaction::VerifiedTransaction,
19};
20use tracing::instrument;
21use typed_store::{
22 DBMapUtils,
23 rocks::{DBMap, MetricConf},
24 traits::Map,
25};
26
27use crate::mutex_table::MutexTable;
28
29pub type IsFirstRecord = bool;
30const NUM_SHARDS: usize = 4096;
31
32#[derive(DBMapUtils)]
33struct WritePathPendingTransactionTable {
34 logs: DBMap<TransactionDigest, TrustedEnvelope<SenderSignedTransaction, EmptySignInfo>>,
35}
36
37pub struct WritePathPendingTransactionLog {
38 pending_transactions: WritePathPendingTransactionTable,
39 mutex_table: MutexTable<TransactionDigest>,
40}
41
42impl WritePathPendingTransactionLog {
43 pub fn new(path: PathBuf) -> Self {
44 let pending_transactions = WritePathPendingTransactionTable::open_tables_read_write(
45 path,
46 MetricConf::new("pending_tx_log"),
47 None,
48 None,
49 );
50 Self {
51 pending_transactions,
52 mutex_table: MutexTable::new(NUM_SHARDS),
53 }
54 }
55
56 #[instrument(level = "trace", skip_all, fields(tx_digest = ?tx.digest()))]
62 pub async fn write_pending_transaction_maybe(
63 &self,
64 tx: &VerifiedTransaction,
65 ) -> IotaResult<IsFirstRecord> {
66 let tx_digest = tx.digest();
67 let _guard = self.mutex_table.acquire_lock(*tx_digest);
68 if self.pending_transactions.logs.contains_key(tx_digest)? {
69 Ok(false)
70 } else {
71 self.pending_transactions
72 .logs
73 .insert(tx_digest, tx.serializable_ref())?;
74 Ok(true)
75 }
76 }
77
78 pub fn finish_transaction(&self, tx: &TransactionDigest) -> IotaResult {
87 let mut write_batch = self.pending_transactions.logs.batch();
88 write_batch.delete_batch(&self.pending_transactions.logs, std::iter::once(tx))?;
89 write_batch.write().map_err(IotaError::from)
90 }
91
92 pub fn load_all_pending_transactions(&self) -> IotaResult<Vec<VerifiedTransaction>> {
93 Ok(self
94 .pending_transactions
95 .logs
96 .safe_iter()
97 .map(|item| item.map(|(_tx_digest, tx)| VerifiedTransaction::from(tx)))
98 .collect::<Result<Vec<_>, _>>()?)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use std::collections::HashSet;
105
106 use anyhow;
107 use iota_types::utils::create_fake_transaction;
108
109 use super::*;
110
111 #[tokio::test]
112 async fn test_pending_tx_log_basic() -> anyhow::Result<()> {
113 let tmp_dir = iota_common::tempdir();
114 let pending_txes = WritePathPendingTransactionLog::new(tmp_dir.path().to_path_buf());
115 let tx = VerifiedTransaction::new_unchecked(create_fake_transaction());
116 let tx_digest = *tx.digest();
117 assert!(
118 pending_txes
119 .write_pending_transaction_maybe(&tx)
120 .await
121 .unwrap()
122 );
123 assert!(
125 !pending_txes
126 .write_pending_transaction_maybe(&tx)
127 .await
128 .unwrap()
129 );
130
131 let loaded_txes = pending_txes.load_all_pending_transactions()?;
132 assert_eq!(vec![tx], loaded_txes);
133
134 pending_txes.finish_transaction(&tx_digest).unwrap();
135 let loaded_txes = pending_txes.load_all_pending_transactions()?;
136 assert!(loaded_txes.is_empty());
137
138 pending_txes.finish_transaction(&tx_digest).unwrap();
140
141 let txes: Vec<_> = (0..10)
143 .map(|_| VerifiedTransaction::new_unchecked(create_fake_transaction()))
144 .collect();
145 for tx in txes.iter().take(10) {
146 assert!(
147 pending_txes
148 .write_pending_transaction_maybe(tx)
149 .await
150 .unwrap()
151 );
152 }
153 let loaded_tx_digests: HashSet<_> = pending_txes
154 .load_all_pending_transactions()?
155 .iter()
156 .map(|t| *t.digest())
157 .collect();
158 assert_eq!(
159 txes.iter().map(|t| *t.digest()).collect::<HashSet<_>>(),
160 loaded_tx_digests
161 );
162
163 for tx in txes.iter().take(5) {
164 pending_txes.finish_transaction(tx.digest()).unwrap();
165 }
166 let loaded_tx_digests: HashSet<_> = pending_txes
167 .load_all_pending_transactions()?
168 .iter()
169 .map(|t| *t.digest())
170 .collect();
171 assert_eq!(
172 txes.iter()
173 .skip(5)
174 .map(|t| *t.digest())
175 .collect::<HashSet<_>>(),
176 loaded_tx_digests
177 );
178
179 Ok(())
180 }
181}