tx_store.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::collections::HashMap;
  19. use darkfi_serial::{deserialize, serialize};
  20. use crate::{tx::Transaction, Error, Result};
  21. use super::{parse_record, parse_u64_key_record, SledDbOverlayPtr};
  22. const SLED_TX_TREE: &[u8] = b"_transactions";
  23. const SLED_PENDING_TX_TREE: &[u8] = b"_pending_transactions";
  24. const SLED_PENDING_TX_ORDER_TREE: &[u8] = b"_pending_transactions_order";
  25. /// The `TxStore` is a `sled` tree storing all the blockchain's
  26. /// transactions where the key is the transaction hash, and the value is
  27. /// the serialized transaction.
  28. #[derive(Clone)]
  29. pub struct TxStore(pub sled::Tree);
  30. impl TxStore {
  31. /// Opens a new or existing `TxStore` on the given sled database.
  32. pub fn new(db: &sled::Db) -> Result<Self> {
  33. let tree = db.open_tree(SLED_TX_TREE)?;
  34. Ok(Self(tree))
  35. }
  36. /// Insert a slice of [`Transaction`] into the txstore.
  37. pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
  38. let (batch, ret) = self.insert_batch(transactions)?;
  39. self.0.apply_batch(batch)?;
  40. Ok(ret)
  41. }
  42. /// Generate the sled batch corresponding to an insert, so caller
  43. /// can handle the write operation.
  44. /// The transactions are hashed with BLAKE3 and this hash is used as
  45. /// the key, while the value is the serialized [`Transaction`] itself.
  46. /// On success, the function returns the transaction hashes in the same
  47. /// order as the input transactions, along with the corresponding operation
  48. /// batch.
  49. pub fn insert_batch(
  50. &self,
  51. transactions: &[Transaction],
  52. ) -> Result<(sled::Batch, Vec<blake3::Hash>)> {
  53. let mut ret = Vec::with_capacity(transactions.len());
  54. let mut batch = sled::Batch::default();
  55. for tx in transactions {
  56. let serialized = serialize(tx);
  57. let tx_hash = blake3::hash(&serialized);
  58. batch.insert(tx_hash.as_bytes(), serialized);
  59. ret.push(tx_hash);
  60. }
  61. Ok((batch, ret))
  62. }
  63. /// Check if the txstore contains a given transaction hash.
  64. pub fn contains(&self, tx_hash: &blake3::Hash) -> Result<bool> {
  65. Ok(self.0.contains_key(tx_hash.as_bytes())?)
  66. }
  67. /// Fetch given tx hashes from the txstore.
  68. /// The resulting vector contains `Option`, which is `Some` if the tx
  69. /// was found in the txstore, and otherwise it is `None`, if it has not.
  70. /// The second parameter is a boolean which tells the function to fail in
  71. /// case at least one block was not found.
  72. pub fn get(
  73. &self,
  74. tx_hashes: &[blake3::Hash],
  75. strict: bool,
  76. ) -> Result<Vec<Option<Transaction>>> {
  77. let mut ret = Vec::with_capacity(tx_hashes.len());
  78. for tx_hash in tx_hashes {
  79. if let Some(found) = self.0.get(tx_hash.as_bytes())? {
  80. let tx = deserialize(&found)?;
  81. ret.push(Some(tx));
  82. } else {
  83. if strict {
  84. let s = tx_hash.to_hex().as_str().to_string();
  85. return Err(Error::TransactionNotFound(s))
  86. }
  87. ret.push(None);
  88. }
  89. }
  90. Ok(ret)
  91. }
  92. /// Retrieve all transactions from the txstore in the form of a tuple
  93. /// (`tx_hash`, `tx`).
  94. /// Be careful as this will try to load everything in memory.
  95. pub fn get_all(&self) -> Result<Vec<(blake3::Hash, Transaction)>> {
  96. let mut txs = vec![];
  97. for tx in self.0.iter() {
  98. txs.push(parse_record(tx.unwrap())?);
  99. }
  100. Ok(txs)
  101. }
  102. /// Retrieve records count
  103. pub fn len(&self) -> usize {
  104. self.0.len()
  105. }
  106. pub fn is_empty(&self) -> bool {
  107. self.0.is_empty()
  108. }
  109. }
  110. /// Overlay structure over a [`TxStore`] instance.
  111. pub struct TxStoreOverlay(SledDbOverlayPtr);
  112. impl TxStoreOverlay {
  113. pub fn new(overlay: &SledDbOverlayPtr) -> Result<Self> {
  114. overlay.lock().unwrap().open_tree(SLED_TX_TREE)?;
  115. Ok(Self(overlay.clone()))
  116. }
  117. /// Insert a slice of [`Transaction`] into the overlay.
  118. /// The transactions are hashed with BLAKE3 and this hash is used as
  119. /// the key, while the value is the serialized [`Transaction`] itself.
  120. /// On success, the function returns the transaction hashes in the same
  121. /// order as the input transactions.
  122. pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
  123. let mut ret = Vec::with_capacity(transactions.len());
  124. let mut lock = self.0.lock().unwrap();
  125. for tx in transactions {
  126. let serialized = serialize(tx);
  127. let tx_hash = blake3::hash(&serialized);
  128. lock.insert(SLED_TX_TREE, tx_hash.as_bytes(), &serialized)?;
  129. ret.push(tx_hash);
  130. }
  131. Ok(ret)
  132. }
  133. /// Fetch given tx hashes from the overlay.
  134. /// The resulting vector contains `Option`, which is `Some` if the tx
  135. /// was found in the overlay, and otherwise it is `None`, if it has not.
  136. /// The second parameter is a boolean which tells the function to fail in
  137. /// case at least one block was not found.
  138. pub fn get(
  139. &self,
  140. tx_hashes: &[blake3::Hash],
  141. strict: bool,
  142. ) -> Result<Vec<Option<Transaction>>> {
  143. let mut ret = Vec::with_capacity(tx_hashes.len());
  144. let lock = self.0.lock().unwrap();
  145. for tx_hash in tx_hashes {
  146. if let Some(found) = lock.get(SLED_TX_TREE, tx_hash.as_bytes())? {
  147. let tx = deserialize(&found)?;
  148. ret.push(Some(tx));
  149. } else {
  150. if strict {
  151. let s = tx_hash.to_hex().as_str().to_string();
  152. return Err(Error::TransactionNotFound(s))
  153. }
  154. ret.push(None);
  155. }
  156. }
  157. Ok(ret)
  158. }
  159. }
  160. /// The `PendingTxStore` is a `sled` tree storing all the node pending
  161. /// transactions where the key is the transaction hash, and the value is
  162. /// the serialized transaction.
  163. #[derive(Clone)]
  164. pub struct PendingTxStore(pub sled::Tree);
  165. impl PendingTxStore {
  166. /// Opens a new or existing `PendingTxStore` on the given sled database.
  167. pub fn new(db: &sled::Db) -> Result<Self> {
  168. let tree = db.open_tree(SLED_PENDING_TX_TREE)?;
  169. Ok(Self(tree))
  170. }
  171. /// Insert a slice of [`Transaction`] into the pending tx store.
  172. pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
  173. let (batch, ret) = self.insert_batch(transactions)?;
  174. self.0.apply_batch(batch)?;
  175. Ok(ret)
  176. }
  177. /// Generate the sled batch corresponding to an insert, so caller
  178. /// can handle the write operation.
  179. /// The transactions are hashed with BLAKE3 and this hash is used as
  180. /// the key, while the value is the serialized [`Transaction`] itself.
  181. /// On success, the function returns the transaction hashes in the same
  182. /// order as the input transactions, along with the corresponding operation
  183. /// batch.
  184. pub fn insert_batch(
  185. &self,
  186. transactions: &[Transaction],
  187. ) -> Result<(sled::Batch, Vec<blake3::Hash>)> {
  188. let mut ret = Vec::with_capacity(transactions.len());
  189. let mut batch = sled::Batch::default();
  190. for tx in transactions {
  191. let serialized = serialize(tx);
  192. let tx_hash = blake3::hash(&serialized);
  193. batch.insert(tx_hash.as_bytes(), serialized);
  194. ret.push(tx_hash);
  195. }
  196. Ok((batch, ret))
  197. }
  198. /// Check if the pending tx store contains a given transaction hash.
  199. pub fn contains(&self, tx_hash: &blake3::Hash) -> Result<bool> {
  200. Ok(self.0.contains_key(tx_hash.as_bytes())?)
  201. }
  202. /// Fetch given tx hashes from the pending tx store.
  203. /// The resulting vector contains `Option`, which is `Some` if the tx
  204. /// was found in the pending tx store, and otherwise it is `None`, if it has not.
  205. /// The second parameter is a boolean which tells the function to fail in
  206. /// case at least one block was not found.
  207. pub fn get(
  208. &self,
  209. tx_hashes: &[blake3::Hash],
  210. strict: bool,
  211. ) -> Result<Vec<Option<Transaction>>> {
  212. let mut ret = Vec::with_capacity(tx_hashes.len());
  213. for tx_hash in tx_hashes {
  214. if let Some(found) = self.0.get(tx_hash.as_bytes())? {
  215. let tx = deserialize(&found)?;
  216. ret.push(Some(tx));
  217. } else {
  218. if strict {
  219. let s = tx_hash.to_hex().as_str().to_string();
  220. return Err(Error::TransactionNotFound(s))
  221. }
  222. ret.push(None);
  223. }
  224. }
  225. Ok(ret)
  226. }
  227. /// Retrieve all transactions from the pending tx store in the form of
  228. /// a HashMap with key the transaction hash and value the transaction
  229. /// itself.
  230. /// Be careful as this will try to load everything in memory.
  231. pub fn get_all(&self) -> Result<HashMap<blake3::Hash, Transaction>> {
  232. let mut txs = HashMap::new();
  233. for tx in self.0.iter() {
  234. let (key, value) = parse_record(tx.unwrap())?;
  235. txs.insert(key, value);
  236. }
  237. Ok(txs)
  238. }
  239. /// Remove a slice of [`blake3::Hash`] from the pending tx store.
  240. pub fn remove(&self, txs_hashes: &[blake3::Hash]) -> Result<()> {
  241. let batch = self.remove_batch(txs_hashes);
  242. self.0.apply_batch(batch)?;
  243. Ok(())
  244. }
  245. /// Generate the sled batch corresponding to a remove, so caller
  246. /// can handle the write operation.
  247. pub fn remove_batch(&self, txs_hashes: &[blake3::Hash]) -> sled::Batch {
  248. let mut batch = sled::Batch::default();
  249. for tx_hash in txs_hashes {
  250. batch.remove(tx_hash.as_bytes());
  251. }
  252. batch
  253. }
  254. }
  255. /// The `PendingTxOrderStore` is a `sled` tree storing the order of all
  256. /// the node pending transactions where the key is an incremental value,
  257. /// and the value is the serialized transaction.
  258. #[derive(Clone)]
  259. pub struct PendingTxOrderStore(pub sled::Tree);
  260. impl PendingTxOrderStore {
  261. /// Opens a new or existing `PendingTxOrderStore` on the given sled database.
  262. pub fn new(db: &sled::Db) -> Result<Self> {
  263. let tree = db.open_tree(SLED_PENDING_TX_ORDER_TREE)?;
  264. Ok(Self(tree))
  265. }
  266. /// Insert a slice of [`blake3::Hash`] into the pending tx order store.
  267. /// With sled, the operation is done as a batch.
  268. pub fn insert(&self, txs_hashes: &[blake3::Hash]) -> Result<()> {
  269. let batch = self.insert_batch(txs_hashes)?;
  270. self.0.apply_batch(batch)?;
  271. Ok(())
  272. }
  273. /// Generate the sled batch corresponding to an insert, so caller
  274. /// can handle the write operation.
  275. pub fn insert_batch(&self, txs_hashes: &[blake3::Hash]) -> Result<sled::Batch> {
  276. let mut batch = sled::Batch::default();
  277. let mut next_index = match self.0.last()? {
  278. Some(n) => {
  279. let prev_bytes: [u8; 8] = n.0.as_ref().try_into().unwrap();
  280. let prev = u64::from_be_bytes(prev_bytes);
  281. prev + 1
  282. }
  283. None => 0,
  284. };
  285. for txs_hash in txs_hashes {
  286. batch.insert(&next_index.to_be_bytes(), txs_hash.as_bytes());
  287. next_index += 1;
  288. }
  289. Ok(batch)
  290. }
  291. /// Retrieve all transactions from the pending tx order store in the form
  292. /// of a tuple (`u64`, `blake3::Hash`).
  293. /// Be careful as this will try to load everything in memory.
  294. pub fn get_all(&self) -> Result<Vec<(u64, blake3::Hash)>> {
  295. let mut txs = vec![];
  296. for tx in self.0.iter() {
  297. txs.push(parse_u64_key_record(tx.unwrap())?);
  298. }
  299. Ok(txs)
  300. }
  301. /// Remove a slice of [`u64`] from the pending tx order store.
  302. pub fn remove(&self, indexes: &[u64]) -> Result<()> {
  303. let batch = self.remove_batch(indexes);
  304. self.0.apply_batch(batch)?;
  305. Ok(())
  306. }
  307. /// Generate the sled batch corresponding to a remove, so caller
  308. /// can handle the write operation.
  309. pub fn remove_batch(&self, indexes: &[u64]) -> sled::Batch {
  310. let mut batch = sled::Batch::default();
  311. for index in indexes {
  312. batch.remove(&index.to_be_bytes());
  313. }
  314. batch
  315. }
  316. }