diff --git a/substrate/srml/contract/src/exec.rs b/substrate/srml/contract/src/exec.rs index 7b5cede5cf..b78c3d7151 100644 --- a/substrate/srml/contract/src/exec.rs +++ b/substrate/srml/contract/src/exec.rs @@ -82,6 +82,14 @@ pub trait Ext { /// Returns a reference to the account id of the current contract. fn address(&self) -> &AccountIdOf; + + /// Returns the balance of the current contract. + /// + /// The `value_transferred` is already added. + fn balance(&self) -> BalanceOf; + + /// Returns the value transfered along with this call or as endowment. + fn value_transferred(&self) -> BalanceOf; } /// Loader is a companion of the `Vm` trait. It loads an appropriate abstract @@ -292,6 +300,7 @@ where &mut CallContext { ctx: &mut nested, caller: self.self_account.clone(), + value_transferred: value, }, input_data, empty_output_buf, @@ -361,6 +370,7 @@ where &mut CallContext { ctx: &mut nested, caller: self.self_account.clone(), + value_transferred: endowment, }, input_data, EmptyOutputBuf::new(), @@ -509,6 +519,7 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( struct CallContext<'a, 'b: 'a, T: Trait + 'b, V: Vm + 'b, L: Loader> { ctx: &'a mut ExecutionContext<'b, T, V, L>, caller: T::AccountId, + value_transferred: T::Balance, } impl<'a, 'b: 'a, T, E, V, L> Ext for CallContext<'a, 'b, T, V, L> @@ -558,6 +569,14 @@ where fn caller(&self) -> &T::AccountId { &self.caller } + + fn balance(&self) -> T::Balance { + self.ctx.overlay.get_balance(&self.ctx.self_account) + } + + fn value_transferred(&self) -> T::Balance { + self.value_transferred + } } /// These tests exercise the executive layer. diff --git a/substrate/srml/contract/src/wasm/mod.rs b/substrate/srml/contract/src/wasm/mod.rs index a2430064cb..0682c008be 100644 --- a/substrate/srml/contract/src/wasm/mod.rs +++ b/substrate/srml/contract/src/wasm/mod.rs @@ -254,6 +254,12 @@ mod tests { fn address(&self) -> &u64 { &69 } + fn balance(&self) -> u64 { + 228 + } + fn value_transferred(&self) -> u64 { + 1337 + } } fn execute( @@ -686,6 +692,257 @@ mod tests { .unwrap(); } + const CODE_BALANCE: &str = r#" +(module + (import "env" "ext_balance" (func $ext_balance)) + (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") + ;; This stores the balance in the scratch buffer + (call $ext_balance) + + ;; 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 228. + (call $assert + (i64.eq + (i64.load + (i32.const 8) + ) + (i64.const 228) + ) + ) + ) + (func (export "deploy")) +) +"#; + + #[test] + fn balance() { + let mut mock_ext = MockExt::default(); + let mut gas_meter = GasMeter::with_limit(50_000, 1); + execute( + CODE_BALANCE, + &[], + &mut Vec::new(), + &mut mock_ext, + &mut gas_meter, + ) + .unwrap(); + } + + const CODE_GAS_PRICE: &str = r#" +(module + (import "env" "ext_gas_price" (func $ext_gas_price)) + (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") + ;; This stores the gas price in the scratch buffer + (call $ext_gas_price) + + ;; 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 1312. + (call $assert + (i64.eq + (i64.load + (i32.const 8) + ) + (i64.const 1312) + ) + ) + ) + (func (export "deploy")) +) +"#; + + #[test] + fn gas_price() { + let mut mock_ext = MockExt::default(); + let mut gas_meter = GasMeter::with_limit(50_000, 1312); + execute( + CODE_GAS_PRICE, + &[], + &mut Vec::new(), + &mut mock_ext, + &mut gas_meter, + ) + .unwrap(); + } + + const CODE_GAS_LEFT: &str = r#" +(module + (import "env" "ext_gas_left" (func $ext_gas_left)) + (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_return" (func $ext_return (param i32 i32))) + (import "env" "memory" (memory 1 1)) + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + ;; This stores the gas left in the scratch buffer + (call $ext_gas_left) + + ;; 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. + ) + + (call $ext_return + (i32.const 8) + (i32.const 8) + ) + + (unreachable) + ) + (func (export "deploy")) +) +"#; + + #[test] + fn gas_left() { + let mut mock_ext = MockExt::default(); + let mut gas_meter = GasMeter::with_limit(50_000, 1312); + execute( + CODE_GAS_LEFT, + &[], + &mut Vec::new(), + &mut mock_ext, + &mut gas_meter, + ) + .unwrap(); + } + + const CODE_VALUE_TRANSFERRED: &str = r#" +(module + (import "env" "ext_value_transferred" (func $ext_value_transferred)) + (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") + ;; This stores the value transferred in the scratch buffer + (call $ext_value_transferred) + + ;; 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 1337. + (call $assert + (i64.eq + (i64.load + (i32.const 8) + ) + (i64.const 1337) + ) + ) + ) + (func (export "deploy")) +) +"#; + + #[test] + fn value_transferred() { + let mut mock_ext = MockExt::default(); + let mut gas_meter = GasMeter::with_limit(50_000, 1); + execute( + CODE_VALUE_TRANSFERRED, + &[], + &mut Vec::new(), + &mut mock_ext, + &mut gas_meter, + ) + .unwrap(); + } + + const CODE_RETURN_FROM_START_FN: &str = r#" (module (import "env" "ext_return" (func $ext_return (param i32 i32))) @@ -709,6 +966,7 @@ mod tests { ) "#; + #[test] fn return_from_start_fn() { let mut mock_ext = MockExt::default(); diff --git a/substrate/srml/contract/src/wasm/runtime.rs b/substrate/srml/contract/src/wasm/runtime.rs index 33154fc429..b1bd1e9f1f 100644 --- a/substrate/srml/contract/src/wasm/runtime.rs +++ b/substrate/srml/contract/src/wasm/runtime.rs @@ -447,6 +447,38 @@ define_env!(Env, , Ok(()) }, + // Stores the gas price for the current transaction into the scratch buffer. + // + // The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. + ext_gas_price(ctx) => { + ctx.scratch_buf = ctx.gas_meter.gas_price().encode(); + Ok(()) + }, + + // Stores the amount of gas left into the scratch buffer. + // + // The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. + ext_gas_left(ctx) => { + ctx.scratch_buf = ctx.gas_meter.gas_left().encode(); + Ok(()) + }, + + // Stores the balance of the current account into the scratch buffer. + // + // The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. + ext_balance(ctx) => { + ctx.scratch_buf = ctx.ext.balance().encode(); + Ok(()) + }, + + // Stores the value transferred along with this call or as endowment into the scratch buffer. + // + // The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten. + ext_value_transferred(ctx) => { + ctx.scratch_buf = ctx.ext.value_transferred().encode(); + Ok(()) + }, + // Returns the size of the input buffer. ext_input_size(ctx) -> u32 => { Ok(ctx.input_data.len() as u32)