multiversx_chain_vm/blockchain/state/
blockchain_state.rs

1use multiversx_chain_core::types::CodeMetadata;
2use num_bigint::BigUint;
3use num_traits::Zero;
4use std::{
5    collections::HashMap,
6    fmt::Debug,
7    ops::{Deref, DerefMut},
8    sync::Arc,
9};
10
11use crate::{
12    blockchain::reserved::STORAGE_REWARD_KEY, host::context::BlockchainUpdate,
13    system_sc::ESDT_SYSTEM_SC_ADDRESS_ARRAY, types::VMAddress,
14};
15
16use super::{AccountData, BlockConfig};
17
18#[derive(Clone)]
19pub struct BlockchainState {
20    pub accounts: HashMap<VMAddress, AccountData>,
21    pub new_addresses: HashMap<(VMAddress, u64), VMAddress>,
22    pub block_config: BlockConfig,
23    pub new_token_identifiers: Vec<String>,
24}
25
26impl Default for BlockchainState {
27    fn default() -> Self {
28        let mut state = Self {
29            accounts: Default::default(),
30            new_addresses: Default::default(),
31            block_config: Default::default(),
32            new_token_identifiers: Default::default(),
33        };
34
35        // pre-populating system SC(s)
36        state.add_empty_account(ESDT_SYSTEM_SC_ADDRESS_ARRAY.into());
37
38        state
39    }
40}
41
42impl BlockchainState {
43    pub fn commit_updates(&mut self, updates: BlockchainUpdate) {
44        updates.apply(self);
45    }
46
47    pub fn account_exists(&self, address: &VMAddress) -> bool {
48        self.accounts.contains_key(address)
49    }
50
51    pub fn increase_account_nonce(&mut self, address: &VMAddress) {
52        let account = self
53            .accounts
54            .get_mut(address)
55            .unwrap_or_else(|| panic!("Account not found: {address}"));
56        account.nonce += 1;
57    }
58
59    pub fn subtract_tx_gas(&mut self, address: &VMAddress, gas_limit: u64, gas_price: u64) {
60        let account = self
61            .accounts
62            .get_mut(address)
63            .unwrap_or_else(|| panic!("Account not found: {address}"));
64        let gas_cost = BigUint::from(gas_limit) * BigUint::from(gas_price);
65        assert!(
66            account.egld_balance >= gas_cost,
67            "Not enough balance to pay gas upfront"
68        );
69        account.egld_balance -= &gas_cost;
70    }
71
72    pub fn increase_validator_reward(&mut self, address: &VMAddress, amount: &BigUint) {
73        let account = self
74            .accounts
75            .get_mut(address)
76            .unwrap_or_else(|| panic!("Account not found: {address}"));
77        account.egld_balance += amount;
78        let mut storage_v_rew =
79            if let Some(old_storage_value) = account.storage.get(STORAGE_REWARD_KEY) {
80                BigUint::from_bytes_be(old_storage_value)
81            } else {
82                BigUint::zero()
83            };
84        storage_v_rew += amount;
85        account
86            .storage
87            .insert(STORAGE_REWARD_KEY.to_vec(), storage_v_rew.to_bytes_be());
88    }
89
90    pub fn put_new_token_identifier(&mut self, token_identifier: String) {
91        self.new_token_identifiers.push(token_identifier)
92    }
93
94    pub fn get_new_token_identifiers(&self) -> Vec<String> {
95        self.new_token_identifiers.clone()
96    }
97
98    pub fn update_new_token_identifiers(&mut self, token_identifiers: Vec<String>) {
99        self.new_token_identifiers = token_identifiers;
100    }
101
102    fn add_empty_account(&mut self, address: VMAddress) {
103        self.accounts.insert(
104            address.clone(),
105            AccountData {
106                address,
107                nonce: 0,
108                egld_balance: Default::default(),
109                esdt: Default::default(),
110                username: Vec::new(),
111                storage: HashMap::new(),
112                contract_path: None,
113                code_metadata: CodeMetadata::empty(),
114                contract_owner: None,
115                developer_rewards: BigUint::zero(),
116            },
117        );
118    }
119}
120
121impl Debug for BlockchainState {
122    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123        f.debug_struct("BlockchainState")
124            .field("accounts", &self.accounts)
125            .field("new_addresses", &self.new_addresses)
126            .field("block_config", &self.block_config)
127            .finish()
128    }
129}
130
131#[derive(Default, Clone)]
132pub struct BlockchainStateRef(Arc<BlockchainState>);
133
134impl BlockchainStateRef {
135    pub fn mut_state(&mut self) -> &mut BlockchainState {
136        Arc::get_mut(&mut self.0).expect("cannot change state, since object is currently shared")
137    }
138
139    pub fn get_arc(&self) -> Arc<BlockchainState> {
140        self.0.clone()
141    }
142}
143
144impl Deref for BlockchainStateRef {
145    type Target = BlockchainState;
146
147    fn deref(&self) -> &Self::Target {
148        self.0.deref()
149    }
150}
151
152impl DerefMut for BlockchainStateRef {
153    fn deref_mut(&mut self) -> &mut Self::Target {
154        Arc::get_mut(&mut self.0).expect("cannot change state, since object is currently shared")
155    }
156}