1use crate::{
21 storage::ContractInfo, AccountIdOf, BalanceOf, Config, Error, HoldReason, Inspect, Origin,
22 StorageDeposit as Deposit, System, LOG_TARGET,
23};
24use alloc::vec::Vec;
25use core::{fmt::Debug, marker::PhantomData};
26use frame_support::{
27 traits::{
28 fungible::{Mutate, MutateHold},
29 tokens::{Fortitude, Fortitude::Polite, Precision, Preservation, Restriction},
30 Get,
31 },
32 DefaultNoBound, RuntimeDebugNoBound,
33};
34use sp_runtime::{
35 traits::{Saturating, Zero},
36 DispatchError, DispatchResult, FixedPointNumber, FixedU128,
37};
38
39pub type DepositOf<T> = Deposit<BalanceOf<T>>;
41
42pub type Meter<T> = RawMeter<T, ReservingExt, Root>;
44
45pub type NestedMeter<T> = RawMeter<T, ReservingExt, Nested>;
47
48pub type GenericMeter<T, S> = RawMeter<T, ReservingExt, S>;
52
53pub trait Ext<T: Config> {
57 fn charge(
64 origin: &T::AccountId,
65 contract: &T::AccountId,
66 amount: &DepositOf<T>,
67 state: &ContractState<T>,
68 ) -> Result<(), DispatchError>;
69}
70
71pub enum ReservingExt {}
75
76pub trait State: private::Sealed {}
80
81#[derive(Default, Debug)]
83pub struct Root;
84
85#[derive(Default, Debug)]
88pub struct Nested;
89
90impl State for Root {}
91impl State for Nested {}
92
93#[derive(DefaultNoBound, RuntimeDebugNoBound)]
95pub struct RawMeter<T: Config, E, S: State + Default + Debug> {
96 limit: BalanceOf<T>,
98 total_deposit: DepositOf<T>,
100 own_contribution: Contribution<T>,
102 charges: Vec<Charge<T>>,
107 is_root: bool,
111 _phantom: PhantomData<(E, S)>,
113}
114
115#[derive(Default, RuntimeDebugNoBound)]
117pub struct Diff {
118 pub bytes_added: u32,
120 pub bytes_removed: u32,
122 pub items_added: u32,
124 pub items_removed: u32,
126}
127
128impl Diff {
129 pub fn update_contract<T: Config>(&self, info: Option<&mut ContractInfo<T>>) -> DepositOf<T> {
138 let per_byte = T::DepositPerByte::get();
139 let per_item = T::DepositPerItem::get();
140 let bytes_added = self.bytes_added.saturating_sub(self.bytes_removed);
141 let items_added = self.items_added.saturating_sub(self.items_removed);
142 let mut bytes_deposit = Deposit::Charge(per_byte.saturating_mul((bytes_added).into()));
143 let mut items_deposit = Deposit::Charge(per_item.saturating_mul((items_added).into()));
144
145 let info = if let Some(info) = info {
147 info
148 } else {
149 debug_assert_eq!(self.bytes_removed, 0);
150 debug_assert_eq!(self.items_removed, 0);
151 return bytes_deposit.saturating_add(&items_deposit)
152 };
153
154 let bytes_removed = self.bytes_removed.saturating_sub(self.bytes_added);
156 let items_removed = self.items_removed.saturating_sub(self.items_added);
157 let ratio = FixedU128::checked_from_rational(bytes_removed, info.storage_bytes)
158 .unwrap_or_default()
159 .min(FixedU128::from_u32(1));
160 bytes_deposit = bytes_deposit
161 .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_byte_deposit)));
162 let ratio = FixedU128::checked_from_rational(items_removed, info.storage_items)
163 .unwrap_or_default()
164 .min(FixedU128::from_u32(1));
165 items_deposit = items_deposit
166 .saturating_add(&Deposit::Refund(ratio.saturating_mul_int(info.storage_item_deposit)));
167
168 info.storage_bytes =
170 info.storage_bytes.saturating_add(bytes_added).saturating_sub(bytes_removed);
171 info.storage_items =
172 info.storage_items.saturating_add(items_added).saturating_sub(items_removed);
173 match &bytes_deposit {
174 Deposit::Charge(amount) =>
175 info.storage_byte_deposit = info.storage_byte_deposit.saturating_add(*amount),
176 Deposit::Refund(amount) =>
177 info.storage_byte_deposit = info.storage_byte_deposit.saturating_sub(*amount),
178 }
179 match &items_deposit {
180 Deposit::Charge(amount) =>
181 info.storage_item_deposit = info.storage_item_deposit.saturating_add(*amount),
182 Deposit::Refund(amount) =>
183 info.storage_item_deposit = info.storage_item_deposit.saturating_sub(*amount),
184 }
185
186 bytes_deposit.saturating_add(&items_deposit)
187 }
188}
189
190impl Diff {
191 fn saturating_add(&self, rhs: &Self) -> Self {
192 Self {
193 bytes_added: self.bytes_added.saturating_add(rhs.bytes_added),
194 bytes_removed: self.bytes_removed.saturating_add(rhs.bytes_removed),
195 items_added: self.items_added.saturating_add(rhs.items_added),
196 items_removed: self.items_removed.saturating_add(rhs.items_removed),
197 }
198 }
199}
200
201#[derive(RuntimeDebugNoBound, Clone, PartialEq, Eq)]
205pub enum ContractState<T: Config> {
206 Alive,
207 Terminated { beneficiary: AccountIdOf<T> },
208}
209
210#[derive(RuntimeDebugNoBound, Clone)]
220struct Charge<T: Config> {
221 contract: T::AccountId,
222 amount: DepositOf<T>,
223 state: ContractState<T>,
224}
225
226#[derive(RuntimeDebugNoBound)]
228enum Contribution<T: Config> {
229 Alive(Diff),
231 Checked(DepositOf<T>),
234 Terminated { deposit: DepositOf<T>, beneficiary: AccountIdOf<T> },
238}
239
240impl<T: Config> Contribution<T> {
241 fn update_contract(&self, info: Option<&mut ContractInfo<T>>) -> DepositOf<T> {
243 match self {
244 Self::Alive(diff) => diff.update_contract::<T>(info),
245 Self::Terminated { deposit, beneficiary: _ } | Self::Checked(deposit) =>
246 deposit.clone(),
247 }
248 }
249}
250
251impl<T: Config> Default for Contribution<T> {
252 fn default() -> Self {
253 Self::Alive(Default::default())
254 }
255}
256
257impl<T, E, S> RawMeter<T, E, S>
259where
260 T: Config,
261 E: Ext<T>,
262 S: State + Default + Debug,
263{
264 pub fn nested(&self, limit: BalanceOf<T>) -> RawMeter<T, E, Nested> {
270 debug_assert!(matches!(self.contract_state(), ContractState::Alive));
271
272 RawMeter { limit: self.available().min(limit), ..Default::default() }
273 }
274
275 pub fn absorb(
291 &mut self,
292 absorbed: RawMeter<T, E, Nested>,
293 contract: &T::AccountId,
294 info: Option<&mut ContractInfo<T>>,
295 ) {
296 let own_deposit = absorbed.own_contribution.update_contract(info);
297 self.total_deposit = self
298 .total_deposit
299 .saturating_add(&absorbed.total_deposit)
300 .saturating_add(&own_deposit);
301 self.charges.extend_from_slice(&absorbed.charges);
302 if !own_deposit.is_zero() {
303 self.charges.push(Charge {
304 contract: contract.clone(),
305 amount: own_deposit,
306 state: absorbed.contract_state(),
307 });
308 }
309 }
310
311 pub fn record_charge(&mut self, amount: &DepositOf<T>) -> DispatchResult {
316 let total_deposit = self.total_deposit.saturating_add(&amount);
317
318 if self.is_root && total_deposit.charge_or_zero() > self.limit {
321 log::debug!( target: LOG_TARGET, "Storage deposit limit exhausted: {:?} > {:?}", amount, self.limit);
322 return Err(<Error<T>>::StorageDepositLimitExhausted.into())
323 }
324
325 self.total_deposit = total_deposit;
326 Ok(())
327 }
328
329 fn available(&self) -> BalanceOf<T> {
331 self.total_deposit.available(&self.limit)
332 }
333
334 fn contract_state(&self) -> ContractState<T> {
336 match &self.own_contribution {
337 Contribution::Terminated { deposit: _, beneficiary } =>
338 ContractState::Terminated { beneficiary: beneficiary.clone() },
339 _ => ContractState::Alive,
340 }
341 }
342}
343
344impl<T, E> RawMeter<T, E, Root>
346where
347 T: Config,
348 E: Ext<T>,
349{
350 pub fn new(limit: BalanceOf<T>) -> Self {
355 Self { limit, is_root: true, ..Default::default() }
356 }
357
358 pub fn try_into_deposit(
365 self,
366 origin: &Origin<T>,
367 skip_transfer: bool,
368 ) -> Result<DepositOf<T>, DispatchError> {
369 if !skip_transfer {
370 let origin = match origin {
372 Origin::Root => return Ok(Deposit::Charge(Zero::zero())),
373 Origin::Signed(o) => o,
374 };
375 let try_charge = || {
376 for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_)))
377 {
378 E::charge(origin, &charge.contract, &charge.amount, &charge.state)?;
379 }
380 for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Charge(_)))
381 {
382 E::charge(origin, &charge.contract, &charge.amount, &charge.state)?;
383 }
384 Ok(())
385 };
386 try_charge().map_err(|_: DispatchError| <Error<T>>::StorageDepositNotEnoughFunds)?;
387 }
388
389 Ok(self.total_deposit)
390 }
391}
392
393impl<T: Config, E: Ext<T>> RawMeter<T, E, Nested> {
395 pub fn charge(&mut self, diff: &Diff) {
397 match &mut self.own_contribution {
398 Contribution::Alive(own) => *own = own.saturating_add(diff),
399 _ => panic!("Charge is never called after termination; qed"),
400 };
401 }
402
403 pub fn charge_deposit(&mut self, contract: T::AccountId, amount: DepositOf<T>) {
413 self.record_charge(&amount).ok();
415 self.charges.push(Charge { contract, amount, state: ContractState::Alive });
416 }
417
418 pub fn terminate(&mut self, info: &ContractInfo<T>, beneficiary: T::AccountId) {
424 debug_assert!(matches!(self.contract_state(), ContractState::Alive));
425 self.own_contribution = Contribution::Terminated {
426 deposit: Deposit::Refund(info.total_deposit()),
427 beneficiary,
428 };
429 }
430
431 pub fn enforce_limit(
434 &mut self,
435 info: Option<&mut ContractInfo<T>>,
436 ) -> Result<(), DispatchError> {
437 let deposit = self.own_contribution.update_contract(info);
438 let total_deposit = self.total_deposit.saturating_add(&deposit);
439 if matches!(self.contract_state(), ContractState::Alive) {
441 self.own_contribution = Contribution::Checked(deposit);
442 }
443 if let Deposit::Charge(amount) = total_deposit {
444 if amount > self.limit {
445 log::debug!( target: LOG_TARGET, "Storage deposit limit exhausted: {:?} > {:?}", amount, self.limit);
446 return Err(<Error<T>>::StorageDepositLimitExhausted.into())
447 }
448 }
449 Ok(())
450 }
451}
452
453impl<T: Config> Ext<T> for ReservingExt {
454 fn charge(
455 origin: &T::AccountId,
456 contract: &T::AccountId,
457 amount: &DepositOf<T>,
458 state: &ContractState<T>,
459 ) -> Result<(), DispatchError> {
460 match amount {
461 Deposit::Charge(amount) | Deposit::Refund(amount) if amount.is_zero() => return Ok(()),
462 Deposit::Charge(amount) => {
463 T::Currency::transfer_and_hold(
464 &HoldReason::StorageDepositReserve.into(),
465 origin,
466 contract,
467 *amount,
468 Precision::Exact,
469 Preservation::Preserve,
470 Fortitude::Polite,
471 )?;
472 },
473 Deposit::Refund(amount) => {
474 let transferred = T::Currency::transfer_on_hold(
475 &HoldReason::StorageDepositReserve.into(),
476 contract,
477 origin,
478 *amount,
479 Precision::BestEffort,
480 Restriction::Free,
481 Fortitude::Polite,
482 )?;
483
484 if transferred < *amount {
485 log::error!(
489 target: LOG_TARGET,
490 "Failed to repatriate full storage deposit {:?} from contract {:?} to origin {:?}. Transferred {:?}.",
491 amount, contract, origin, transferred,
492 );
493 }
494 },
495 }
496 if let ContractState::<T>::Terminated { beneficiary } = state {
497 System::<T>::dec_consumers(&contract);
498 T::Currency::transfer(
500 &contract,
501 &beneficiary,
502 T::Currency::reducible_balance(&contract, Preservation::Expendable, Polite),
503 Preservation::Expendable,
504 )?;
505 }
506 Ok(())
507 }
508}
509
510mod private {
511 pub trait Sealed {}
512 impl Sealed for super::Root {}
513 impl Sealed for super::Nested {}
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519 use crate::{exec::AccountIdOf, test_utils::*, tests::Test};
520 use frame_support::parameter_types;
521 use pretty_assertions::assert_eq;
522
523 type TestMeter = RawMeter<Test, TestExt, Root>;
524
525 parameter_types! {
526 static TestExtTestValue: TestExt = Default::default();
527 }
528
529 #[derive(Debug, PartialEq, Eq, Clone)]
530 struct Charge {
531 origin: AccountIdOf<Test>,
532 contract: AccountIdOf<Test>,
533 amount: DepositOf<Test>,
534 state: ContractState<Test>,
535 }
536
537 #[derive(Default, Debug, PartialEq, Eq, Clone)]
538 pub struct TestExt {
539 charges: Vec<Charge>,
540 }
541
542 impl TestExt {
543 fn clear(&mut self) {
544 self.charges.clear();
545 }
546 }
547
548 impl Ext<Test> for TestExt {
549 fn charge(
550 origin: &AccountIdOf<Test>,
551 contract: &AccountIdOf<Test>,
552 amount: &DepositOf<Test>,
553 state: &ContractState<Test>,
554 ) -> Result<(), DispatchError> {
555 TestExtTestValue::mutate(|ext| {
556 ext.charges.push(Charge {
557 origin: origin.clone(),
558 contract: contract.clone(),
559 amount: amount.clone(),
560 state: state.clone(),
561 })
562 });
563 Ok(())
564 }
565 }
566
567 fn clear_ext() {
568 TestExtTestValue::mutate(|ext| ext.clear())
569 }
570
571 struct ChargingTestCase {
572 origin: Origin<Test>,
573 deposit: DepositOf<Test>,
574 expected: TestExt,
575 }
576
577 #[derive(Default)]
578 struct StorageInfo {
579 bytes: u32,
580 items: u32,
581 bytes_deposit: BalanceOf<Test>,
582 items_deposit: BalanceOf<Test>,
583 immutable_data_len: u32,
584 }
585
586 fn new_info(info: StorageInfo) -> ContractInfo<Test> {
587 ContractInfo::<Test> {
588 trie_id: Default::default(),
589 code_hash: Default::default(),
590 storage_bytes: info.bytes,
591 storage_items: info.items,
592 storage_byte_deposit: info.bytes_deposit,
593 storage_item_deposit: info.items_deposit,
594 storage_base_deposit: Default::default(),
595 immutable_data_len: info.immutable_data_len,
596 }
597 }
598
599 #[test]
600 fn new_reserves_balance_works() {
601 clear_ext();
602
603 TestMeter::new(1_000);
604
605 assert_eq!(TestExtTestValue::get(), TestExt { ..Default::default() })
606 }
607
608 #[test]
612 fn nested_zero_limit_requested() {
613 clear_ext();
614
615 let meter = TestMeter::new(1_000);
616 assert_eq!(meter.available(), 1_000);
617 let nested0 = meter.nested(BalanceOf::<Test>::zero());
618 assert_eq!(nested0.available(), 0);
619 }
620
621 #[test]
622 fn nested_some_limit_requested() {
623 clear_ext();
624
625 let meter = TestMeter::new(1_000);
626 assert_eq!(meter.available(), 1_000);
627 let nested0 = meter.nested(500);
628 assert_eq!(nested0.available(), 500);
629 }
630
631 #[test]
632 fn nested_all_limit_requested() {
633 clear_ext();
634
635 let meter = TestMeter::new(1_000);
636 assert_eq!(meter.available(), 1_000);
637 let nested0 = meter.nested(1_000);
638 assert_eq!(nested0.available(), 1_000);
639 }
640
641 #[test]
642 fn nested_over_limit_requested() {
643 clear_ext();
644
645 let meter = TestMeter::new(1_000);
646 assert_eq!(meter.available(), 1_000);
647 let nested0 = meter.nested(2_000);
648 assert_eq!(nested0.available(), 1_000);
649 }
650
651 #[test]
652 fn empty_charge_works() {
653 clear_ext();
654
655 let mut meter = TestMeter::new(1_000);
656 assert_eq!(meter.available(), 1_000);
657
658 let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
660 nested0.charge(&Default::default());
661 meter.absorb(nested0, &BOB, None);
662
663 assert_eq!(TestExtTestValue::get(), TestExt { ..Default::default() })
664 }
665
666 #[test]
667 fn charging_works() {
668 let test_cases = vec![
669 ChargingTestCase {
670 origin: Origin::<Test>::from_account_id(ALICE),
671 deposit: Deposit::Refund(28),
672 expected: TestExt {
673 charges: vec![
674 Charge {
675 origin: ALICE,
676 contract: CHARLIE,
677 amount: Deposit::Refund(10),
678 state: ContractState::Alive,
679 },
680 Charge {
681 origin: ALICE,
682 contract: CHARLIE,
683 amount: Deposit::Refund(20),
684 state: ContractState::Alive,
685 },
686 Charge {
687 origin: ALICE,
688 contract: BOB,
689 amount: Deposit::Charge(2),
690 state: ContractState::Alive,
691 },
692 ],
693 },
694 },
695 ChargingTestCase {
696 origin: Origin::<Test>::Root,
697 deposit: Deposit::Charge(0),
698 expected: TestExt { charges: vec![] },
699 },
700 ];
701
702 for test_case in test_cases {
703 clear_ext();
704
705 let mut meter = TestMeter::new(100);
706 assert_eq!(meter.available(), 100);
707
708 let mut nested0_info = new_info(StorageInfo {
709 bytes: 100,
710 items: 5,
711 bytes_deposit: 100,
712 items_deposit: 10,
713 immutable_data_len: 0,
714 });
715 let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
716 nested0.charge(&Diff {
717 bytes_added: 108,
718 bytes_removed: 5,
719 items_added: 1,
720 items_removed: 2,
721 });
722 nested0.charge(&Diff { bytes_removed: 99, ..Default::default() });
723
724 let mut nested1_info = new_info(StorageInfo {
725 bytes: 100,
726 items: 10,
727 bytes_deposit: 100,
728 items_deposit: 20,
729 immutable_data_len: 0,
730 });
731 let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
732 nested1.charge(&Diff { items_removed: 5, ..Default::default() });
733 nested0.absorb(nested1, &CHARLIE, Some(&mut nested1_info));
734
735 let mut nested2_info = new_info(StorageInfo {
736 bytes: 100,
737 items: 7,
738 bytes_deposit: 100,
739 items_deposit: 20,
740 immutable_data_len: 0,
741 });
742 let mut nested2 = nested0.nested(BalanceOf::<Test>::zero());
743 nested2.charge(&Diff { items_removed: 7, ..Default::default() });
744 nested0.absorb(nested2, &CHARLIE, Some(&mut nested2_info));
745
746 nested0.enforce_limit(Some(&mut nested0_info)).unwrap();
747 meter.absorb(nested0, &BOB, Some(&mut nested0_info));
748
749 assert_eq!(
750 meter.try_into_deposit(&test_case.origin, false).unwrap(),
751 test_case.deposit
752 );
753
754 assert_eq!(nested0_info.extra_deposit(), 112);
755 assert_eq!(nested1_info.extra_deposit(), 110);
756 assert_eq!(nested2_info.extra_deposit(), 100);
757
758 assert_eq!(TestExtTestValue::get(), test_case.expected)
759 }
760 }
761
762 #[test]
763 fn termination_works() {
764 let test_cases = vec![
765 ChargingTestCase {
766 origin: Origin::<Test>::from_account_id(ALICE),
767 deposit: Deposit::Refund(108),
768 expected: TestExt {
769 charges: vec![
770 Charge {
771 origin: ALICE,
772 contract: CHARLIE,
773 amount: Deposit::Refund(120),
774 state: ContractState::Terminated { beneficiary: CHARLIE },
775 },
776 Charge {
777 origin: ALICE,
778 contract: BOB,
779 amount: Deposit::Charge(12),
780 state: ContractState::Alive,
781 },
782 ],
783 },
784 },
785 ChargingTestCase {
786 origin: Origin::<Test>::Root,
787 deposit: Deposit::Charge(0),
788 expected: TestExt { charges: vec![] },
789 },
790 ];
791
792 for test_case in test_cases {
793 clear_ext();
794
795 let mut meter = TestMeter::new(1_000);
796 assert_eq!(meter.available(), 1_000);
797
798 let mut nested0 = meter.nested(BalanceOf::<Test>::max_value());
799 nested0.charge(&Diff {
800 bytes_added: 5,
801 bytes_removed: 1,
802 items_added: 3,
803 items_removed: 1,
804 });
805 nested0.charge(&Diff { items_added: 2, ..Default::default() });
806
807 let mut nested1_info = new_info(StorageInfo {
808 bytes: 100,
809 items: 10,
810 bytes_deposit: 100,
811 items_deposit: 20,
812 immutable_data_len: 0,
813 });
814 let mut nested1 = nested0.nested(BalanceOf::<Test>::max_value());
815 nested1.charge(&Diff { items_removed: 5, ..Default::default() });
816 nested1.charge(&Diff { bytes_added: 20, ..Default::default() });
817 nested1.terminate(&nested1_info, CHARLIE);
818 nested0.enforce_limit(Some(&mut nested1_info)).unwrap();
819 nested0.absorb(nested1, &CHARLIE, None);
820
821 meter.absorb(nested0, &BOB, None);
822 assert_eq!(
823 meter.try_into_deposit(&test_case.origin, false).unwrap(),
824 test_case.deposit
825 );
826 assert_eq!(TestExtTestValue::get(), test_case.expected)
827 }
828 }
829}