contracts: Make Origin information available (#13708)

* contracts: allow root calls

* contracts: update ContractOrigin

* contracts: test allow root calls

* contracts: rustfmt

* contracts: test root caller traps

* contracts: add Caller enum

* contracts: improve some comments

* contracts: improve some comments

* contracts: format code with +nightly

* contracts: fix failing tests

* contracts: improve charte instantiate call when root origin

* contract: refactor common, call and instantiate inputs

* contracts: fix some failing test

* contracts: trap caller when there is no account id

* Update frame/contracts/src/lib.rs

Co-authored-by: PG Herveou <pgherveou@gmail.com>

* Update frame/contracts/src/exec.rs

Co-authored-by: PG Herveou <pgherveou@gmail.com>

* contracts: make `into_deposit` return zero when the origin is root

* contracts: cargo fmt

* contracts: charging and terminating tests refactored

* contracts: improved errors

* contracts: refactor & merge ContractOrigin cand Caller enums

* Update frame/contracts/src/lib.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* contracts: apply suggested pr changes

* contracts: rename Caller to Origin

* contracts: refactor run fn

* contracts: cr improvements

* contracts: allow root to use delegate call

* contracts: add caller_is_root weight

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* contracts: add caller_is_root benchmarking

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* contracts: add caller_is_root benchmarking

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* contracts: add some minor improvements

* contracts: make caller_is_root runtime fn unstable

* contracts: fix failing wasm test

* contracts: update benchmarking for origin_is_root

* contracts: improve seal_caller_is_root benchmarking

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* contracts: add small pr improvements

* contracts: fix broken tests after master merge

* contracts: acknowledge the default storage deposit limit

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* contracts: move origin to CommonInput

* contracts: add some extra tests

* contracts: move ensure origin logic inside invokable::run_guarded

* contracts: add minor improvements

* contracts: fix caller_is_root benchmarking

* contracts: improve function description

* Update frame/contracts/src/lib.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Update frame/contracts/src/lib.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Update frame/contracts/src/wasm/runtime.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* contracts: improve function description

---------

Co-authored-by: parity-processbot <>
Co-authored-by: PG Herveou <pgherveou@gmail.com>
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
juangirini
2023-05-02 18:45:14 +02:00
committed by GitHub
parent 0e28645153
commit 5db4119f93
10 changed files with 1835 additions and 1234 deletions
+164 -103
View File
@@ -19,7 +19,7 @@
use crate::{
storage::{ContractInfo, DepositAccount},
BalanceOf, Config, Error, Inspect, Pallet, System,
BalanceOf, Config, Error, Inspect, Origin, Pallet, System,
};
use codec::Encode;
use frame_support::{
@@ -352,12 +352,21 @@ where
///
/// This tries to [`Ext::check_limit`] on `origin` and fails if this is not possible.
pub fn new(
origin: &T::AccountId,
origin: &Origin<T>,
limit: Option<BalanceOf<T>>,
min_leftover: BalanceOf<T>,
) -> Result<Self, DispatchError> {
let limit = E::check_limit(origin, limit, min_leftover)?;
Ok(Self { limit, ..Default::default() })
// Check the limit only if the origin is not root.
return match origin {
Origin::Root => Ok(Self {
limit: limit.unwrap_or(T::DefaultDepositLimit::get()),
..Default::default()
}),
Origin::Signed(o) => {
let limit = E::check_limit(o, limit, min_leftover)?;
Ok(Self { limit, ..Default::default() })
},
}
}
/// The total amount of deposit that should change hands as result of the execution
@@ -366,7 +375,13 @@ where
///
/// This drops the root meter in order to make sure it is only called when the whole
/// execution did finish.
pub fn try_into_deposit(self, origin: &T::AccountId) -> Result<DepositOf<T>, DispatchError> {
pub fn try_into_deposit(self, origin: &Origin<T>) -> Result<DepositOf<T>, DispatchError> {
// Only refund or charge deposit if the origin is not root.
let origin = match origin {
Origin::Root => return Ok(Deposit::Charge(Zero::zero())),
Origin::Signed(o) => o,
};
for charge in self.charges.iter().filter(|c| matches!(c.amount, Deposit::Refund(_))) {
E::charge(origin, &charge.deposit_account, &charge.amount, charge.terminated)?;
}
@@ -625,6 +640,12 @@ mod tests {
TestExtTestValue::mutate(|ext| ext.clear())
}
struct ChargingTestCase {
origin: Origin<Test>,
deposit: DepositOf<Test>,
expected: TestExt,
}
#[derive(Default)]
struct StorageInfo {
bytes: u32,
@@ -650,7 +671,7 @@ mod tests {
fn new_reserves_balance_works() {
clear_ext();
TestMeter::new(&ALICE, Some(1_000), 0).unwrap();
TestMeter::new(&Origin::from_account_id(ALICE), Some(1_000), 0).unwrap();
assert_eq!(
TestExtTestValue::get(),
@@ -665,7 +686,7 @@ mod tests {
fn empty_charge_works() {
clear_ext();
let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap();
let mut meter = TestMeter::new(&Origin::from_account_id(ALICE), Some(1_000), 0).unwrap();
assert_eq!(meter.available(), 1_000);
// an empty charge does not create a `Charge` entry
@@ -684,118 +705,158 @@ mod tests {
#[test]
fn charging_works() {
clear_ext();
let test_cases = vec![
ChargingTestCase {
origin: Origin::<Test>::from_account_id(ALICE),
deposit: Deposit::Refund(28),
expected: TestExt {
limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }],
charges: vec![
Charge {
origin: ALICE,
contract: DepositAccount(CHARLIE),
amount: Deposit::Refund(10),
terminated: false,
},
Charge {
origin: ALICE,
contract: DepositAccount(CHARLIE),
amount: Deposit::Refund(20),
terminated: false,
},
Charge {
origin: ALICE,
contract: DepositAccount(BOB),
amount: Deposit::Charge(2),
terminated: false,
},
],
},
},
ChargingTestCase {
origin: Origin::<Test>::Root,
deposit: Deposit::Charge(0),
expected: TestExt { limit_checks: vec![], charges: vec![] },
},
];
let mut meter = TestMeter::new(&ALICE, Some(100), 0).unwrap();
assert_eq!(meter.available(), 100);
for test_case in test_cases {
clear_ext();
let mut nested0_info =
new_info(StorageInfo { bytes: 100, items: 5, bytes_deposit: 100, items_deposit: 10 });
let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
nested0.charge(&Diff {
bytes_added: 108,
bytes_removed: 5,
items_added: 1,
items_removed: 2,
});
nested0.charge(&Diff { bytes_removed: 99, ..Default::default() });
let mut meter = TestMeter::new(&test_case.origin, Some(100), 0).unwrap();
assert_eq!(meter.available(), 100);
let mut nested1_info =
new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 });
let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
nested1.charge(&Diff { items_removed: 5, ..Default::default() });
nested0.absorb(nested1, DepositAccount(CHARLIE), Some(&mut nested1_info));
let mut nested0_info = new_info(StorageInfo {
bytes: 100,
items: 5,
bytes_deposit: 100,
items_deposit: 10,
});
let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
nested0.charge(&Diff {
bytes_added: 108,
bytes_removed: 5,
items_added: 1,
items_removed: 2,
});
nested0.charge(&Diff { bytes_removed: 99, ..Default::default() });
let mut nested2_info =
new_info(StorageInfo { bytes: 100, items: 7, bytes_deposit: 100, items_deposit: 20 });
let mut nested2 = nested0.nested(BalanceOf::<Test>::zero());
nested2.charge(&Diff { items_removed: 7, ..Default::default() });
nested0.absorb(nested2, DepositAccount(CHARLIE), Some(&mut nested2_info));
let mut nested1_info = new_info(StorageInfo {
bytes: 100,
items: 10,
bytes_deposit: 100,
items_deposit: 20,
});
let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
nested1.charge(&Diff { items_removed: 5, ..Default::default() });
nested0.absorb(nested1, DepositAccount(CHARLIE), Some(&mut nested1_info));
nested0.enforce_limit(Some(&mut nested0_info)).unwrap();
meter.absorb(nested0, DepositAccount(BOB), Some(&mut nested0_info));
let mut nested2_info = new_info(StorageInfo {
bytes: 100,
items: 7,
bytes_deposit: 100,
items_deposit: 20,
});
let mut nested2 = nested0.nested(BalanceOf::<Test>::zero());
nested2.charge(&Diff { items_removed: 7, ..Default::default() });
nested0.absorb(nested2, DepositAccount(CHARLIE), Some(&mut nested2_info));
meter.try_into_deposit(&ALICE).unwrap();
nested0.enforce_limit(Some(&mut nested0_info)).unwrap();
meter.absorb(nested0, DepositAccount(BOB), Some(&mut nested0_info));
assert_eq!(nested0_info.extra_deposit(), 112);
assert_eq!(nested1_info.extra_deposit(), 110);
assert_eq!(nested2_info.extra_deposit(), 100);
assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit);
assert_eq!(
TestExtTestValue::get(),
TestExt {
limit_checks: vec![LimitCheck { origin: ALICE, limit: 100, min_leftover: 0 }],
charges: vec![
Charge {
origin: ALICE,
contract: DepositAccount(CHARLIE),
amount: Deposit::Refund(10),
terminated: false
},
Charge {
origin: ALICE,
contract: DepositAccount(CHARLIE),
amount: Deposit::Refund(20),
terminated: false
},
Charge {
origin: ALICE,
contract: DepositAccount(BOB),
amount: Deposit::Charge(2),
terminated: false
}
]
}
)
assert_eq!(nested0_info.extra_deposit(), 112);
assert_eq!(nested1_info.extra_deposit(), 110);
assert_eq!(nested2_info.extra_deposit(), 100);
assert_eq!(TestExtTestValue::get(), test_case.expected)
}
}
#[test]
fn termination_works() {
clear_ext();
let test_cases = vec![
ChargingTestCase {
origin: Origin::<Test>::from_account_id(ALICE),
deposit: Deposit::Refund(107),
expected: TestExt {
limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }],
charges: vec![
Charge {
origin: ALICE,
contract: DepositAccount(CHARLIE),
amount: Deposit::Refund(119),
terminated: true,
},
Charge {
origin: ALICE,
contract: DepositAccount(BOB),
amount: Deposit::Charge(12),
terminated: false,
},
],
},
},
ChargingTestCase {
origin: Origin::<Test>::Root,
deposit: Deposit::Charge(0),
expected: TestExt { limit_checks: vec![], charges: vec![] },
},
];
let mut meter = TestMeter::new(&ALICE, Some(1_000), 0).unwrap();
assert_eq!(meter.available(), 1_000);
for test_case in test_cases {
clear_ext();
let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
nested0.charge(&Diff {
bytes_added: 5,
bytes_removed: 1,
items_added: 3,
items_removed: 1,
});
nested0.charge(&Diff { items_added: 2, ..Default::default() });
let mut meter = TestMeter::new(&test_case.origin, Some(1_000), 0).unwrap();
assert_eq!(meter.available(), 1_000);
let mut nested1_info =
new_info(StorageInfo { bytes: 100, items: 10, bytes_deposit: 100, items_deposit: 20 });
let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
nested1.charge(&Diff { items_removed: 5, ..Default::default() });
nested1.charge(&Diff { bytes_added: 20, ..Default::default() });
nested1.terminate(&nested1_info);
nested0.enforce_limit(Some(&mut nested1_info)).unwrap();
nested0.absorb(nested1, DepositAccount(CHARLIE), None);
let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
nested0.charge(&Diff {
bytes_added: 5,
bytes_removed: 1,
items_added: 3,
items_removed: 1,
});
nested0.charge(&Diff { items_added: 2, ..Default::default() });
meter.absorb(nested0, DepositAccount(BOB), None);
meter.try_into_deposit(&ALICE).unwrap();
let mut nested1_info = new_info(StorageInfo {
bytes: 100,
items: 10,
bytes_deposit: 100,
items_deposit: 20,
});
let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
nested1.charge(&Diff { items_removed: 5, ..Default::default() });
nested1.charge(&Diff { bytes_added: 20, ..Default::default() });
nested1.terminate(&nested1_info);
nested0.enforce_limit(Some(&mut nested1_info)).unwrap();
nested0.absorb(nested1, DepositAccount(CHARLIE), None);
assert_eq!(
TestExtTestValue::get(),
TestExt {
limit_checks: vec![LimitCheck { origin: ALICE, limit: 1_000, min_leftover: 0 }],
charges: vec![
Charge {
origin: ALICE,
contract: DepositAccount(CHARLIE),
amount: Deposit::Refund(119),
terminated: true
},
Charge {
origin: ALICE,
contract: DepositAccount(BOB),
amount: Deposit::Charge(12),
terminated: false
}
]
}
)
meter.absorb(nested0, DepositAccount(BOB), None);
assert_eq!(meter.try_into_deposit(&test_case.origin).unwrap(), test_case.deposit);
assert_eq!(TestExtTestValue::get(), test_case.expected)
}
}
}