multiversx_chain_vm/host/context/
tx_context.rs

1use crate::{
2    blockchain::{
3        state::{AccountData, AccountEsdt, BlockchainState},
4        VMConfigRef,
5    },
6    host::runtime::RuntimeRef,
7    types::{VMAddress, VMCodeMetadata},
8};
9use num_bigint::BigUint;
10use num_traits::Zero;
11use std::{
12    collections::HashMap,
13    sync::{Arc, Mutex, MutexGuard},
14};
15
16use super::{
17    BackTransfers, BlockchainRng, BlockchainUpdate, FailingExecutor, ManagedTypeContainer, TxCache,
18    TxInput, TxResult,
19};
20
21pub struct TxContext {
22    pub runtime_ref: RuntimeRef,
23    pub tx_input_box: Box<TxInput>,
24    pub tx_cache: Arc<TxCache>,
25    pub managed_types: Mutex<ManagedTypeContainer>,
26    pub back_transfers: Mutex<BackTransfers>,
27    pub tx_result_cell: Mutex<TxResult>,
28    pub b_rng: Mutex<BlockchainRng>,
29}
30
31impl TxContext {
32    pub fn new(runtime_ref: RuntimeRef, tx_input: TxInput, tx_cache: TxCache) -> Self {
33        let b_rng = Mutex::new(BlockchainRng::new(&tx_input, &tx_cache));
34        TxContext {
35            runtime_ref,
36            tx_input_box: Box::new(tx_input),
37            tx_cache: Arc::new(tx_cache),
38            managed_types: Mutex::new(ManagedTypeContainer::new()),
39            back_transfers: Mutex::default(),
40            tx_result_cell: Mutex::new(TxResult::empty()),
41            b_rng,
42        }
43    }
44
45    pub fn dummy() -> Self {
46        let tx_cache = TxCache::new(Arc::new(BlockchainState::default()));
47        let contract_address = VMAddress::from([b'c'; 32]);
48        tx_cache.insert_account(AccountData {
49            address: contract_address.clone(),
50            nonce: 0,
51            egld_balance: BigUint::zero(),
52            storage: HashMap::new(),
53            esdt: AccountEsdt::default(),
54            username: Vec::new(),
55            contract_path: None,
56            code_metadata: VMCodeMetadata::empty(),
57            contract_owner: None,
58            developer_rewards: BigUint::zero(),
59        });
60
61        let tx_input = TxInput {
62            from: contract_address.clone(),
63            to: contract_address,
64            tx_hash: b"dummy...........................".into(),
65            ..Default::default()
66        };
67
68        let b_rng = Mutex::new(BlockchainRng::new(&tx_input, &tx_cache));
69        let vm_ref = VMConfigRef::new();
70        TxContext {
71            runtime_ref: RuntimeRef::new(vm_ref, Box::new(FailingExecutor)),
72            tx_input_box: Box::new(tx_input),
73            tx_cache: Arc::new(tx_cache),
74            managed_types: Mutex::new(ManagedTypeContainer::new()),
75            back_transfers: Mutex::default(),
76            tx_result_cell: Mutex::new(TxResult::empty()),
77            b_rng,
78        }
79    }
80
81    pub fn input_ref(&self) -> &TxInput {
82        self.tx_input_box.as_ref()
83    }
84
85    pub fn blockchain_cache(&self) -> &TxCache {
86        &self.tx_cache
87    }
88
89    pub fn blockchain_cache_arc(&self) -> Arc<TxCache> {
90        self.tx_cache.clone()
91    }
92
93    pub fn blockchain_ref(&self) -> &BlockchainState {
94        self.tx_cache.blockchain_ref()
95    }
96
97    pub fn with_account<R, F>(&self, address: &VMAddress, f: F) -> R
98    where
99        F: FnOnce(&AccountData) -> R,
100    {
101        self.tx_cache.with_account(address, f)
102    }
103
104    pub fn with_account_or_else<R, F, Else>(&self, address: &VMAddress, f: F, or_else: Else) -> R
105    where
106        F: FnOnce(&AccountData) -> R,
107        Else: FnOnce() -> R,
108    {
109        self.tx_cache.with_account_or_else(address, f, or_else)
110    }
111
112    pub fn with_contract_account<R, F>(&self, f: F) -> R
113    where
114        F: FnOnce(&AccountData) -> R,
115    {
116        self.with_account(&self.tx_input_box.to, f)
117    }
118
119    pub fn with_account_mut<R, F>(&self, address: &VMAddress, f: F) -> R
120    where
121        F: FnOnce(&mut AccountData) -> R,
122    {
123        self.tx_cache.with_account_mut(address, f)
124    }
125
126    pub fn with_contract_account_mut<R, F>(&self, f: F) -> R
127    where
128        F: FnOnce(&mut AccountData) -> R,
129    {
130        self.with_account_mut(&self.tx_input_box.to, f)
131    }
132
133    pub fn m_types_lock(&self) -> MutexGuard<'_, ManagedTypeContainer> {
134        self.managed_types.lock().unwrap()
135    }
136
137    pub fn back_transfers_lock(&self) -> MutexGuard<'_, BackTransfers> {
138        self.back_transfers.lock().unwrap()
139    }
140
141    pub fn result_lock(&self) -> MutexGuard<'_, TxResult> {
142        self.tx_result_cell.lock().unwrap()
143    }
144
145    pub fn extract_result(&self) -> TxResult {
146        std::mem::replace(&mut *self.tx_result_cell.lock().unwrap(), TxResult::empty())
147    }
148
149    pub fn rng_lock(&self) -> MutexGuard<'_, BlockchainRng> {
150        self.b_rng.lock().unwrap()
151    }
152
153    pub fn create_new_contract(
154        &self,
155        new_address: &VMAddress,
156        contract_path: Vec<u8>,
157        code_metadata: VMCodeMetadata,
158        contract_owner: VMAddress,
159    ) {
160        assert!(
161            !self.tx_cache.blockchain_ref().account_exists(new_address),
162            "Account already exists at deploy address."
163        );
164
165        self.tx_cache.insert_account(AccountData {
166            address: new_address.clone(),
167            nonce: 0,
168            egld_balance: BigUint::zero(),
169            storage: HashMap::new(),
170            esdt: AccountEsdt::default(),
171            username: Vec::new(),
172            contract_path: Some(contract_path),
173            code_metadata,
174            contract_owner: Some(contract_owner),
175            developer_rewards: BigUint::zero(),
176        });
177    }
178
179    pub fn into_blockchain_updates(self) -> BlockchainUpdate {
180        let tx_cache = Arc::try_unwrap(self.tx_cache).unwrap();
181        tx_cache.into_blockchain_updates()
182    }
183
184    pub fn into_results(self) -> (TxResult, BlockchainUpdate) {
185        let tx_cache = Arc::try_unwrap(self.tx_cache).unwrap();
186        let tx_result = Mutex::into_inner(self.tx_result_cell).unwrap();
187        let blockchain_updates = tx_cache.into_blockchain_updates();
188        (tx_result, blockchain_updates)
189    }
190}
191
192impl std::fmt::Debug for TxContext {
193    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194        f.debug_struct("TxContext")
195            .field("tx_input_box", &self.tx_input_box)
196            .field("tx_cache", &self.tx_cache)
197            .field("managed_types", &self.managed_types)
198            .field("tx_result_cell", &self.tx_result_cell)
199            .field("b_rng", &self.b_rng)
200            .finish()
201    }
202}