cfx_executor/state/state_object/
collateral.rs1use super::{State, Substate};
2use crate::{
3 executive_observer::TracerTrait, internal_contract::storage_point_prop,
4 return_if, try_loaded,
5};
6use cfx_parameters::{
7 consensus_internal::CIP107_STORAGE_POINT_PROP_INIT,
8 staking::DRIPS_PER_STORAGE_COLLATERAL_UNIT,
9};
10use cfx_statedb::{global_params::*, Result as DbResult};
11use cfx_types::{address_util::AddressUtil, Address, AddressSpaceUtil, U256};
12use cfx_vm_types::{self as vm, Spec};
13
14impl State {
15 pub fn collateral_for_storage(&self, address: &Address) -> DbResult<U256> {
16 let acc = try_loaded!(self.read_native_account_lock(address));
17 Ok(acc.collateral_for_storage())
18 }
19
20 pub fn token_collateral_for_storage(
21 &self, address: &Address,
22 ) -> DbResult<U256> {
23 let acc = try_loaded!(self.read_native_account_lock(address));
24 Ok(acc.token_collateral_for_storage())
25 }
26
27 pub fn available_storage_points_for_collateral(
28 &self, address: &Address,
29 ) -> DbResult<U256> {
30 let acc = try_loaded!(self.read_native_account_lock(address));
31 Ok(acc
32 .sponsor_info()
33 .storage_points
34 .as_ref()
35 .map(|points| points.unused)
36 .unwrap_or_default())
37 }
38
39 pub fn add_collateral_for_storage(
42 &mut self, address: &Address, by: &U256,
43 ) -> DbResult<U256> {
44 return_if!(by.is_zero());
45
46 let storage_points_used = self
47 .write_native_account_lock(&address)?
48 .add_collateral_for_storage(by);
49 *self.global_stat.val::<TotalStorage>() += *by - storage_points_used;
50 *self.global_stat.val::<UsedStoragePoints>() += storage_points_used;
51 Ok(storage_points_used)
52 }
53
54 pub fn sub_collateral_for_storage(
55 &mut self, address: &Address, by: &U256,
56 ) -> DbResult<U256> {
57 return_if!(by.is_zero());
58
59 let collateral = self.token_collateral_for_storage(address)?;
60 let refundable = if by > &collateral { &collateral } else { by };
61 let burnt = *by - *refundable;
62 let storage_points_refund = if !refundable.is_zero() {
63 self.write_account_or_new_lock(&address.with_native_space())?
64 .sub_collateral_for_storage(refundable)
65 } else {
66 U256::zero()
67 };
68
69 *self.global_stat.val::<TotalStorage>() -= *by - storage_points_refund;
70 *self.global_stat.val::<UsedStoragePoints>() -= storage_points_refund;
71 self.sub_total_issued(burnt);
72
73 Ok(storage_points_refund)
74 }
75
76 pub fn check_storage_limit(
77 &self, original_sender: &Address, storage_limit: &U256, dry_run: bool,
78 ) -> DbResult<CollateralCheckResult> {
79 let collateral_for_storage =
80 self.collateral_for_storage(original_sender)?;
81 Ok(if collateral_for_storage > *storage_limit && !dry_run {
82 Err(CollateralCheckError::ExceedStorageLimit {
83 limit: *storage_limit,
84 required: collateral_for_storage,
85 })
86 } else {
87 Ok(())
88 })
89 }
90
91 pub fn storage_point_prop(&self) -> DbResult<U256> {
92 self.get_system_storage(&storage_point_prop())
93 }
94
95 fn initialize_cip107(
96 &mut self, address: &Address,
97 ) -> DbResult<(U256, U256)> {
98 debug!("Check initialize CIP-107");
99
100 let prop: U256 = self.storage_point_prop()?;
101 let mut account =
102 self.write_account_or_new_lock(&address.with_native_space())?;
103 return_if!(!account.is_contract());
104 return_if!(account.is_cip_107_initialized());
105
106 let (from_balance, from_collateral) = account.initialize_cip107(prop);
107 std::mem::drop(account);
108
109 self.add_converted_storage_point(from_balance, from_collateral);
110 Ok((from_balance, from_collateral))
111 }
112}
113
114impl State {
115 #[cfg(test)]
118 pub fn settle_collateral_and_check(
119 &mut self, storage_owner: &Address, storage_limit: &U256,
120 substate: &mut Substate, tracer: &mut dyn TracerTrait, spec: &Spec,
121 dry_run: bool,
122 ) -> DbResult<CollateralCheckResult> {
123 let res =
124 settle_collateral_for_all(self, substate, tracer, spec, dry_run)?;
125 Ok(if res.is_ok() {
126 self.check_storage_limit(storage_owner, storage_limit, dry_run)?
127 } else {
128 res
129 })
130 }
131
132 #[cfg(test)]
133 pub fn settle_collateral_and_assert(
134 &mut self, storage_owner: &Address, substate: &mut Substate,
135 should_success: bool,
136 ) -> DbResult<()> {
137 let res = self.settle_collateral_and_check(
138 storage_owner,
139 &U256::MAX,
140 substate,
141 &mut (),
142 &Spec::new_spec_for_test(),
143 false,
144 )?;
145
146 if should_success {
147 res.unwrap();
148 } else {
149 res.unwrap_err();
150 }
151
152 Ok(())
153 }
154}
155
156fn settle_collateral_for_address(
158 state: &mut State, addr: &Address, substate: &Substate,
159 tracer: &mut dyn TracerTrait, spec: &Spec, dry_run: bool,
160) -> DbResult<CollateralCheckResult> {
161 let addr_with_space = addr.with_native_space();
162 let (inc_collaterals, sub_collaterals) =
163 substate.get_collateral_change(addr);
164 let (inc, sub) = (
165 *DRIPS_PER_STORAGE_COLLATERAL_UNIT * inc_collaterals,
166 *DRIPS_PER_STORAGE_COLLATERAL_UNIT * sub_collaterals,
167 );
168
169 let is_contract = state.is_contract_with_code(&addr_with_space)?;
170
171 if spec.cip107
173 && addr.is_contract_address()
174 && (!sub.is_zero() || !inc.is_zero())
175 {
176 let (from_balance, from_collateral) = state.initialize_cip107(addr)?;
177 tracer.trace_convert_storage_points(
178 *addr,
179 from_balance,
180 from_collateral,
181 );
182 }
183
184 if !sub.is_zero() {
185 let storage_points_refund =
186 state.sub_collateral_for_storage(addr, &sub)?;
187 tracer.trace_refund_collateral(*addr, sub - storage_points_refund);
188 }
189 if !inc.is_zero() && !dry_run {
190 let balance = if is_contract {
191 state.sponsor_balance_for_collateral(addr)?
192 + state.available_storage_points_for_collateral(addr)?
193 } else {
194 state.balance(&addr_with_space)?
195 };
196 if inc > balance {
198 return Ok(Err(CollateralCheckError::NotEnoughBalance {
199 required: inc,
200 got: balance,
201 }));
202 }
203
204 let storage_points_used =
205 state.add_collateral_for_storage(addr, &inc)?;
206 tracer.trace_occupy_collateral(*addr, inc - storage_points_used);
207 }
208 Ok(Ok(()))
209}
210
211pub fn settle_collateral_for_all(
216 state: &mut State, substate: &Substate, tracer: &mut dyn TracerTrait,
217 spec: &Spec, dry_run: bool,
218) -> DbResult<CollateralCheckResult> {
219 for address in substate.keys_for_collateral_changed().iter() {
220 let res = settle_collateral_for_address(
221 state, &address, substate, tracer, spec, dry_run,
222 )?;
223 if res.is_err() {
224 return Ok(res);
225 }
226 }
227 Ok(Ok(()))
228}
229
230pub fn initialize_cip107(state: &mut State) -> DbResult<()> {
232 debug!(
233 "set storage_point_prop to {}",
234 CIP107_STORAGE_POINT_PROP_INIT
235 );
236 state.set_system_storage(
237 storage_point_prop().to_vec(),
238 CIP107_STORAGE_POINT_PROP_INIT.into(),
239 )
240}
241
242pub type CollateralCheckResult = std::result::Result<(), CollateralCheckError>;
243
244#[derive(Copy, Clone, PartialEq, Debug)]
245pub enum CollateralCheckError {
246 ExceedStorageLimit { limit: U256, required: U256 },
247 NotEnoughBalance { required: U256, got: U256 },
248}
249
250impl CollateralCheckError {
251 pub fn into_vm_error(self) -> vm::Error {
252 match self {
253 CollateralCheckError::ExceedStorageLimit { .. } => {
254 vm::Error::ExceedStorageLimit
255 }
256 CollateralCheckError::NotEnoughBalance { required, got } => {
257 vm::Error::NotEnoughBalanceForStorage { required, got }
258 }
259 }
260 }
261}