Skip to main content

multiversx_chain_vm/host/context/
tx_cache.rs

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