multiversx_chain_vm/host/context/
tx_cache.rs

1use std::{
2    collections::HashMap,
3    fmt,
4    sync::{Arc, Mutex},
5};
6
7use colored::Colorize;
8
9use crate::{
10    blockchain::state::{AccountData, BlockchainState},
11    display_util::address_hex,
12    types::VMAddress,
13};
14
15use super::{BlockchainUpdate, TxCacheSource};
16
17pub struct TxCache {
18    source_ref: Arc<dyn TxCacheSource>,
19    pub(super) accounts: Mutex<HashMap<VMAddress, AccountData>>,
20    pub(super) new_token_identifiers: Mutex<Option<Vec<String>>>,
21}
22
23impl fmt::Debug for TxCache {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        f.debug_struct("TxCache")
26            .field("accounts", &self.accounts)
27            .finish()
28    }
29}
30
31impl TxCache {
32    pub fn new(source_ref: Arc<dyn TxCacheSource>) -> Self {
33        TxCache {
34            source_ref,
35            accounts: Mutex::new(HashMap::new()),
36            new_token_identifiers: Mutex::new(None),
37        }
38    }
39
40    pub fn blockchain_ref(&self) -> &BlockchainState {
41        self.source_ref.blockchain_ref()
42    }
43
44    fn load_account_if_necessary(&self, address: &VMAddress) {
45        let mut accounts_mut = self.accounts.lock().unwrap();
46        if !accounts_mut.contains_key(address) {
47            if let Some(blockchain_account) = self.source_ref.load_account(address) {
48                accounts_mut.insert(address.clone(), blockchain_account);
49            }
50        }
51    }
52
53    pub fn with_account<R, F>(&self, address: &VMAddress, f: F) -> R
54    where
55        F: FnOnce(&AccountData) -> R,
56    {
57        self.with_account_or_else(address, f, || {
58            panic!("Account {} not found", address_hex(address))
59        })
60    }
61
62    pub fn with_account_or_else<R, F, Else>(&self, address: &VMAddress, f: F, or_else: Else) -> R
63    where
64        F: FnOnce(&AccountData) -> R,
65        Else: FnOnce() -> R,
66    {
67        self.load_account_if_necessary(address);
68        let accounts = self.accounts.lock().unwrap();
69        if let Some(account) = accounts.get(address) {
70            f(account)
71        } else {
72            or_else()
73        }
74    }
75
76    pub fn with_account_mut<R, F>(&self, address: &VMAddress, f: F) -> R
77    where
78        F: FnOnce(&mut AccountData) -> R,
79    {
80        self.load_account_if_necessary(address);
81        let mut accounts = self.accounts.lock().unwrap();
82        let account = accounts
83            .get_mut(address)
84            .unwrap_or_else(|| panic!("Account {} not found", address_hex(address)));
85        f(account)
86    }
87
88    pub fn insert_account(&self, account_data: AccountData) {
89        self.accounts
90            .lock()
91            .unwrap()
92            .insert(account_data.address.clone(), account_data);
93    }
94
95    pub fn increase_account_nonce(&self, address: &VMAddress) {
96        self.with_account_mut(address, |account| {
97            account.nonce += 1;
98        });
99    }
100
101    /// Assumes the nonce has already been increased.
102    pub fn get_new_address(&self, creator_address: &VMAddress) -> VMAddress {
103        let current_nonce = self.with_account(creator_address, |account| account.nonce);
104        self.blockchain_ref()
105            .get_new_address(creator_address.clone(), current_nonce - 1)
106            .unwrap_or_else(|| {
107                let new_mock_address =
108                    VMAddress::generate_mock_address(&creator_address.to_vec(), current_nonce - 1);
109                println!(
110                    "{}",
111                    format!(
112                        "Missing new address for {:?}.\nCreating a new mock address...: {:?}",
113                        address_hex(creator_address),
114                        address_hex(&new_mock_address)
115                    )
116                    .yellow()
117                );
118                new_mock_address
119            })
120    }
121
122    pub fn get_new_token_identifiers(&self) -> Vec<String> {
123        self.blockchain_ref().get_new_token_identifiers()
124    }
125
126    pub fn set_new_token_identifiers(&self, token_identifiers: Vec<String>) {
127        *self.new_token_identifiers.lock().unwrap() = Some(token_identifiers);
128    }
129
130    pub fn into_blockchain_updates(self) -> BlockchainUpdate {
131        BlockchainUpdate {
132            accounts: self.accounts.into_inner().unwrap(),
133            new_token_identifiers: self.new_token_identifiers.into_inner().unwrap(),
134        }
135    }
136
137    pub fn commit_updates(&self, updates: BlockchainUpdate) {
138        self.accounts.lock().unwrap().extend(updates.accounts);
139    }
140}