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
+57 -3
View File
@@ -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#"
+23 -1
View File
@@ -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`.