cfx_executor/
substate.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use cfx_types::{Address, AddressWithSpace};
6
7use primitives::{
8    receipt::{SortedStorageChanges, StorageChange},
9    LogEntry,
10};
11use std::collections::{HashMap, HashSet};
12
13/// Tracks execution changes for the post-execution process in the executive,
14/// such as charging collateral, generating receipt, and recycling killed
15/// contracts.
16#[derive(Debug, Default)]
17pub struct Substate {
18    /// Any accounts that have suicided.
19    pub suicides: HashSet<AddressWithSpace>,
20    /// Any accounts that occupy some storage.
21    pub storage_collateralized: HashMap<Address, u64>,
22    /// Any accounts that release some storage.
23    pub storage_released: HashMap<Address, u64>,
24    /// Any logs.
25    pub logs: Vec<LogEntry>,
26    /// gas to be refunded
27    pub refund_gas: i128,
28    /// Created contracts.
29    contracts_created: Vec<AddressWithSpace>,
30}
31
32impl Substate {
33    pub fn accrue(&mut self, s: Self) {
34        self.suicides.extend(s.suicides);
35        self.logs.extend(s.logs);
36        self.contracts_created.extend(s.contracts_created);
37        for (address, amount) in s.storage_collateralized {
38            *self.storage_collateralized.entry(address).or_insert(0) += amount;
39        }
40        for (address, amount) in s.storage_released {
41            *self.storage_released.entry(address).or_insert(0) += amount;
42        }
43        self.refund_gas += s.refund_gas;
44    }
45
46    pub fn new() -> Self { Substate::default() }
47}
48
49impl Substate {
50    pub fn get_collateral_change(&self, address: &Address) -> (u64, u64) {
51        let inc = self
52            .storage_collateralized
53            .get(address)
54            .cloned()
55            .unwrap_or(0);
56        let sub = self.storage_released.get(address).cloned().unwrap_or(0);
57        if inc > sub {
58            (inc - sub, 0)
59        } else {
60            (0, sub - inc)
61        }
62    }
63
64    pub fn compute_storage_changes(&self) -> SortedStorageChanges {
65        let mut storage_collateralized = vec![];
66        let mut storage_released = vec![];
67
68        let mut affected_address: Vec<_> =
69            self.keys_for_collateral_changed().iter().cloned().collect();
70        affected_address.sort();
71        for address in affected_address {
72            let (inc, sub) = self.get_collateral_change(&address);
73            if inc > 0 {
74                storage_collateralized.push(StorageChange {
75                    address: *address,
76                    collaterals: inc.into(),
77                });
78            } else if sub > 0 {
79                storage_released.push(StorageChange {
80                    address: *address,
81                    collaterals: sub.into(),
82                });
83            }
84        }
85        SortedStorageChanges {
86            storage_collateralized,
87            storage_released,
88        }
89    }
90
91    pub fn contracts_created(&self) -> &Vec<AddressWithSpace> {
92        &self.contracts_created
93    }
94
95    pub fn record_contract_create(&mut self, address: AddressWithSpace) {
96        self.contracts_created.push(address);
97    }
98
99    pub fn record_storage_occupy(
100        &mut self, address: &Address, collaterals: u64,
101    ) {
102        *self.storage_collateralized.entry(*address).or_insert(0) +=
103            collaterals;
104    }
105
106    pub fn record_storage_release(
107        &mut self, address: &Address, collaterals: u64,
108    ) {
109        *self.storage_released.entry(*address).or_insert(0) += collaterals;
110    }
111
112    pub fn keys_for_collateral_changed(&self) -> HashSet<&Address> {
113        let affected_address1: HashSet<_> =
114            self.storage_collateralized.keys().collect();
115        let affected_address2: HashSet<_> =
116            self.storage_released.keys().collect();
117        affected_address1
118            .union(&affected_address2)
119            .cloned()
120            .collect()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::Substate;
127    use cfx_types::{Address, AddressSpaceUtil, Space};
128    use primitives::LogEntry;
129
130    #[test]
131    fn created() {
132        let sub_state = Substate::new();
133        assert_eq!(sub_state.suicides.len(), 0);
134    }
135
136    #[test]
137    fn accrue() {
138        let mut sub_state = Substate::new();
139        sub_state
140            .contracts_created
141            .push(Address::from_low_u64_be(1).with_native_space());
142        sub_state.logs.push(LogEntry {
143            address: Address::from_low_u64_be(1),
144            topics: vec![],
145            data: vec![],
146            space: Space::Native,
147        });
148        sub_state
149            .suicides
150            .insert(Address::from_low_u64_be(10).with_native_space());
151
152        let mut sub_state_2 = Substate::new();
153        sub_state_2
154            .contracts_created
155            .push(Address::from_low_u64_be(2).with_native_space());
156        sub_state_2.logs.push(LogEntry {
157            address: Address::from_low_u64_be(1),
158            topics: vec![],
159            data: vec![],
160            space: Space::Native,
161        });
162
163        sub_state.accrue(sub_state_2);
164        assert_eq!(sub_state.contracts_created.len(), 2);
165        assert_eq!(sub_state.suicides.len(), 1);
166    }
167}