txstore.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. use crate::{
  2. serial::{deserialize, serialize},
  3. tx::Transaction,
  4. Error, Result,
  5. };
  6. const SLED_TX_TREE: &[u8] = b"_transactions";
  7. /// The `TxStore` is a `sled` tree storing all the blockchain's
  8. /// transactions where the key is the transaction hash, and the value is
  9. /// the serialized transaction.
  10. #[derive(Clone)]
  11. pub struct TxStore(sled::Tree);
  12. impl TxStore {
  13. /// Opens a new or existing `TxStore` on the given sled database.
  14. pub fn new(db: &sled::Db) -> Result<Self> {
  15. let tree = db.open_tree(SLED_TX_TREE)?;
  16. Ok(Self(tree))
  17. }
  18. /// Insert a slice of [`Transaction`] into the txstore. With sled, the
  19. /// operation is done as a batch.
  20. /// The transactions are hashed with BLAKE3 and this hash is used as
  21. /// the key, while the value is the serialized [`Transaction`] itself.
  22. /// On success, the function returns the transaction hashes in the same
  23. /// order as the input transactions.
  24. pub fn insert(&self, transactions: &[Transaction]) -> Result<Vec<blake3::Hash>> {
  25. let mut ret = Vec::with_capacity(transactions.len());
  26. let mut batch = sled::Batch::default();
  27. for tx in transactions {
  28. let serialized = serialize(tx);
  29. let txhash = blake3::hash(&serialized);
  30. batch.insert(txhash.as_bytes(), serialized);
  31. ret.push(txhash);
  32. }
  33. self.0.apply_batch(batch)?;
  34. Ok(ret)
  35. }
  36. /// Check if the txstore contains a given transaction hash.
  37. pub fn contains(&self, txid: &blake3::Hash) -> Result<bool> {
  38. Ok(self.0.contains_key(txid.as_bytes())?)
  39. }
  40. /// Fetch given tx hashes from the txstore.
  41. /// The resulting vector contains `Option`, which is `Some` if the tx
  42. /// was found in the txstore, and otherwise it is `None`, if it has not.
  43. /// The second parameter is a boolean which tells the function to fail in
  44. /// case at least one block was not found.
  45. pub fn get(&self, txids: &[blake3::Hash], strict: bool) -> Result<Vec<Option<Transaction>>> {
  46. let mut ret = Vec::with_capacity(txids.len());
  47. for txid in txids {
  48. if let Some(found) = self.0.get(txid.as_bytes())? {
  49. let tx = deserialize(&found)?;
  50. ret.push(Some(tx));
  51. } else {
  52. if strict {
  53. let s = txid.to_hex().as_str().to_string();
  54. return Err(Error::TransactionNotFound(s))
  55. }
  56. ret.push(None);
  57. }
  58. }
  59. Ok(ret)
  60. }
  61. /// Retrieve all transactions from the txstore in the form of a tuple
  62. /// (`tx_hash`, `tx`).
  63. /// Be careful as this will try to load everything in memory.
  64. pub fn get_all(&self) -> Result<Vec<(blake3::Hash, Transaction)>> {
  65. let mut txs = vec![];
  66. for tx in self.0.iter() {
  67. let (key, value) = tx.unwrap();
  68. let hash_bytes: [u8; 32] = key.as_ref().try_into().unwrap();
  69. let tx = deserialize(&value)?;
  70. txs.push((hash_bytes.into(), tx));
  71. }
  72. Ok(txs)
  73. }
  74. }