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