mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 21:11:07 +00:00
srml-contract: introduce ext_caller external function (#1255)
* Implement ext_caller. * Rebuild wasm. * Update COMPLEIXTY.md
This commit is contained in:
committed by
Gav Wood
parent
c1b0fba486
commit
ce91da6b89
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -275,6 +275,12 @@ This function receives a `data` buffer as an argument. Execution of the function
|
||||
|
||||
**complexity**: The complexity of this function is proportional to the size of the `data` buffer.
|
||||
|
||||
## ext_caller
|
||||
|
||||
This function serializes the account ID of the caller into the scratch buffer.
|
||||
|
||||
**complexity**: Assuming that the account ID is of constant size, this function has constant complexity.
|
||||
|
||||
## ext_input_size
|
||||
|
||||
**complexity**: This function is of constant complexity.
|
||||
@@ -90,7 +90,7 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
|
||||
output_data,
|
||||
&mut CallContext {
|
||||
ctx: &mut nested,
|
||||
_caller: caller,
|
||||
caller: caller,
|
||||
},
|
||||
&self.config.schedule,
|
||||
gas_meter,
|
||||
@@ -158,7 +158,7 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
|
||||
&mut contract_code,
|
||||
&mut CallContext {
|
||||
ctx: &mut nested,
|
||||
_caller: caller,
|
||||
caller: caller,
|
||||
},
|
||||
&self.config.schedule,
|
||||
gas_meter,
|
||||
@@ -255,7 +255,7 @@ fn transfer<'a, T: Trait>(
|
||||
|
||||
struct CallContext<'a, 'b: 'a, T: Trait + 'b> {
|
||||
ctx: &'a mut ExecutionContext<'b, T>,
|
||||
_caller: T::AccountId,
|
||||
caller: T::AccountId,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a, T: Trait + 'b> vm::Ext for CallContext<'a, 'b, T> {
|
||||
@@ -298,4 +298,8 @@ impl<'a, 'b: 'a, T: Trait + 'b> vm::Ext for CallContext<'a, 'b, T> {
|
||||
.map_err(|_| ())
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
fn caller(&self) -> &T::AccountId {
|
||||
&self.caller
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,3 +869,85 @@ fn input_data() {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Stores the caller into the storage under the [0x00; 32] key in the contract's storage.
|
||||
const CODE_CALLER_LOGGER: &'static str = r#"
|
||||
(module
|
||||
(import "env" "ext_caller" (func $ext_caller))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "call")
|
||||
;; Fill the scratch buffer with the caller.
|
||||
(call $ext_caller)
|
||||
|
||||
;; Copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(i32.const 32) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
)
|
||||
|
||||
(call $ext_set_storage
|
||||
(i32.const 0) ;; the storage key
|
||||
(i32.const 1) ;; value_not_null=1, i.e. we are not removing the value
|
||||
(i32.const 32) ;; the pointer to the value to store
|
||||
(i32.const 8) ;; the length of the value
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn caller_top_level() {
|
||||
let code_caller_logger = wabt::wat2wasm(CODE_CALLER_LOGGER).unwrap();
|
||||
with_externalities(
|
||||
&mut ExtBuilder::default().build(),
|
||||
|| {
|
||||
<CodeOf<Test>>::insert(1, code_caller_logger.to_vec());
|
||||
|
||||
Balances::set_free_balance(&2, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(Origin::signed(2), 1, 0.into(), 50_000.into(), vec![]));
|
||||
|
||||
// Load the zero-th slot of the storage of the caller logger contract.
|
||||
// We verify here that the caller logger contract has witnessed the call coming from
|
||||
// the account with address 0x02 (see the origin above) - the origin of the tx.
|
||||
assert_eq!(
|
||||
<StorageOf<Test>>::get(1, vec![0; 32]),
|
||||
Some(vec![0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caller_contract() {
|
||||
const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9;
|
||||
|
||||
let code_caller_logger = wabt::wat2wasm(CODE_CALLER_LOGGER).unwrap();
|
||||
let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap();
|
||||
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
|
||||
<CodeOf<Test>>::insert(CONTRACT_SHOULD_TRANSFER_TO, code_caller_logger);
|
||||
|
||||
Balances::set_free_balance(&0, 100_000_000);
|
||||
Balances::increase_total_stake_by(100_000_000);
|
||||
Balances::set_free_balance(&1, 11);
|
||||
Balances::increase_total_stake_by(11);
|
||||
|
||||
assert_ok!(Contract::call(Origin::signed(0), 1, 3.into(), 100_000.into(), Vec::new()));
|
||||
|
||||
// Load the zero-th slot of the storage of the caller logger contract.
|
||||
// We verify here that the caller logger contract has witnessed the call coming from
|
||||
// the caller contract - 0x01.
|
||||
assert_eq!(
|
||||
<StorageOf<Test>>::get(CONTRACT_SHOULD_TRANSFER_TO, vec![0; 32]),
|
||||
Some(vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ pub trait Ext {
|
||||
data: &[u8],
|
||||
output_data: &mut Vec<u8>,
|
||||
) -> Result<(), ()>;
|
||||
|
||||
/// Returns a reference to the account id of the caller.
|
||||
fn caller(&self) -> &AccountIdOf<Self::T>;
|
||||
}
|
||||
|
||||
/// Error that can occur while preparing or executing wasm smart-contract.
|
||||
@@ -225,6 +228,9 @@ mod tests {
|
||||
// TODO: Add tests for different call outcomes.
|
||||
Ok(())
|
||||
}
|
||||
fn caller(&self) -> &u64 {
|
||||
&42
|
||||
}
|
||||
}
|
||||
|
||||
const CODE_TRANSFER: &str = r#"
|
||||
@@ -533,4 +539,69 @@ mod tests {
|
||||
[0x22; 32].to_vec(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const CODE_CALLER: &'static str =
|
||||
r#"
|
||||
(module
|
||||
(import "env" "ext_caller" (func $ext_caller))
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "call")
|
||||
;; Fill the scratch buffer with the caller.
|
||||
(call $ext_caller)
|
||||
|
||||
;; assert $ext_scratch_size == 8
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_scratch_size)
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
|
||||
;; Copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_copy
|
||||
(i32.const 8) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 8) ;; Count of bytes to copy.
|
||||
)
|
||||
|
||||
;; assert that contents of the buffer is equal to the i64 value of 42.
|
||||
(call $assert
|
||||
(i64.eq
|
||||
(i64.load
|
||||
(i32.const 8)
|
||||
)
|
||||
(i64.const 42)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn caller() {
|
||||
let code_caller = wabt::wat2wasm(CODE_CALLER).unwrap();
|
||||
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(
|
||||
&code_caller,
|
||||
&[],
|
||||
&mut Vec::new(),
|
||||
&mut mock_ext,
|
||||
&Schedule::<u64>::default(),
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,16 @@ define_env!(init_env, <E: Ext>,
|
||||
Err(sandbox::HostError)
|
||||
},
|
||||
|
||||
// Stores the address of the caller into the scratch buffer.
|
||||
//
|
||||
// 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.
|
||||
ext_caller(ctx) => {
|
||||
ctx.scratch_buf = ctx.ext.caller().encode();
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Returns the size of the input buffer.
|
||||
ext_input_size(ctx) -> u32 => {
|
||||
Ok(ctx.input_data.len() as u32)
|
||||
|
||||
Reference in New Issue
Block a user