stratus/eth/primitives/
execution_account_changes.rs

1use std::collections::BTreeMap;
2
3use display_json::DebugAsJson;
4
5use crate::alias::RevmBytecode;
6use crate::eth::primitives::Account;
7use crate::eth::primitives::Address;
8use crate::eth::primitives::ExecutionValueChange;
9use crate::eth::primitives::Nonce;
10use crate::eth::primitives::Slot;
11use crate::eth::primitives::SlotIndex;
12use crate::eth::primitives::Wei;
13
14/// Changes that happened to an account during a transaction.
15#[derive(DebugAsJson, Clone, PartialEq, Eq, fake::Dummy, serde::Serialize, serde::Deserialize)]
16pub struct ExecutionAccountChanges {
17    pub nonce: ExecutionValueChange<Nonce>,
18    pub balance: ExecutionValueChange<Wei>,
19
20    // TODO: bytecode related information should be grouped in a Bytecode struct
21    #[dummy(default)]
22    pub bytecode: ExecutionValueChange<Option<RevmBytecode>>,
23    pub slots: BTreeMap<SlotIndex, ExecutionValueChange<Slot>>, // TODO: should map idx to slotvalue
24}
25
26impl ExecutionAccountChanges {
27    /// Merges another [`ExecutionAccountChanges`] into this one, replacing self values with values from other.
28    /// For slots, performs a union of the BTrees, giving preference to values from other.
29    pub fn merge(&mut self, other: ExecutionAccountChanges) {
30        self.nonce = other.nonce;
31        self.balance = other.balance;
32        self.bytecode = other.bytecode;
33
34        // Merge slots, giving preference to values from other
35        for (slot_index, slot_change) in other.slots {
36            self.slots.insert(slot_index, slot_change);
37        }
38    }
39
40    pub fn update_empty_values(&mut self, other: Account) {
41        if self.nonce.is_empty() {
42            self.nonce.set_original(other.nonce);
43        }
44        if self.balance.is_empty() {
45            self.balance.set_original(other.balance);
46        }
47        if self.bytecode.is_empty() {
48            self.bytecode.set_original(other.bytecode);
49        }
50    }
51
52    /// Creates a new [`ExecutionAccountChanges`] from Account original values.
53    pub fn from_original_values(account: impl Into<Account>) -> Self {
54        let account: Account = account.into();
55        Self {
56            nonce: ExecutionValueChange::from_original(account.nonce),
57            balance: ExecutionValueChange::from_original(account.balance),
58            bytecode: ExecutionValueChange::from_original(account.bytecode),
59            slots: BTreeMap::new(),
60        }
61    }
62
63    /// Creates a new [`ExecutionAccountChanges`] from Account modified values.
64    pub fn from_modified_values(account: impl Into<Account>, modified_slots: Vec<Slot>) -> Self {
65        let account: Account = account.into();
66        let mut changes = Self {
67            nonce: ExecutionValueChange::from_modified(account.nonce),
68            balance: ExecutionValueChange::from_modified(account.balance),
69
70            // bytecode
71            bytecode: ExecutionValueChange::from_modified(account.bytecode),
72            slots: BTreeMap::new(),
73        };
74
75        for slot in modified_slots {
76            changes.slots.insert(slot.index, ExecutionValueChange::from_modified(slot));
77        }
78
79        changes
80    }
81
82    /// Updates an existing account state with changes that happened during the transaction.
83    pub fn apply_modifications(&mut self, modified_account: Account, modified_slots: Vec<Slot>) {
84        // update nonce if modified
85        let is_nonce_modified = match self.nonce.take_original_ref() {
86            Some(original_nonce) => *original_nonce != modified_account.nonce,
87            None => true,
88        };
89        if is_nonce_modified {
90            self.nonce.set_modified(modified_account.nonce);
91        }
92
93        // update balance if modified
94        let is_balance_modified = match self.balance.take_original_ref() {
95            Some(original_balance) => *original_balance != modified_account.balance,
96            None => true,
97        };
98        if is_balance_modified {
99            self.balance.set_modified(modified_account.balance);
100        }
101
102        // update all slots because all of them are modified
103        for slot in modified_slots {
104            match self.slots.get_mut(&slot.index) {
105                Some(ref mut entry) => {
106                    entry.set_modified(slot);
107                }
108                None => {
109                    self.slots.insert(slot.index, ExecutionValueChange::from_modified(slot));
110                }
111            };
112        }
113    }
114
115    /// Checks if account nonce, balance or bytecode were modified.
116    pub fn is_account_modified(&self) -> bool {
117        self.nonce.is_modified() || self.balance.is_modified() || self.bytecode.is_modified()
118    }
119
120    pub fn to_account(self, address: Address) -> Account {
121        Account {
122            address,
123            nonce: self.nonce.take().unwrap_or_default(),
124            balance: self.balance.take().unwrap_or_default(),
125            bytecode: self.bytecode.take().unwrap_or_default(),
126        }
127    }
128}