mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 16:31:07 +00:00
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:
@@ -534,6 +534,28 @@ benchmarks! {
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
|
||||
|
||||
#[pov_mode = Measured]
|
||||
seal_caller_is_root {
|
||||
let r in 0 .. API_BENCHMARK_RUNS;
|
||||
|
||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
imported_functions: vec![ImportedFunction {
|
||||
module: "seal0",
|
||||
name: "caller_is_root",
|
||||
params: vec![],
|
||||
return_type: Some(ValueType::I32),
|
||||
}],
|
||||
call_body: Some(body::repeated(r, &[
|
||||
Instruction::Call(0),
|
||||
Instruction::Drop,
|
||||
])),
|
||||
.. Default::default()
|
||||
});
|
||||
let instance = Contract::<T>::new(code, vec![])?;
|
||||
let origin = RawOrigin::Root;
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
|
||||
|
||||
#[pov_mode = Measured]
|
||||
seal_address {
|
||||
let r in 0 .. API_BENCHMARK_RUNS;
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::{
|
||||
gas::GasMeter,
|
||||
storage::{self, DepositAccount, WriteOutcome},
|
||||
BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, DebugBufferVec, Determinism, Error,
|
||||
Event, Nonce, Pallet as Contracts, Schedule, System, LOG_TARGET,
|
||||
Event, Nonce, Origin, Pallet as Contracts, Schedule, System, LOG_TARGET,
|
||||
};
|
||||
use frame_support::{
|
||||
crypto::ecdsa::ECDSAExt,
|
||||
@@ -203,8 +203,8 @@ pub trait Ext: sealing::Sealed {
|
||||
take_old: bool,
|
||||
) -> Result<WriteOutcome, DispatchError>;
|
||||
|
||||
/// Returns a reference to the account id of the caller.
|
||||
fn caller(&self) -> &AccountIdOf<Self::T>;
|
||||
/// Returns the caller.
|
||||
fn caller(&self) -> Origin<Self::T>;
|
||||
|
||||
/// Check if a contract lives at the specified `address`.
|
||||
fn is_contract(&self, address: &AccountIdOf<Self::T>) -> bool;
|
||||
@@ -223,6 +223,9 @@ pub trait Ext: sealing::Sealed {
|
||||
/// However, this function does not require any storage lookup and therefore uses less weight.
|
||||
fn caller_is_origin(&self) -> bool;
|
||||
|
||||
/// Check if the caller is origin, and this origin is root.
|
||||
fn caller_is_root(&self) -> bool;
|
||||
|
||||
/// Returns a reference to the account id of the current contract.
|
||||
fn address(&self) -> &AccountIdOf<Self::T>;
|
||||
|
||||
@@ -373,14 +376,15 @@ pub trait Executable<T: Config>: Sized {
|
||||
/// This type implements `Ext` and by that exposes the business logic of contract execution to
|
||||
/// the runtime module which interfaces with the contract (the wasm blob) itself.
|
||||
pub struct Stack<'a, T: Config, E> {
|
||||
/// The account id of a plain account that initiated the call stack.
|
||||
/// The origin that initiated the call stack. It could either be a Signed plain account that
|
||||
/// holds an account id or Root.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Please note that it is possible that the id belongs to a contract rather than a plain
|
||||
/// account when being called through one of the contract RPCs where the client can freely
|
||||
/// choose the origin. This usually makes no sense but is still possible.
|
||||
origin: T::AccountId,
|
||||
/// Please note that it is possible that the id of a Signed origin belongs to a contract rather
|
||||
/// than a plain account when being called through one of the contract RPCs where the
|
||||
/// client can freely choose the origin. This usually makes no sense but is still possible.
|
||||
origin: Origin<T>,
|
||||
/// The cost schedule used when charging from the gas meter.
|
||||
schedule: &'a Schedule<T>,
|
||||
/// The gas meter where costs are charged to.
|
||||
@@ -436,15 +440,15 @@ pub struct Frame<T: Config> {
|
||||
/// If `false` the contract enabled its defense against reentrance attacks.
|
||||
allows_reentry: bool,
|
||||
/// The caller of the currently executing frame which was spawned by `delegate_call`.
|
||||
delegate_caller: Option<T::AccountId>,
|
||||
delegate_caller: Option<Origin<T>>,
|
||||
}
|
||||
|
||||
/// Used in a delegate call frame arguments in order to override the executable and caller.
|
||||
struct DelegatedCall<T: Config, E> {
|
||||
/// The executable which is run instead of the contracts own `executable`.
|
||||
executable: E,
|
||||
/// The account id of the caller contract.
|
||||
caller: T::AccountId,
|
||||
/// The caller of the contract.
|
||||
caller: Origin<T>,
|
||||
}
|
||||
|
||||
/// Parameter passed in when creating a new `Frame`.
|
||||
@@ -617,7 +621,7 @@ where
|
||||
///
|
||||
/// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
|
||||
pub fn run_call(
|
||||
origin: T::AccountId,
|
||||
origin: Origin<T>,
|
||||
dest: T::AccountId,
|
||||
gas_meter: &'a mut GasMeter<T>,
|
||||
storage_meter: &'a mut storage::meter::Meter<T>,
|
||||
@@ -669,7 +673,7 @@ where
|
||||
salt,
|
||||
input_data: input_data.as_ref(),
|
||||
},
|
||||
origin,
|
||||
Origin::from_account_id(origin),
|
||||
gas_meter,
|
||||
storage_meter,
|
||||
schedule,
|
||||
@@ -684,7 +688,7 @@ where
|
||||
/// Create a new call stack.
|
||||
fn new(
|
||||
args: FrameArgs<T, E>,
|
||||
origin: T::AccountId,
|
||||
origin: Origin<T>,
|
||||
gas_meter: &'a mut GasMeter<T>,
|
||||
storage_meter: &'a mut storage::meter::Meter<T>,
|
||||
schedule: &'a Schedule<T>,
|
||||
@@ -846,9 +850,12 @@ where
|
||||
// We need to charge the storage deposit before the initial transfer so that
|
||||
// it can create the account in case the initial transfer is < ed.
|
||||
if entry_point == ExportedFunction::Constructor {
|
||||
// Root origin can't be used to instantiate a contract, so it is safe to assume that
|
||||
// if we reached this point the origin has an associated account.
|
||||
let origin = &self.origin.account_id()?;
|
||||
let frame = top_frame_mut!(self);
|
||||
frame.nested_storage.charge_instantiate(
|
||||
&self.origin,
|
||||
origin,
|
||||
&frame.account_id,
|
||||
frame.contract_info.get(&frame.account_id),
|
||||
)?;
|
||||
@@ -894,13 +901,12 @@ where
|
||||
let contract = frame.contract_info.as_contract();
|
||||
frame.nested_storage.enforce_subcall_limit(contract)?;
|
||||
|
||||
let caller = self.caller().account_id()?.clone();
|
||||
|
||||
// Deposit an instantiation event.
|
||||
Contracts::<T>::deposit_event(
|
||||
vec![T::Hashing::hash_of(self.caller()), T::Hashing::hash_of(account_id)],
|
||||
Event::Instantiated {
|
||||
deployer: self.caller().clone(),
|
||||
contract: account_id.clone(),
|
||||
},
|
||||
vec![T::Hashing::hash_of(&caller), T::Hashing::hash_of(account_id)],
|
||||
Event::Instantiated { deployer: caller, contract: account_id.clone() },
|
||||
);
|
||||
},
|
||||
(ExportedFunction::Call, Some(code_hash)) => {
|
||||
@@ -918,7 +924,7 @@ where
|
||||
|
||||
let caller = self.caller();
|
||||
Contracts::<T>::deposit_event(
|
||||
vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(&account_id)],
|
||||
vec![T::Hashing::hash_of(&caller), T::Hashing::hash_of(&account_id)],
|
||||
Event::Called { caller: caller.clone(), contract: account_id.clone() },
|
||||
);
|
||||
},
|
||||
@@ -1076,7 +1082,16 @@ where
|
||||
}
|
||||
|
||||
let value = frame.value_transferred;
|
||||
Self::transfer(ExistenceRequirement::KeepAlive, self.caller(), &frame.account_id, value)
|
||||
|
||||
// Get the account id from the caller.
|
||||
// If the caller is root there is no account to transfer from, and therefore we can't take
|
||||
// any `value` other than 0.
|
||||
let caller = match self.caller() {
|
||||
Origin::Signed(caller) => caller,
|
||||
Origin::Root if value.is_zero() => return Ok(()),
|
||||
Origin::Root => return DispatchError::RootNotAllowed.into(),
|
||||
};
|
||||
Self::transfer(ExistenceRequirement::KeepAlive, &caller, &frame.account_id, value)
|
||||
}
|
||||
|
||||
/// Reference to the current (top) frame.
|
||||
@@ -1282,11 +1297,14 @@ where
|
||||
&self.top_frame().account_id
|
||||
}
|
||||
|
||||
fn caller(&self) -> &T::AccountId {
|
||||
fn caller(&self) -> Origin<T> {
|
||||
if let Some(caller) = &self.top_frame().delegate_caller {
|
||||
caller
|
||||
caller.clone()
|
||||
} else {
|
||||
self.frames().nth(1).map(|f| &f.account_id).unwrap_or(&self.origin)
|
||||
self.frames()
|
||||
.nth(1)
|
||||
.map(|f| Origin::from_account_id(f.account_id.clone()))
|
||||
.unwrap_or(self.origin.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1303,7 +1321,12 @@ where
|
||||
}
|
||||
|
||||
fn caller_is_origin(&self) -> bool {
|
||||
self.caller() == &self.origin
|
||||
self.origin == self.caller()
|
||||
}
|
||||
|
||||
fn caller_is_root(&self) -> bool {
|
||||
// if the caller isn't origin, then it can't be root.
|
||||
self.caller_is_origin() && self.origin == Origin::Root
|
||||
}
|
||||
|
||||
fn balance(&self) -> BalanceOf<T> {
|
||||
@@ -1637,11 +1660,13 @@ mod tests {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, exec_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), value).unwrap();
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&Origin::from_account_id(ALICE), Some(0), value)
|
||||
.unwrap();
|
||||
|
||||
assert_matches!(
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
Origin::from_account_id(ALICE),
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -1692,10 +1717,12 @@ mod tests {
|
||||
place_contract(&dest, success_ch);
|
||||
set_balance(&origin, 100);
|
||||
let balance = get_balance(&dest);
|
||||
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), value).unwrap();
|
||||
let contract_origin = Origin::from_account_id(origin.clone());
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap();
|
||||
|
||||
let _ = MockStack::run_call(
|
||||
origin.clone(),
|
||||
contract_origin.clone(),
|
||||
dest.clone(),
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1734,10 +1761,12 @@ mod tests {
|
||||
place_contract(&dest, delegate_ch);
|
||||
set_balance(&origin, 100);
|
||||
let balance = get_balance(&dest);
|
||||
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap();
|
||||
let contract_origin = Origin::from_account_id(origin.clone());
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap();
|
||||
|
||||
let _ = MockStack::run_call(
|
||||
origin.clone(),
|
||||
contract_origin.clone(),
|
||||
dest.clone(),
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1770,10 +1799,12 @@ mod tests {
|
||||
place_contract(&dest, return_ch);
|
||||
set_balance(&origin, 100);
|
||||
let balance = get_balance(&dest);
|
||||
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap();
|
||||
let contract_origin = Origin::from_account_id(origin.clone());
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 55).unwrap();
|
||||
|
||||
let output = MockStack::run_call(
|
||||
origin.clone(),
|
||||
contract_origin.clone(),
|
||||
dest.clone(),
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1821,11 +1852,13 @@ mod tests {
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(origin);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
place_contract(&BOB, return_ch);
|
||||
|
||||
let result = MockStack::run_call(
|
||||
origin,
|
||||
contract_origin,
|
||||
dest,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1855,10 +1888,12 @@ mod tests {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, return_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(origin);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
let result = MockStack::run_call(
|
||||
origin,
|
||||
contract_origin,
|
||||
dest,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1886,10 +1921,12 @@ mod tests {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, input_data_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1918,7 +1955,9 @@ mod tests {
|
||||
let executable =
|
||||
MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 10_000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap();
|
||||
|
||||
let result = MockStack::run_instantiate(
|
||||
ALICE,
|
||||
@@ -1966,10 +2005,12 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
set_balance(&BOB, 1);
|
||||
place_contract(&BOB, recurse_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), value).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), value).unwrap();
|
||||
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -1996,7 +2037,9 @@ mod tests {
|
||||
|
||||
let bob_ch = MockLoader::insert(Call, |ctx, _| {
|
||||
// Record the caller for bob.
|
||||
WitnessedCallerBob::mutate(|caller| *caller = Some(ctx.ext.caller().clone()));
|
||||
WitnessedCallerBob::mutate(|caller| {
|
||||
*caller = Some(ctx.ext.caller().account_id().unwrap().clone())
|
||||
});
|
||||
|
||||
// Call into CHARLIE contract.
|
||||
assert_matches!(
|
||||
@@ -2008,7 +2051,9 @@ mod tests {
|
||||
});
|
||||
let charlie_ch = MockLoader::insert(Call, |ctx, _| {
|
||||
// Record the caller for charlie.
|
||||
WitnessedCallerCharlie::mutate(|caller| *caller = Some(ctx.ext.caller().clone()));
|
||||
WitnessedCallerCharlie::mutate(|caller| {
|
||||
*caller = Some(ctx.ext.caller().account_id().unwrap().clone())
|
||||
});
|
||||
exec_success()
|
||||
});
|
||||
|
||||
@@ -2016,10 +2061,12 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&dest, bob_ch);
|
||||
place_contract(&CHARLIE, charlie_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(origin.clone());
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
let result = MockStack::run_call(
|
||||
origin.clone(),
|
||||
contract_origin.clone(),
|
||||
dest.clone(),
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2051,9 +2098,11 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, bob_ch);
|
||||
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2080,10 +2129,12 @@ mod tests {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
// ALICE (not contract) -> BOB (contract)
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2108,10 +2159,12 @@ mod tests {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, bob_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
// ALICE (not contract) -> BOB (contract)
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2145,10 +2198,111 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
place_contract(&CHARLIE, code_charlie);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
// ALICE -> BOB (caller is origin) -> CHARLIE (caller is not origin)
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
&schedule,
|
||||
0,
|
||||
vec![0],
|
||||
None,
|
||||
Determinism::Enforced,
|
||||
);
|
||||
assert_matches!(result, Ok(_));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_caller_succeeds() {
|
||||
let code_bob = MockLoader::insert(Call, |ctx, _| {
|
||||
// root is the origin of the call stack.
|
||||
assert!(ctx.ext.caller_is_root());
|
||||
exec_success()
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
let contract_origin = Origin::Root;
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
// root -> BOB (caller is root)
|
||||
let result = MockStack::run_call(
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
&schedule,
|
||||
0,
|
||||
vec![0],
|
||||
None,
|
||||
Determinism::Enforced,
|
||||
);
|
||||
assert_matches!(result, Ok(_));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_caller_does_not_succeed_when_value_not_zero() {
|
||||
let code_bob = MockLoader::insert(Call, |ctx, _| {
|
||||
// root is the origin of the call stack.
|
||||
assert!(ctx.ext.caller_is_root());
|
||||
exec_success()
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
let contract_origin = Origin::Root;
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
// root -> BOB (caller is root)
|
||||
let result = MockStack::run_call(
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
&schedule,
|
||||
1,
|
||||
vec![0],
|
||||
None,
|
||||
Determinism::Enforced,
|
||||
);
|
||||
assert_matches!(result, Err(_));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_caller_succeeds_with_consecutive_calls() {
|
||||
let code_charlie = MockLoader::insert(Call, |ctx, _| {
|
||||
// BOB is not root, even though the origin is root.
|
||||
assert!(!ctx.ext.caller_is_root());
|
||||
exec_success()
|
||||
});
|
||||
|
||||
let code_bob = MockLoader::insert(Call, |ctx, _| {
|
||||
// root is the origin of the call stack.
|
||||
assert!(ctx.ext.caller_is_root());
|
||||
// BOB calls CHARLIE.
|
||||
ctx.ext
|
||||
.call(Weight::zero(), BalanceOf::<Test>::zero(), CHARLIE, 0, vec![], true)
|
||||
});
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
place_contract(&CHARLIE, code_charlie);
|
||||
let contract_origin = Origin::Root;
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
// root -> BOB (caller is root) -> CHARLIE (caller is not root)
|
||||
let result = MockStack::run_call(
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2185,10 +2339,12 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, bob_ch);
|
||||
place_contract(&CHARLIE, charlie_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2212,7 +2368,9 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
let executable =
|
||||
MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
assert_matches!(
|
||||
MockStack::run_instantiate(
|
||||
@@ -2244,8 +2402,10 @@ mod tests {
|
||||
let executable =
|
||||
MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap();
|
||||
storage::meter::Meter::new(&contract_origin, Some(min_balance * 100), min_balance)
|
||||
.unwrap();
|
||||
|
||||
let instantiated_contract_address = assert_matches!(
|
||||
MockStack::run_instantiate(
|
||||
@@ -2288,8 +2448,10 @@ mod tests {
|
||||
let executable =
|
||||
MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, Some(min_balance * 100), min_balance).unwrap();
|
||||
storage::meter::Meter::new(&contract_origin, Some(min_balance * 100), min_balance)
|
||||
.unwrap();
|
||||
|
||||
let instantiated_contract_address = assert_matches!(
|
||||
MockStack::run_instantiate(
|
||||
@@ -2342,13 +2504,17 @@ mod tests {
|
||||
let min_balance = <Test as Config>::Currency::minimum_balance();
|
||||
set_balance(&ALICE, min_balance * 100);
|
||||
place_contract(&BOB, instantiator_ch);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, Some(min_balance * 10), min_balance * 10)
|
||||
.unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(
|
||||
&contract_origin,
|
||||
Some(min_balance * 10),
|
||||
min_balance * 10,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_matches!(
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2374,7 +2540,7 @@ mod tests {
|
||||
&events(),
|
||||
&[
|
||||
Event::Instantiated { deployer: BOB, contract: instantiated_contract_address },
|
||||
Event::Called { caller: ALICE, contract: BOB },
|
||||
Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },
|
||||
]
|
||||
);
|
||||
});
|
||||
@@ -2410,11 +2576,13 @@ mod tests {
|
||||
set_balance(&ALICE, 1000);
|
||||
set_balance(&BOB, 100);
|
||||
place_contract(&BOB, instantiator_ch);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(200), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(200), 0).unwrap();
|
||||
|
||||
assert_matches!(
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2429,7 +2597,10 @@ mod tests {
|
||||
|
||||
// The contract wasn't instantiated so we don't expect to see an instantiation
|
||||
// event here.
|
||||
assert_eq!(&events(), &[Event::Called { caller: ALICE, contract: BOB },]);
|
||||
assert_eq!(
|
||||
&events(),
|
||||
&[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2446,7 +2617,9 @@ mod tests {
|
||||
let executable =
|
||||
MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, 10_000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 100).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, None, 100).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
MockStack::run_instantiate(
|
||||
@@ -2510,10 +2683,12 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
place_contract(&CHARLIE, code_charlie);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2544,7 +2719,9 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
let executable = MockExecutable::from_storage(code, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 10_000);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, min_balance).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, None, min_balance).unwrap();
|
||||
|
||||
let result = MockStack::run_instantiate(
|
||||
ALICE,
|
||||
@@ -2577,9 +2754,11 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -2611,9 +2790,11 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -2648,9 +2829,11 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -2679,11 +2862,13 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
place_contract(&CHARLIE, code_charlie);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
// Calling another contract should succeed
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin.clone(),
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2697,7 +2882,7 @@ mod tests {
|
||||
// Calling into oneself fails
|
||||
assert_err!(
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2733,12 +2918,14 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_bob);
|
||||
place_contract(&CHARLIE, code_charlie);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
|
||||
// BOB -> CHARLIE -> BOB fails as BOB denies reentry.
|
||||
assert_err!(
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -2770,10 +2957,12 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
System::reset_events();
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -2800,10 +2989,10 @@ mod tests {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: BOB,
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&BOB)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&BOB)],
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -2855,10 +3044,12 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 10);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
System::reset_events();
|
||||
MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -2898,10 +3089,10 @@ mod tests {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: BOB,
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&BOB)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&BOB)],
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -2959,8 +3150,9 @@ mod tests {
|
||||
let succ_succ_executable =
|
||||
MockExecutable::from_storage(succ_succ_code, &schedule, &mut gas_meter).unwrap();
|
||||
set_balance(&ALICE, min_balance * 10_000);
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&ALICE, None, min_balance * 100).unwrap();
|
||||
storage::meter::Meter::new(&contract_origin, None, min_balance * 100).unwrap();
|
||||
|
||||
MockStack::run_instantiate(
|
||||
ALICE,
|
||||
@@ -3069,9 +3261,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3196,9 +3389,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3235,9 +3429,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3274,9 +3469,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3330,9 +3526,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3386,9 +3583,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3418,9 +3616,11 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, bob_ch);
|
||||
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
@@ -3480,9 +3680,10 @@ mod tests {
|
||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||
set_balance(&ALICE, min_balance * 1000);
|
||||
place_contract(&BOB, code_hash);
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, None, 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter = storage::meter::Meter::new(&contract_origin, None, 0).unwrap();
|
||||
assert_ok!(MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut gas_meter,
|
||||
&mut storage_meter,
|
||||
@@ -3510,9 +3711,11 @@ mod tests {
|
||||
let schedule = <Test as Config>::Schedule::get();
|
||||
place_contract(&BOB, code_hash);
|
||||
|
||||
let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap();
|
||||
let contract_origin = Origin::from_account_id(ALICE);
|
||||
let mut storage_meter =
|
||||
storage::meter::Meter::new(&contract_origin, Some(0), 0).unwrap();
|
||||
let result = MockStack::run_call(
|
||||
ALICE,
|
||||
contract_origin,
|
||||
BOB,
|
||||
&mut GasMeter::<Test>::new(GAS_LIMIT),
|
||||
&mut storage_meter,
|
||||
|
||||
@@ -97,7 +97,6 @@ pub mod weights;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::{
|
||||
exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack},
|
||||
gas::GasMeter,
|
||||
@@ -105,19 +104,20 @@ use crate::{
|
||||
wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate},
|
||||
weights::WeightInfo,
|
||||
};
|
||||
use codec::{Codec, Encode, HasCompact};
|
||||
use codec::{Codec, Decode, Encode, HasCompact};
|
||||
use environmental::*;
|
||||
use frame_support::{
|
||||
dispatch::{Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo},
|
||||
dispatch::{DispatchError, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo, RawOrigin},
|
||||
ensure,
|
||||
error::BadOrigin,
|
||||
traits::{
|
||||
tokens::fungible::Inspect, ConstU32, Contains, Currency, Get, Randomness,
|
||||
ReservableCurrency, Time,
|
||||
},
|
||||
weights::Weight,
|
||||
BoundedVec, WeakBoundedVec,
|
||||
BoundedVec, RuntimeDebugNoBound, WeakBoundedVec,
|
||||
};
|
||||
use frame_system::Pallet as System;
|
||||
use frame_system::{ensure_signed, pallet_prelude::OriginFor, Pallet as System};
|
||||
use pallet_contracts_primitives::{
|
||||
Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
|
||||
ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue,
|
||||
@@ -584,17 +584,15 @@ pub mod pallet {
|
||||
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
||||
data: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let gas_limit: Weight = gas_limit.into();
|
||||
let origin = ensure_signed(origin)?;
|
||||
let dest = T::Lookup::lookup(dest)?;
|
||||
let common = CommonInput {
|
||||
origin,
|
||||
origin: Origin::from_runtime_origin(origin)?,
|
||||
value,
|
||||
data,
|
||||
gas_limit,
|
||||
gas_limit: gas_limit.into(),
|
||||
storage_deposit_limit: storage_deposit_limit.map(Into::into),
|
||||
debug_message: None,
|
||||
};
|
||||
let dest = T::Lookup::lookup(dest)?;
|
||||
let mut output =
|
||||
CallInput::<T> { dest, determinism: Determinism::Enforced }.run_guarded(common);
|
||||
if let Ok(retval) = &output.result {
|
||||
@@ -645,12 +643,11 @@ pub mod pallet {
|
||||
data: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let code_len = code.len() as u32;
|
||||
let data_len = data.len() as u32;
|
||||
let salt_len = salt.len() as u32;
|
||||
let common = CommonInput {
|
||||
origin,
|
||||
origin: Origin::from_runtime_origin(origin)?,
|
||||
value,
|
||||
data,
|
||||
gas_limit,
|
||||
@@ -688,11 +685,10 @@ pub mod pallet {
|
||||
data: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let data_len = data.len() as u32;
|
||||
let salt_len = salt.len() as u32;
|
||||
let common = CommonInput {
|
||||
origin,
|
||||
origin: Origin::from_runtime_origin(origin)?,
|
||||
value,
|
||||
data,
|
||||
gas_limit,
|
||||
@@ -764,8 +760,8 @@ pub mod pallet {
|
||||
/// calls. This is because on failure all storage changes including events are
|
||||
/// rolled back.
|
||||
Called {
|
||||
/// The account that called the `contract`.
|
||||
caller: T::AccountId,
|
||||
/// The caller of the `contract`.
|
||||
caller: Origin<T>,
|
||||
/// The contract that was called.
|
||||
contract: T::AccountId,
|
||||
},
|
||||
@@ -924,9 +920,38 @@ pub mod pallet {
|
||||
StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
|
||||
}
|
||||
|
||||
/// The type of origins supported by the contracts pallet.
|
||||
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)]
|
||||
pub enum Origin<T: Config> {
|
||||
Root,
|
||||
Signed(T::AccountId),
|
||||
}
|
||||
|
||||
impl<T: Config> Origin<T> {
|
||||
/// Creates a new Signed Caller from an AccountId.
|
||||
pub fn from_account_id(account_id: T::AccountId) -> Self {
|
||||
Origin::Signed(account_id)
|
||||
}
|
||||
/// Creates a new Origin from a `RuntimeOrigin`.
|
||||
pub fn from_runtime_origin(o: OriginFor<T>) -> Result<Self, DispatchError> {
|
||||
match o.into() {
|
||||
Ok(RawOrigin::Root) => Ok(Self::Root),
|
||||
Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)),
|
||||
_ => Err(BadOrigin.into()),
|
||||
}
|
||||
}
|
||||
/// Returns the AccountId of a Signed Origin or an error if the origin is Root.
|
||||
pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> {
|
||||
match self {
|
||||
Origin::Signed(id) => Ok(id),
|
||||
Origin::Root => Err(DispatchError::RootNotAllowed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context of a contract invocation.
|
||||
struct CommonInput<'a, T: Config> {
|
||||
origin: T::AccountId,
|
||||
origin: Origin<T>,
|
||||
value: BalanceOf<T>,
|
||||
data: Vec<u8>,
|
||||
gas_limit: Weight,
|
||||
@@ -975,6 +1000,19 @@ trait Invokable<T: Config> {
|
||||
environmental!(executing_contract: bool);
|
||||
|
||||
let gas_limit = common.gas_limit;
|
||||
|
||||
// Check whether the origin is allowed here. The logic of the access rules
|
||||
// is in the `ensure_origin`, this could vary for different implementations of this
|
||||
// trait. For example, some actions might not allow Root origin as they could require an
|
||||
// AccountId associated with the origin.
|
||||
if let Err(e) = self.ensure_origin(common.origin.clone()) {
|
||||
return InternalOutput {
|
||||
gas_meter: GasMeter::new(gas_limit),
|
||||
storage_deposit: Default::default(),
|
||||
result: Err(ExecError { error: e.into(), origin: ErrorOrigin::Caller }),
|
||||
}
|
||||
}
|
||||
|
||||
executing_contract::using_once(&mut false, || {
|
||||
executing_contract::with(|f| {
|
||||
// Fail if already entered contract execution
|
||||
@@ -1010,6 +1048,11 @@ trait Invokable<T: Config> {
|
||||
common: CommonInput<T>,
|
||||
gas_meter: GasMeter<T>,
|
||||
) -> InternalOutput<T, Self::Output>;
|
||||
|
||||
/// This method ensures that the given `origin` is allowed to invoke the current `Invokable`.
|
||||
///
|
||||
/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
|
||||
fn ensure_origin(&self, origin: Origin<T>) -> Result<(), DispatchError>;
|
||||
}
|
||||
|
||||
impl<T: Config> Invokable<T> for CallInput<T> {
|
||||
@@ -1020,8 +1063,10 @@ impl<T: Config> Invokable<T> for CallInput<T> {
|
||||
common: CommonInput<T>,
|
||||
mut gas_meter: GasMeter<T>,
|
||||
) -> InternalOutput<T, Self::Output> {
|
||||
let CallInput { dest, determinism } = self;
|
||||
let CommonInput { origin, value, data, debug_message, .. } = common;
|
||||
let mut storage_meter =
|
||||
match StorageMeter::new(&common.origin, common.storage_deposit_limit, common.value) {
|
||||
match StorageMeter::new(&origin, common.storage_deposit_limit, common.value) {
|
||||
Ok(meter) => meter,
|
||||
Err(err) =>
|
||||
return InternalOutput {
|
||||
@@ -1031,8 +1076,6 @@ impl<T: Config> Invokable<T> for CallInput<T> {
|
||||
},
|
||||
};
|
||||
let schedule = T::Schedule::get();
|
||||
let CallInput { dest, determinism } = self;
|
||||
let CommonInput { origin, value, data, debug_message, .. } = common;
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||
origin.clone(),
|
||||
dest.clone(),
|
||||
@@ -1054,6 +1097,10 @@ impl<T: Config> Invokable<T> for CallInput<T> {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_origin(&self, _origin: Origin<T>) -> Result<(), DispatchError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Invokable<T> for InstantiateInput<T> {
|
||||
@@ -1067,12 +1114,15 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
|
||||
let mut storage_deposit = Default::default();
|
||||
let try_exec = || {
|
||||
let schedule = T::Schedule::get();
|
||||
let InstantiateInput { salt, .. } = self;
|
||||
let CommonInput { origin: contract_origin, .. } = common;
|
||||
let origin = contract_origin.account_id()?;
|
||||
let (extra_deposit, executable) = match &self.code {
|
||||
Code::Upload(binary) => {
|
||||
let executable = PrefabWasmModule::from_code(
|
||||
binary.clone(),
|
||||
&schedule,
|
||||
common.origin.clone(),
|
||||
origin.clone(),
|
||||
Determinism::Enforced,
|
||||
TryInstantiate::Skip,
|
||||
)
|
||||
@@ -1094,14 +1144,13 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
|
||||
PrefabWasmModule::from_storage(*hash, &schedule, &mut gas_meter)?,
|
||||
),
|
||||
};
|
||||
let contract_origin = Origin::from_account_id(origin.clone());
|
||||
let mut storage_meter = StorageMeter::new(
|
||||
&common.origin,
|
||||
&contract_origin,
|
||||
common.storage_deposit_limit,
|
||||
common.value.saturating_add(extra_deposit),
|
||||
)?;
|
||||
|
||||
let InstantiateInput { salt, .. } = self;
|
||||
let CommonInput { origin, value, data, debug_message, .. } = common;
|
||||
let CommonInput { value, data, debug_message, .. } = common;
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin.clone(),
|
||||
executable,
|
||||
@@ -1115,12 +1164,19 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
|
||||
);
|
||||
|
||||
storage_deposit = storage_meter
|
||||
.try_into_deposit(&origin)?
|
||||
.try_into_deposit(&contract_origin)?
|
||||
.saturating_add(&StorageDeposit::Charge(extra_deposit));
|
||||
result
|
||||
};
|
||||
InternalOutput { result: try_exec(), gas_meter, storage_deposit }
|
||||
}
|
||||
|
||||
fn ensure_origin(&self, origin: Origin<T>) -> Result<(), DispatchError> {
|
||||
match origin {
|
||||
Origin::Signed(_) => Ok(()),
|
||||
Origin::Root => Err(DispatchError::RootNotAllowed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
@@ -1147,6 +1203,7 @@ impl<T: Config> Pallet<T> {
|
||||
determinism: Determinism,
|
||||
) -> ContractExecResult<BalanceOf<T>> {
|
||||
let mut debug_message = if debug { Some(DebugBufferVec::<T>::default()) } else { None };
|
||||
let origin = Origin::from_account_id(origin);
|
||||
let common = CommonInput {
|
||||
origin,
|
||||
value,
|
||||
@@ -1189,7 +1246,7 @@ impl<T: Config> Pallet<T> {
|
||||
) -> ContractInstantiateResult<T::AccountId, BalanceOf<T>> {
|
||||
let mut debug_message = if debug { Some(DebugBufferVec::<T>::default()) } else { None };
|
||||
let common = CommonInput {
|
||||
origin,
|
||||
origin: Origin::from_account_id(origin),
|
||||
value,
|
||||
data,
|
||||
gas_limit,
|
||||
|
||||
@@ -260,6 +260,9 @@ pub struct HostFnWeights<T: Config> {
|
||||
/// Weight of calling `seal_caller_is_origin`.
|
||||
pub caller_is_origin: Weight,
|
||||
|
||||
/// Weight of calling `seal_caller_is_root`.
|
||||
pub caller_is_root: Weight,
|
||||
|
||||
/// Weight of calling `seal_address`.
|
||||
pub address: Weight,
|
||||
|
||||
@@ -557,6 +560,7 @@ impl<T: Config> Default for HostFnWeights<T> {
|
||||
code_hash: cost!(seal_code_hash),
|
||||
own_code_hash: cost!(seal_own_code_hash),
|
||||
caller_is_origin: cost!(seal_caller_is_origin),
|
||||
caller_is_root: cost!(seal_caller_is_root),
|
||||
address: cost!(seal_address),
|
||||
gas_left: cost!(seal_gas_left),
|
||||
balance: cost!(seal_balance),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ use crate::{
|
||||
wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode},
|
||||
weights::WeightInfo,
|
||||
BalanceOf, Code, CodeStorage, Config, ContractInfo, ContractInfoOf, DefaultAddressGenerator,
|
||||
DeletionQueueCounter, Error, Pallet, Schedule,
|
||||
DeletionQueueCounter, Error, Origin, Pallet, Schedule,
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use codec::Encode;
|
||||
use frame_support::{
|
||||
assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok,
|
||||
dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo},
|
||||
dispatch::{DispatchError, DispatchErrorWithPostInfo, PostDispatchInfo},
|
||||
parameter_types,
|
||||
storage::child,
|
||||
traits::{
|
||||
@@ -517,6 +517,11 @@ impl<'a> From<ExtensionInput<'a>> for Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Origin<Test> {
|
||||
fn default() -> Self {
|
||||
Self::Signed(ALICE)
|
||||
}
|
||||
}
|
||||
// Perform a call to a plain account.
|
||||
// The actual transfer fails because we can only call contracts.
|
||||
// Then we check that at least the base costs where charged (no runtime gas costs.)
|
||||
@@ -986,18 +991,21 @@ fn deploy_and_call_other_contract() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: caller_addr.clone(),
|
||||
caller: Origin::from_account_id(caller_addr.clone()),
|
||||
contract: callee_addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&caller_addr), hash(&callee_addr)],
|
||||
topics: vec![
|
||||
hash(&Origin::<Test>::from_account_id(caller_addr.clone())),
|
||||
hash(&callee_addr)
|
||||
],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: caller_addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&caller_addr)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&caller_addr)],
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -1305,10 +1313,10 @@ fn self_destruct_works() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&addr)],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
@@ -3626,10 +3634,10 @@ fn storage_deposit_works() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&addr)],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
@@ -3643,10 +3651,10 @@ fn storage_deposit_works() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&addr)],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
@@ -3660,10 +3668,10 @@ fn storage_deposit_works() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&addr)],
|
||||
topics: vec![hash(&Origin::<Test>::from_account_id(ALICE)), hash(&addr)],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
@@ -4115,18 +4123,24 @@ fn set_code_hash() {
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: contract_addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&contract_addr)],
|
||||
topics: vec![
|
||||
hash(&Origin::<Test>::from_account_id(ALICE)),
|
||||
hash(&contract_addr)
|
||||
],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Contracts(crate::Event::Called {
|
||||
caller: ALICE,
|
||||
caller: Origin::from_account_id(ALICE),
|
||||
contract: contract_addr.clone(),
|
||||
}),
|
||||
topics: vec![hash(&ALICE), hash(&contract_addr)],
|
||||
topics: vec![
|
||||
hash(&Origin::<Test>::from_account_id(ALICE)),
|
||||
hash(&contract_addr)
|
||||
],
|
||||
},
|
||||
],
|
||||
);
|
||||
@@ -5015,3 +5029,122 @@ fn account_reentrance_count_works() {
|
||||
assert_eq!(result2.data, 0.encode());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_cannot_upload_code() {
|
||||
let (wasm, _) = compile_module::<Test>("dummy").unwrap();
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_noop!(
|
||||
Contracts::upload_code(RuntimeOrigin::root(), wasm, None, Determinism::Enforced),
|
||||
DispatchError::BadOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_cannot_remove_code() {
|
||||
let (_, code_hash) = compile_module::<Test>("dummy").unwrap();
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_noop!(
|
||||
Contracts::remove_code(RuntimeOrigin::root(), code_hash),
|
||||
DispatchError::BadOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_cannot_set_code() {
|
||||
let (_, code_hash) = compile_module::<Test>("dummy").unwrap();
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_noop!(
|
||||
Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB, code_hash),
|
||||
DispatchError::BadOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_cannot_call_code() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_noop!(
|
||||
Contracts::call(RuntimeOrigin::none(), BOB, 0, GAS_LIMIT, None, Vec::new()),
|
||||
DispatchError::BadOrigin,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_can_call() {
|
||||
let (wasm, _) = compile_module::<Test>("dummy").unwrap();
|
||||
|
||||
ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
let addr = Contracts::bare_instantiate(
|
||||
ALICE,
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
Code::Upload(wasm),
|
||||
vec![],
|
||||
vec![],
|
||||
false,
|
||||
)
|
||||
.result
|
||||
.unwrap()
|
||||
.account_id;
|
||||
|
||||
// Call the contract.
|
||||
assert_ok!(Contracts::call(
|
||||
RuntimeOrigin::root(),
|
||||
addr.clone(),
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
vec![]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_cannot_instantiate_with_code() {
|
||||
let (wasm, _) = compile_module::<Test>("dummy").unwrap();
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_err_ignore_postinfo!(
|
||||
Contracts::instantiate_with_code(
|
||||
RuntimeOrigin::root(),
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
wasm,
|
||||
vec![],
|
||||
vec![],
|
||||
),
|
||||
DispatchError::RootNotAllowed,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_cannot_instantiate() {
|
||||
let (_, code_hash) = compile_module::<Test>("dummy").unwrap();
|
||||
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_err_ignore_postinfo!(
|
||||
Contracts::instantiate(
|
||||
RuntimeOrigin::root(),
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
None,
|
||||
code_hash,
|
||||
vec![],
|
||||
vec![],
|
||||
),
|
||||
DispatchError::RootNotAllowed
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ mod tests {
|
||||
gas::GasMeter,
|
||||
storage::WriteOutcome,
|
||||
tests::{RuntimeCall, Test, ALICE, BOB},
|
||||
BalanceOf, CodeHash, Error, Pallet as Contracts,
|
||||
BalanceOf, CodeHash, Error, Origin, Pallet as Contracts,
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use frame_support::{
|
||||
@@ -436,6 +436,7 @@ mod tests {
|
||||
ecdsa_recover: RefCell<Vec<([u8; 65], [u8; 32])>>,
|
||||
sr25519_verify: RefCell<Vec<([u8; 64], Vec<u8>, [u8; 32])>>,
|
||||
code_hashes: Vec<CodeHash<Test>>,
|
||||
caller: Origin<Test>,
|
||||
}
|
||||
|
||||
/// The call is mocked and just returns this hardcoded value.
|
||||
@@ -459,6 +460,7 @@ mod tests {
|
||||
gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)),
|
||||
debug_buffer: Default::default(),
|
||||
ecdsa_recover: Default::default(),
|
||||
caller: Default::default(),
|
||||
sr25519_verify: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -545,8 +547,8 @@ mod tests {
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
fn caller(&self) -> &AccountIdOf<Self::T> {
|
||||
&ALICE
|
||||
fn caller(&self) -> Origin<Self::T> {
|
||||
self.caller.clone()
|
||||
}
|
||||
fn is_contract(&self, _address: &AccountIdOf<Self::T>) -> bool {
|
||||
true
|
||||
@@ -561,6 +563,9 @@ mod tests {
|
||||
fn caller_is_origin(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn caller_is_root(&self) -> bool {
|
||||
&self.caller == &Origin::Root
|
||||
}
|
||||
fn address(&self) -> &AccountIdOf<Self::T> {
|
||||
&BOB
|
||||
}
|
||||
@@ -1499,6 +1504,16 @@ mod tests {
|
||||
assert_ok!(execute(CODE_CALLER, vec![], MockExt::default()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caller_traps_when_no_account_id() {
|
||||
let mut ext = MockExt::default();
|
||||
ext.caller = Origin::Root;
|
||||
assert_eq!(
|
||||
execute(CODE_CALLER, vec![], ext),
|
||||
Err(ExecError { error: DispatchError::RootNotAllowed, origin: ErrorOrigin::Caller })
|
||||
);
|
||||
}
|
||||
|
||||
/// calls `seal_address` and compares the result with the constant (BOB's address part).
|
||||
const CODE_ADDRESS: &str = r#"
|
||||
(module
|
||||
@@ -2977,6 +2992,45 @@ mod tests {
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 0u32.encode() },);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caller_is_root_works() {
|
||||
const CODE_CALLER_IS_ROOT: &str = r#"
|
||||
;; This runs `caller_is_root` check on zero account address
|
||||
(module
|
||||
(import "seal0" "caller_is_root" (func $caller_is_root (result i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; [0, 4) here the return code of the `caller_is_root` will be stored
|
||||
;; we initialize it with non-zero value to be sure that it's being overwritten below
|
||||
(data (i32.const 0) "\10\10\10\10")
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
(func (export "call")
|
||||
(i32.store
|
||||
(i32.const 0)
|
||||
(call $caller_is_root)
|
||||
)
|
||||
;; exit with success and take `caller_is_root` return code to the output buffer
|
||||
(call $seal_return (i32.const 0) (i32.const 0) (i32.const 4))
|
||||
)
|
||||
)
|
||||
"#;
|
||||
// The default `caller` is ALICE. Therefore not root.
|
||||
let output = execute(CODE_CALLER_IS_ROOT, vec![], MockExt::default()).unwrap();
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 0u32.encode() },);
|
||||
|
||||
// The caller is forced to be root instead of using the default ALICE.
|
||||
let output = execute(
|
||||
CODE_CALLER_IS_ROOT,
|
||||
vec![],
|
||||
MockExt { caller: Origin::Root, ..MockExt::default() },
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: 1u32.encode() },);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_code_hash() {
|
||||
const CODE: &str = r#"
|
||||
|
||||
@@ -191,6 +191,8 @@ pub enum RuntimeCosts {
|
||||
OwnCodeHash,
|
||||
/// Weight of calling `seal_caller_is_origin`.
|
||||
CallerIsOrigin,
|
||||
/// Weight of calling `caller_is_root`.
|
||||
CallerIsRoot,
|
||||
/// Weight of calling `seal_address`.
|
||||
Address,
|
||||
/// Weight of calling `seal_gas_left`.
|
||||
@@ -283,6 +285,7 @@ impl RuntimeCosts {
|
||||
CodeHash => s.code_hash,
|
||||
OwnCodeHash => s.own_code_hash,
|
||||
CallerIsOrigin => s.caller_is_origin,
|
||||
CallerIsRoot => s.caller_is_root,
|
||||
Address => s.address,
|
||||
GasLeft => s.gas_left,
|
||||
Balance => s.balance,
|
||||
@@ -1750,14 +1753,18 @@ pub mod env {
|
||||
/// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the
|
||||
/// extrinsic will be returned. Otherwise, if this call is initiated by another contract then
|
||||
/// the address of the contract will be returned. The value is encoded as T::AccountId.
|
||||
///
|
||||
/// If there is no address associated with the caller (e.g. because the caller is root) then
|
||||
/// it traps with `BadOrigin`.
|
||||
#[prefixed_alias]
|
||||
fn caller(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
|
||||
ctx.charge_gas(RuntimeCosts::Caller)?;
|
||||
let caller = ctx.ext.caller().account_id()?.clone();
|
||||
Ok(ctx.write_sandbox_output(
|
||||
memory,
|
||||
out_ptr,
|
||||
out_len_ptr,
|
||||
&ctx.ext.caller().encode(),
|
||||
&caller.encode(),
|
||||
false,
|
||||
already_charged,
|
||||
)?)
|
||||
@@ -1856,6 +1863,21 @@ pub mod env {
|
||||
Ok(ctx.ext.caller_is_origin() as u32)
|
||||
}
|
||||
|
||||
/// Checks whether the caller of the current contract is root.
|
||||
///
|
||||
/// Note that only the origin of the call stack can be root. Hence this function returning
|
||||
/// `true` implies that the contract is being called by the origin.
|
||||
///
|
||||
/// A return value of `true` indicates that this contract is being called by a root origin,
|
||||
/// and `false` indicates that the caller is a signed origin.
|
||||
///
|
||||
/// Returned value is a `u32`-encoded boolean: (`0 = false`, `1 = true`).
|
||||
#[unstable]
|
||||
fn caller_is_root(ctx: _, _memory: _) -> Result<u32, TrapReason> {
|
||||
ctx.charge_gas(RuntimeCosts::CallerIsRoot)?;
|
||||
Ok(ctx.ext.caller_is_root() as u32)
|
||||
}
|
||||
|
||||
/// Stores the address of the current contract into the supplied buffer.
|
||||
///
|
||||
/// The value is stored to linear memory at the address pointed to by `out_ptr`.
|
||||
|
||||
Generated
+1013
-972
File diff suppressed because it is too large
Load Diff
@@ -553,6 +553,8 @@ pub enum DispatchError {
|
||||
Corruption,
|
||||
/// Some resource (e.g. a preimage) is unavailable right now. This might fix itself later.
|
||||
Unavailable,
|
||||
/// Root origin is not allowed.
|
||||
RootNotAllowed,
|
||||
}
|
||||
|
||||
/// Result of a `Dispatchable` which contains the `DispatchResult` and additional information about
|
||||
@@ -678,6 +680,7 @@ impl From<DispatchError> for &'static str {
|
||||
Exhausted => "Resources exhausted",
|
||||
Corruption => "State corrupt",
|
||||
Unavailable => "Resource unavailable",
|
||||
RootNotAllowed => "Root not allowed",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,6 +727,7 @@ impl traits::Printable for DispatchError {
|
||||
Exhausted => "Resources exhausted".print(),
|
||||
Corruption => "State corrupt".print(),
|
||||
Unavailable => "Resource unavailable".print(),
|
||||
RootNotAllowed => "Root not allowed".print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user