Dispatch Calls to other modules (#1473)

* WIP

* Use system::Origin::Signed as an origin

* Add a vm test for ext_dispatch_call

* Take fee for dispatching a Call

# Conflicts:
#	node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm

* Clean & Rebuild

# Conflicts:
#	node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm

* Dispatch call test.

* Rebuild the runtime.

* Fix the length of the buffer.

* Rebuild binaries.
This commit is contained in:
Sergei Pepyakin
2019-01-22 13:10:14 +01:00
committed by Gav Wood
parent 22b65c9cb0
commit 58cd6530be
11 changed files with 298 additions and 35 deletions
+47 -1
View File
@@ -174,12 +174,15 @@ mod tests {
use std::collections::HashMap;
use substrate_primitives::H256;
use exec::{CallReceipt, Ext, InstantiateReceipt, EmptyOutputBuf};
use balances;
use gas::GasMeter;
use tests::Test;
use tests::{Test, Call};
use wabt;
use wasm::prepare::prepare_contract;
use CodeHash;
#[derive(Debug, PartialEq, Eq)]
struct DispatchEntry(Call);
#[derive(Debug, PartialEq, Eq)]
struct CreateEntry {
code_hash: H256,
@@ -199,6 +202,7 @@ mod tests {
storage: HashMap<Vec<u8>, Vec<u8>>,
creates: Vec<CreateEntry>,
transfers: Vec<TransferEntry>,
dispatches: Vec<DispatchEntry>,
next_account_id: u64,
}
impl Ext for MockExt {
@@ -248,6 +252,9 @@ mod tests {
output_data: Vec::new(),
})
}
fn note_dispatch_call(&mut self, call: Call) {
self.dispatches.push(DispatchEntry(call));
}
fn caller(&self) -> &u64 {
&42
}
@@ -942,6 +949,45 @@ mod tests {
.unwrap();
}
const CODE_DISPATCH_CALL: &str = r#"
(module
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))
(import "env" "memory" (memory 1 1))
(func (export "call")
(call $ext_dispatch_call
(i32.const 8) ;; Pointer to the start of encoded call buffer
(i32.const 13) ;; Length of the buffer
)
)
(func (export "deploy"))
(data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00")
)
"#;
#[test]
fn dispatch_call() {
// This test can fail due to the encoding changes. In case it becomes too annoying
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
let mut mock_ext = MockExt::default();
execute(
CODE_DISPATCH_CALL,
&[],
&mut Vec::new(),
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
)
.unwrap();
assert_eq!(
&mock_ext.dispatches,
&[DispatchEntry(
Call::Balances(balances::Call::set_balance(42, 1337.into(), 0.into())),
)]
);
}
const CODE_RETURN_FROM_START_FN: &str = r#"
(module
+31 -4
View File
@@ -21,11 +21,11 @@ use exec::{Ext, BalanceOf, VmExecResult, OutputBuf, EmptyOutputBuf, CallReceipt,
use rstd::prelude::*;
use rstd::mem;
use codec::{Decode, Encode};
use gas::{GasMeter, Token, GasMeterResult};
use gas::{GasMeter, Token, GasMeterResult, approx_gas_for_balance};
use runtime_primitives::traits::{As, CheckedMul, Bounded};
use sandbox;
use system;
use {Trait, CodeHash};
use {Trait, CodeHash, ComputeDispatchFee};
/// Enumerates all possible *special* trap conditions.
///
@@ -96,7 +96,7 @@ pub(crate) fn to_execution_result<E: Ext>(
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
pub enum RuntimeToken {
pub enum RuntimeToken<Gas> {
/// Explicit call to the `gas` function. Charge the gas meter
/// with the value provided.
Explicit(u32),
@@ -107,9 +107,11 @@ pub enum RuntimeToken {
/// The given number of bytes is read from the sandbox memory and
/// is returned as the return data buffer of the call.
ReturnData(u32),
/// Dispatch fee calculated by `T::ComputeDispatchFee`.
ComputedDispatchFee(Gas),
}
impl<T: Trait> Token<T> for RuntimeToken {
impl<T: Trait> Token<T> for RuntimeToken<T::Gas> {
type Metadata = Schedule<T::Gas>;
fn calculate_amount(&self, metadata: &Schedule<T::Gas>) -> T::Gas {
@@ -125,6 +127,7 @@ impl<T: Trait> Token<T> for RuntimeToken {
ReturnData(byte_count) => metadata
.return_data_per_byte_cost
.checked_mul(&<T::Gas as As<u32>>::sa(byte_count)),
ComputedDispatchFee(gas) => Some(gas),
};
value.unwrap_or_else(|| Bounded::max_value())
@@ -482,6 +485,30 @@ define_env!(Env, <E: Ext>,
Ok(())
},
// Decodes the given buffer as a `T::Call` and adds it to the list
// of to-be-dispatched calls.
//
// All calls made it to the top-level context will be dispatched before
// finishing the execution of the calling extrinsic.
ext_dispatch_call(ctx, call_ptr: u32, call_len: u32) => {
let call = {
let call_buf = read_sandbox_memory(ctx, call_ptr, call_len)?;
<<<E as Ext>::T as Trait>::Call>::decode(&mut &call_buf[..])
.ok_or_else(|| sandbox::HostError)?
};
// Charge gas for dispatching this call.
let fee = {
let balance_fee = <<E as Ext>::T as Trait>::ComputeDispatchFee::compute_dispatch_fee(&call);
approx_gas_for_balance::<<E as Ext>::T>(ctx.gas_meter.gas_price(), balance_fee)
};
charge_gas(&mut ctx.gas_meter, ctx.schedule, RuntimeToken::ComputedDispatchFee(fee))?;
ctx.ext.note_dispatch_call(call);
Ok(())
},
// Returns the size of the input buffer.
ext_input_size(ctx) -> u32 => {
Ok(ctx.input_data.len() as u32)