diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 15ca3ed130..9f80da0d3c 100644 Binary files a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index eaecba6231..5d81e65191 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/contract/README.md b/substrate/srml/contract/COMPLEXITY.md similarity index 98% rename from substrate/srml/contract/README.md rename to substrate/srml/contract/COMPLEXITY.md index 5673758cc7..824bcc4e0d 100644 --- a/substrate/srml/contract/README.md +++ b/substrate/srml/contract/COMPLEXITY.md @@ -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. diff --git a/substrate/srml/contract/src/exec.rs b/substrate/srml/contract/src/exec.rs index f3cc2139bf..25dfe2e8fe 100644 --- a/substrate/srml/contract/src/exec.rs +++ b/substrate/srml/contract/src/exec.rs @@ -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 + } } diff --git a/substrate/srml/contract/src/tests.rs b/substrate/srml/contract/src/tests.rs index 27d6e59a95..c946ee23b9 100644 --- a/substrate/srml/contract/src/tests.rs +++ b/substrate/srml/contract/src/tests.rs @@ -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(), + || { + >::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!( + >::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(), || { + >::insert(1, code_transfer.to_vec()); + >::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!( + >::get(CONTRACT_SHOULD_TRANSFER_TO, vec![0; 32]), + Some(vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + ); + }); +} diff --git a/substrate/srml/contract/src/vm/mod.rs b/substrate/srml/contract/src/vm/mod.rs index dc59154266..38cf7bec8d 100644 --- a/substrate/srml/contract/src/vm/mod.rs +++ b/substrate/srml/contract/src/vm/mod.rs @@ -69,6 +69,9 @@ pub trait Ext { data: &[u8], output_data: &mut Vec, ) -> Result<(), ()>; + + /// Returns a reference to the account id of the caller. + fn caller(&self) -> &AccountIdOf; } /// 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::::default(), + &mut GasMeter::with_limit(50_000, 1), + ).unwrap(); + } } diff --git a/substrate/srml/contract/src/vm/runtime.rs b/substrate/srml/contract/src/vm/runtime.rs index c56953fcdb..d328086359 100644 --- a/substrate/srml/contract/src/vm/runtime.rs +++ b/substrate/srml/contract/src/vm/runtime.rs @@ -360,6 +360,16 @@ define_env!(init_env, , 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)