multiversx_chain_vm/host/context/
tx_cache.rs1use 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 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}