multiversx_chain_vm/blockchain/state/
blockchain_state.rs1use 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 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}