mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 04:41:02 +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:
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user