1#[cfg(test)]
21mod tests;
22
23use super::{Nested, Root, State};
24use crate::{
25 BalanceOf, Config, ExecConfig, ExecOrigin as Origin, HoldReason, Pallet,
26 StorageDeposit as Deposit, storage::ContractInfo,
27};
28use alloc::vec::Vec;
29use core::{marker::PhantomData, mem};
30use frame_support::{DebugNoBound, DefaultNoBound, traits::Get};
31use sp_runtime::{
32 DispatchError, FixedPointNumber, FixedU128,
33 traits::{Saturating, Zero},
34};
35
36#[cfg(test)]
37use num_traits::Bounded;
38
39pub type DepositOf<T> = Deposit<BalanceOf<T>>;
41
42pub type Meter<T> = RawMeter<T, ReservingExt, Root>;
44
45pub type GenericMeter<T, S> = RawMeter<T, ReservingExt, S>;
49
50pub trait Ext<T: Config> {
54 fn charge(
61 origin: &T::AccountId,
62 contract: &T::AccountId,
63 amount: &DepositOf<T>,
64 exec_config: &ExecConfig<T>,
65 ) -> Result<(), DispatchError>;
66}
67
68pub enum ReservingExt {}
72
73#[derive(DefaultNoBound, DebugNoBound)]
75pub struct RawMeter<T: Config, E, S: State> {
76 pub(crate) limit: Option<BalanceOf<T>>,
78 total_deposit: DepositOf<T>,
80 own_contribution: Contribution<T>,
83 charges: Vec<Charge<T>>,
88 max_charged: BalanceOf<T>,
91 pub(crate) is_root: bool,
95 _phantom: PhantomData<(E, S)>,
97}
98
99#[derive(Default, DebugNoBound)]
101pub struct Diff {
102 pub bytes_added: u32,
104 pub bytes_removed: u32,
106 pub items_added: u32,
108 pub items_removed: u32,
110}
111
112impl Diff {
113 pub fn update_contract<T: Config>(&self, info: Option<&mut ContractInfo<T>>) -> DepositOf<T> {
122 let per_byte = T::DepositPerByte::get();
123 let per_item = T::DepositPerChildTrieItem::get();
124 let bytes_added = self.bytes_added.saturating_sub(self.bytes_removed);
125 let items_added = self.items_added.saturating_sub(self.items_removed);
126 let mut bytes_deposit = Deposit::Charge(per_byte.saturating_mul((bytes_added).into()));
127 let mut items_deposit = Deposit::Charge(per_item.saturating_mul((items_added).into()));
128
129 let info = if let Some(info) = info {
131 info
132 } else {
133 return bytes_deposit.saturating_add(&items_deposit);
134 };
135
136 let bytes_removed = self.bytes_removed.saturating_sub(self.bytes_added);
138 let items_removed = self.items_removed.saturating_sub(self.items_added);
139 let ratio = FixedU128::checked_from_rational(bytes_removed, info.storage_bytes)
140 .unwrap_or_default()
141 .min(FixedU128::from_u32(1));
142 bytes_deposit = bytes_deposit
143 .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_byte_deposit)));
144 let ratio = FixedU128::checked_from_rational(items_removed, info.storage_items)
145 .unwrap_or_default()
146 .min(FixedU128::from_u32(1));
147 items_deposit = items_deposit
148 .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_item_deposit)));
149
150 info.storage_bytes =
152 info.storage_bytes.saturating_add(bytes_added).saturating_sub(bytes_removed);
153 info.storage_items =
154 info.storage_items.saturating_add(items_added).saturating_sub(items_removed);
155 match &bytes_deposit {
156 Deposit::Charge(amount) => {
157 info.storage_byte_deposit = info.storage_byte_deposit.saturating_add(*amount)
158 },
159 Deposit::Refund(amount) => {
160 info.storage_byte_deposit = info.storage_byte_deposit.saturating_sub(*amount)
161 },
162 }
163 match &items_deposit {
164 Deposit::Charge(amount) => {
165 info.storage_item_deposit = info.storage_item_deposit.saturating_add(*amount)
166 },
167 Deposit::Refund(amount) => {
168 info.storage_item_deposit = info.storage_item_deposit.saturating_sub(*amount)
169 },
170 }
171
172 bytes_deposit.saturating_add(&items_deposit)
173 }
174}
175
176impl Diff {
177 fn saturating_add(&self, rhs: &Self) -> Self {
178 Self {
179 bytes_added: self.bytes_added.saturating_add(rhs.bytes_added),
180 bytes_removed: self.bytes_removed.saturating_add(rhs.bytes_removed),
181 items_added: self.items_added.saturating_add(rhs.items_added),
182 items_removed: self.items_removed.saturating_add(rhs.items_removed),
183 }
184 }
185}
186
187#[derive(DebugNoBound, Clone, PartialEq, Eq)]
189pub enum ContractState<T: Config> {
190 Alive { amount: DepositOf<T> },
191 Terminated,
192}
193
194#[derive(DebugNoBound, Clone)]
204struct Charge<T: Config> {
205 contract: T::AccountId,
206 state: ContractState<T>,
207}
208
209#[derive(DebugNoBound)]
211enum Contribution<T: Config> {
212 Alive(Diff),
214 Checked(DepositOf<T>),
217}
218
219impl<T: Config> Contribution<T> {
220 fn update_contract(&self, info: Option<&mut ContractInfo<T>>) -> DepositOf<T> {
222 match self {
223 Self::Alive(diff) => diff.update_contract::<T>(info),
224 Self::Checked(deposit) => deposit.clone(),
225 }
226 }
227}
228
229impl<T: Config> Default for Contribution<T> {
230 fn default() -> Self {
231 Self::Alive(Default::default())
232 }
233}
234
235impl<T, E, S> RawMeter<T, E, S>
237where
238 T: Config,
239 E: Ext<T>,
240 S: State,
241{
242 pub fn nested(&self, mut limit: Option<BalanceOf<T>>) -> RawMeter<T, E, Nested> {
248 if let (Some(new_limit), Some(old_limit)) = (limit, self.limit) {
249 limit = Some(new_limit.min(old_limit));
250 }
251
252 RawMeter { limit, ..Default::default() }
253 }
254
255 pub fn absorb(
271 &mut self,
272 absorbed: RawMeter<T, E, Nested>,
273 contract: &T::AccountId,
274 info: Option<&mut ContractInfo<T>>,
275 ) {
276 self.max_charged = self
284 .max_charged
285 .max(self.consumed().saturating_add(&absorbed.max_charged()).charge_or_zero());
286
287 let own_deposit = absorbed.own_contribution.update_contract(info);
288 self.total_deposit = self
289 .total_deposit
290 .saturating_add(&absorbed.total_deposit)
291 .saturating_add(&own_deposit);
292 self.charges.extend_from_slice(&absorbed.charges);
293
294 self.recalulculate_max_charged();
295
296 if !own_deposit.is_zero() {
297 self.charges.push(Charge {
298 contract: contract.clone(),
299 state: ContractState::Alive { amount: own_deposit },
300 });
301 }
302 }
303
304 pub fn absorb_only_max_charged(&mut self, absorbed: RawMeter<T, E, Nested>) {
312 self.max_charged = self
313 .max_charged
314 .max(self.consumed().saturating_add(&absorbed.max_charged()).charge_or_zero());
315 }
316
317 pub fn record_charge(&mut self, amount: &DepositOf<T>) {
322 self.total_deposit = self.total_deposit.saturating_add(amount);
323 self.recalulculate_max_charged();
324 }
325
326 pub fn consumed(&self) -> DepositOf<T> {
331 self.total_deposit.saturating_add(&self.own_contribution.update_contract(None))
332 }
333
334 pub fn max_charged(&self) -> DepositOf<T> {
336 Deposit::Charge(self.max_charged)
337 }
338
339 fn recalulculate_max_charged(&mut self) {
341 self.max_charged = self.max_charged.max(self.consumed().charge_or_zero());
342 }
343
344 #[cfg(test)]
348 pub fn available(&self) -> BalanceOf<T> {
349 self.consumed()
350 .available(&self.limit.unwrap_or(BalanceOf::<T>::max_value()))
351 .unwrap_or_default()
352 }
353}
354
355impl<T, E> RawMeter<T, E, Root>
357where
358 T: Config,
359 E: Ext<T>,
360{
361 pub fn new(limit: Option<BalanceOf<T>>) -> Self {
366 Self {
367 limit,
368 is_root: true,
369 own_contribution: Contribution::Checked(Default::default()),
370 ..Default::default()
371 }
372 }
373
374 pub fn execute_postponed_deposits(
378 &mut self,
379 origin: &Origin<T>,
380 exec_config: &ExecConfig<T>,
381 ) -> Result<DepositOf<T>, DispatchError> {
382 let origin = match origin {
384 Origin::Root => return Ok(Deposit::Charge(Zero::zero())),
385 Origin::Signed(o) => o,
386 };
387
388 self.charges.sort_by(|a, b| a.contract.cmp(&b.contract));
390 self.charges = {
391 let mut coalesced: Vec<Charge<T>> = Vec::with_capacity(self.charges.len());
392 for mut ch in mem::take(&mut self.charges) {
393 if let Some(last) = coalesced.last_mut() {
394 if last.contract == ch.contract {
395 match (&mut last.state, &mut ch.state) {
396 (
397 ContractState::Alive { amount: last_amount },
398 ContractState::Alive { amount: ch_amount },
399 ) => {
400 *last_amount = last_amount.saturating_add(&ch_amount);
401 },
402 (ContractState::Alive { amount }, ContractState::Terminated) |
403 (ContractState::Terminated, ContractState::Alive { amount }) => {
404 self.total_deposit = self.total_deposit.saturating_sub(&amount);
406 last.state = ContractState::Terminated;
407 },
408 (ContractState::Terminated, ContractState::Terminated) => {
409 debug_assert!(
410 false,
411 "We never emit two terminates for the same contract."
412 )
413 },
414 }
415 continue;
416 }
417 }
418 coalesced.push(ch);
419 }
420 coalesced
421 };
422
423 for charge in self.charges.iter() {
425 if let ContractState::Alive { amount: amount @ Deposit::Refund(_) } = &charge.state {
426 E::charge(origin, &charge.contract, amount, exec_config)?;
427 }
428 }
429 for charge in self.charges.iter() {
430 if let ContractState::Alive { amount: amount @ Deposit::Charge(_) } = &charge.state {
431 E::charge(origin, &charge.contract, amount, exec_config)?;
432 }
433 }
434
435 Ok(self.total_deposit.clone())
436 }
437
438 pub fn terminate(&mut self, contract: T::AccountId, refunded: BalanceOf<T>) {
443 self.total_deposit = self.total_deposit.saturating_add(&Deposit::Refund(refunded));
444 self.charges.push(Charge { contract, state: ContractState::Terminated });
445
446 }
449}
450
451impl<T: Config, E: Ext<T>> RawMeter<T, E, Nested> {
453 pub fn charge(&mut self, diff: &Diff) {
455 match &mut self.own_contribution {
456 Contribution::Alive(own) => {
457 *own = own.saturating_add(diff);
458 self.recalulculate_max_charged();
459 },
460 _ => panic!("Charge is never called after termination; qed"),
461 };
462 }
463
464 pub fn charge_deposit(&mut self, contract: T::AccountId, amount: DepositOf<T>) {
474 self.record_charge(&amount);
476 self.charges.push(Charge { contract, state: ContractState::Alive { amount } });
477 }
478
479 pub fn finalize_own_contributions(&mut self, info: Option<&mut ContractInfo<T>>) {
481 let deposit = self.own_contribution.update_contract(info);
482 self.own_contribution = Contribution::Checked(deposit);
483
484 }
487
488 pub fn apply_pending_changes_to_contract(&self, info: &mut ContractInfo<T>) {
496 if let Contribution::Alive(diff) = &self.own_contribution {
497 let _ = diff.update_contract::<T>(Some(info));
501 }
502 }
503
504 pub fn bank_pending_changes(&mut self, contract: T::AccountId, info: &mut ContractInfo<T>) {
507 if let Contribution::Alive(_) = &self.own_contribution {
508 let deposit = self.own_contribution.update_contract(Some(info));
509 self.own_contribution = Contribution::Alive(Default::default());
510 if !deposit.is_zero() {
511 self.charge_deposit(contract, deposit);
512 }
513 } else {
514 debug_assert!(
515 false,
516 "on-stack ancestor frames have not finalized yet, so own_contribution \
517 should be Alive when banked; qed",
518 );
519 }
520 }
521}
522
523impl<T: Config> Ext<T> for ReservingExt {
524 fn charge(
525 origin: &T::AccountId,
526 contract: &T::AccountId,
527 amount: &DepositOf<T>,
528 exec_config: &ExecConfig<T>,
529 ) -> Result<(), DispatchError> {
530 match amount {
531 Deposit::Charge(amount) | Deposit::Refund(amount) if amount.is_zero() => (),
532 Deposit::Charge(amount) => {
533 <Pallet<T>>::charge_deposit(
534 HoldReason::StorageDepositReserve,
535 origin,
536 contract,
537 *amount,
538 exec_config,
539 )?;
540 },
541 Deposit::Refund(amount) => {
542 <Pallet<T>>::refund_deposit(
543 HoldReason::StorageDepositReserve,
544 contract,
545 exec_config.funds(origin),
546 *amount,
547 )?;
548 },
549 }
550 Ok(())
551 }
552}