lake_parent_transaction_cache/
lib.rs

1#![doc = include_str!("../README.md")]
2#[macro_use]
3extern crate derive_builder;
4
5use cached::{Cached, SizedCache};
6use near_lake_framework::{
7    near_indexer_primitives::{near_primitives::types::AccountId, CryptoHash},
8    near_lake_primitives::{actions::ActionMetaDataExt, block::Block},
9    LakeContextExt,
10};
11
12pub type ReceiptId = CryptoHash;
13pub type TransactionHash = CryptoHash;
14type Cache = SizedCache<ReceiptId, TransactionHash>;
15
16#[derive(Debug, Builder)]
17#[builder(pattern = "owned")]
18pub struct ParentTransactionCache {
19    #[builder(
20        setter(custom = true, name = "cache_size"),
21        default = "std::sync::RwLock::new(Cache::with_size(100_000))"
22    )]
23    cache: std::sync::RwLock<Cache>,
24    #[builder(setter(custom = true, name = "for_accounts"))]
25    account_ids: Vec<AccountId>,
26}
27
28impl ParentTransactionCacheBuilder {
29    /// Sets the size of the cache. Default is 100_000.
30    pub fn cache_size(mut self, value: usize) -> Self {
31        self.cache = Some(std::sync::RwLock::new(Cache::with_size(value)));
32        self
33    }
34
35    /// Stores the Vec of [AccountId](near_lake_framework::near_indexer_primitives::near_primitives::types::AccountId) to cache transactions for.
36    /// If not set, the cache will be created for all the Transactions in the block.
37    /// If set the cache will be created only for the transactions that have the
38    /// sender or receiver in the list of accounts.
39    /// **Warning**: This method overrides the previous value.
40    pub fn for_accounts(mut self, accounts_id: Vec<AccountId>) -> Self {
41        self.account_ids = Some(accounts_id);
42        self
43    }
44
45    /// Adds an account to the watching list for the parent transaction cache.
46    /// Similarly to the method [for_accounts](#method.for_accounts) this method will
47    /// create the cache only for the transactions that have the sender or receiver
48    /// in the list of accounts.
49    /// **Warning**: This method appends to the previous value.
50    pub fn for_account(mut self, account_id: AccountId) -> Self {
51        if let Some(mut accounts_id) = self.account_ids.take() {
52            accounts_id.push(account_id);
53            self.account_ids = Some(accounts_id);
54        } else {
55            self.account_ids = Some(vec![account_id]);
56        }
57        self
58    }
59}
60
61impl LakeContextExt for ParentTransactionCache {
62    /// The process to scan the [near_lake_primitives::Block](near_lake_framework::near_lake_primitives::block::Block) and update the cache
63    /// with the new transactions and first expected receipts.
64    /// The cache is used to find the parent transaction hash for a given receipt id.
65    fn execute_before_run(&self, block: &mut Block) {
66        // Fill up the cache with new transactions and first expected receipts
67        // We will try to skip the transactions related to the accounts we're not watching for.
68        // Based on `accounts_id`
69        for tx in block.transactions().filter(move |tx| {
70            self.account_ids.is_empty()
71                || self.account_ids.contains(tx.signer_id())
72                || self.account_ids.contains(tx.receiver_id())
73        }) {
74            let tx_hash = tx.transaction_hash();
75            tx.actions_included()
76                .map(|action| action.metadata().receipt_id())
77                .for_each(|receipt_id| {
78                    let mut cache = self.cache.write().unwrap();
79                    cache.cache_set(receipt_id, tx_hash);
80                });
81        }
82        for receipt in block.receipts() {
83            let receipt_id = receipt.receipt_id();
84            let mut cache = self.cache.write().unwrap();
85            let parent_tx_hash = cache.cache_remove(&receipt_id);
86
87            if let Some(parent_tx_hash) = parent_tx_hash {
88                cache.cache_set(receipt_id, parent_tx_hash);
89            }
90        }
91    }
92
93    /// We don't need to do anything after the run.
94    fn execute_after_run(&self) {}
95}
96
97impl ParentTransactionCache {
98    /// Returns the parent transaction hash for a given receipt id.
99    /// If the receipt id is not found in the cache, it returns None.
100    /// If the receipt id is found in the cache, it returns the parent transaction hash.
101    pub fn get_parent_transaction_hash(&self, receipt_id: &ReceiptId) -> Option<TransactionHash> {
102        // **Note**: [cached::SizedCache] updates metadata on every cache access. That's why
103        // we need to use a write lock here.
104        let mut cache = self.cache.write().unwrap();
105        cache.cache_get(receipt_id).cloned()
106    }
107}