mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
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:
committed by
Gav Wood
parent
22b65c9cb0
commit
58cd6530be
+2
@@ -933,6 +933,7 @@ dependencies = [
|
||||
name = "sr-version"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1073,6 +1074,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
BIN
Binary file not shown.
@@ -206,9 +206,11 @@ impl treasury::Trait for Runtime {
|
||||
}
|
||||
|
||||
impl contract::Trait for Runtime {
|
||||
type Call = Call;
|
||||
type Event = Event;
|
||||
type Gas = u64;
|
||||
type DetermineContractAddress = contract::SimpleAddressDeterminator<Runtime>;
|
||||
type Event = Event;
|
||||
type ComputeDispatchFee = contract::DefaultDispatchFeeComputor<Runtime>;
|
||||
}
|
||||
|
||||
impl sudo::Trait for Runtime {
|
||||
|
||||
Generated
+2
@@ -1025,6 +1025,7 @@ dependencies = [
|
||||
name = "sr-version"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1451,6 +1452,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
BIN
Binary file not shown.
@@ -16,14 +16,15 @@
|
||||
|
||||
use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait};
|
||||
use account_db::{AccountDb, DirectAccountDb, OverlayAccountDb};
|
||||
use gas::{GasMeter, Token};
|
||||
use gas::{GasMeter, Token, approx_gas_for_balance};
|
||||
|
||||
use balances::{self, EnsureAccountLiquid};
|
||||
use rstd::prelude::*;
|
||||
use runtime_primitives::traits::{As, CheckedAdd, CheckedSub, Zero};
|
||||
use runtime_primitives::traits::{CheckedAdd, CheckedSub, Zero};
|
||||
|
||||
pub type BalanceOf<T> = <T as balances::Trait>::Balance;
|
||||
pub type AccountIdOf<T> = <T as system::Trait>::AccountId;
|
||||
pub type CallOf<T> = <T as Trait>::Call;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct InstantiateReceipt<AccountId> {
|
||||
@@ -77,12 +78,16 @@ pub trait Ext {
|
||||
empty_output_buf: EmptyOutputBuf,
|
||||
) -> Result<CallReceipt, &'static str>;
|
||||
|
||||
/// Notes a call dispatch.
|
||||
fn note_dispatch_call(&mut self, call: CallOf<Self::T>);
|
||||
|
||||
/// Returns a reference to the account id of the caller.
|
||||
fn caller(&self) -> &AccountIdOf<Self::T>;
|
||||
|
||||
/// Returns a reference to the account id of the current contract.
|
||||
fn address(&self) -> &AccountIdOf<Self::T>;
|
||||
|
||||
|
||||
/// Returns the balance of the current contract.
|
||||
///
|
||||
/// The `value_transferred` is already added.
|
||||
@@ -214,6 +219,7 @@ pub struct ExecutionContext<'a, T: Trait + 'a, V, L> {
|
||||
pub overlay: OverlayAccountDb<'a, T>,
|
||||
pub depth: usize,
|
||||
pub events: Vec<Event<T>>,
|
||||
pub calls: Vec<(T::AccountId, T::Call)>,
|
||||
pub config: &'a Config<T>,
|
||||
pub vm: &'a V,
|
||||
pub loader: &'a L,
|
||||
@@ -235,6 +241,7 @@ where
|
||||
depth: 0,
|
||||
overlay,
|
||||
events: Vec::new(),
|
||||
calls: Vec::new(),
|
||||
config: &cfg,
|
||||
vm: &vm,
|
||||
loader: &loader,
|
||||
@@ -247,6 +254,7 @@ where
|
||||
self_account: dest,
|
||||
depth: self.depth + 1,
|
||||
events: Vec::new(),
|
||||
calls: Vec::new(),
|
||||
config: self.config,
|
||||
vm: self.vm,
|
||||
loader: self.loader,
|
||||
@@ -276,7 +284,7 @@ where
|
||||
let dest_code_hash = self.overlay.get_code(&dest);
|
||||
let mut output_data = Vec::new();
|
||||
|
||||
let (change_set, events) = {
|
||||
let (change_set, events, calls) = {
|
||||
let mut overlay = OverlayAccountDb::new(&self.overlay);
|
||||
let mut nested = self.nested(overlay, dest.clone());
|
||||
|
||||
@@ -309,11 +317,12 @@ where
|
||||
.into_result()?;
|
||||
}
|
||||
|
||||
(nested.overlay.into_change_set(), nested.events)
|
||||
(nested.overlay.into_change_set(), nested.events, nested.calls)
|
||||
};
|
||||
|
||||
self.overlay.commit(change_set);
|
||||
self.events.extend(events);
|
||||
self.calls.extend(calls);
|
||||
|
||||
Ok(CallReceipt { output_data })
|
||||
}
|
||||
@@ -347,7 +356,7 @@ where
|
||||
return Err("contract already exists");
|
||||
}
|
||||
|
||||
let (change_set, events) = {
|
||||
let (change_set, events, calls) = {
|
||||
let mut overlay = OverlayAccountDb::new(&self.overlay);
|
||||
overlay.set_code(&dest, Some(code_hash.clone()));
|
||||
let mut nested = self.nested(overlay, dest.clone());
|
||||
@@ -381,11 +390,12 @@ where
|
||||
// Deposit an instantiation event.
|
||||
nested.events.push(RawEvent::Instantiated(self.self_account.clone(), dest.clone()));
|
||||
|
||||
(nested.overlay.into_change_set(), nested.events)
|
||||
(nested.overlay.into_change_set(), nested.events, nested.calls)
|
||||
};
|
||||
|
||||
self.overlay.commit(change_set);
|
||||
self.events.extend(events);
|
||||
self.calls.extend(calls);
|
||||
|
||||
Ok(InstantiateReceipt { address: dest })
|
||||
}
|
||||
@@ -416,11 +426,7 @@ impl<T: Trait> Token<T> for TransferFeeToken<T::Balance> {
|
||||
TransferFeeKind::AccountCreate => metadata.account_create_fee,
|
||||
TransferFeeKind::Transfer => metadata.transfer_fee,
|
||||
};
|
||||
|
||||
let amount_in_gas: T::Balance = balance_fee / self.gas_price;
|
||||
let amount_in_gas: T::Gas = <T::Gas as As<T::Balance>>::sa(amount_in_gas);
|
||||
|
||||
amount_in_gas
|
||||
approx_gas_for_balance::<T>(self.gas_price, balance_fee)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,6 +568,13 @@ where
|
||||
.call(to.clone(), value, gas_meter, input_data, empty_output_buf)
|
||||
}
|
||||
|
||||
/// Notes a call dispatch.
|
||||
fn note_dispatch_call(&mut self, call: CallOf<Self::T>) {
|
||||
self.ctx.calls.push(
|
||||
(self.ctx.self_account.clone(), call)
|
||||
);
|
||||
}
|
||||
|
||||
fn address(&self) -> &T::AccountId {
|
||||
&self.ctx.self_account
|
||||
}
|
||||
|
||||
@@ -247,6 +247,13 @@ pub fn refund_unused_gas<T: Trait>(transactor: &T::AccountId, gas_meter: GasMete
|
||||
<balances::Module<T>>::increase_total_stake_by(refund);
|
||||
}
|
||||
|
||||
/// A little handy utility for converting a value in balance units into approximitate value in gas units
|
||||
/// at the given gas price.
|
||||
pub fn approx_gas_for_balance<T: Trait>(gas_price: T::Balance, balance: T::Balance) -> T::Gas {
|
||||
let amount_in_gas: T::Balance = balance / gas_price;
|
||||
<T::Gas as As<T::Balance>>::sa(amount_in_gas)
|
||||
}
|
||||
|
||||
/// A simple utility macro that helps to match against a
|
||||
/// list of tokens.
|
||||
#[macro_export]
|
||||
|
||||
@@ -104,26 +104,41 @@ use rstd::prelude::*;
|
||||
use rstd::marker::PhantomData;
|
||||
use codec::{Codec, HasCompact};
|
||||
use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::dispatch::{Result, Dispatchable};
|
||||
use runtime_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap};
|
||||
use system::ensure_signed;
|
||||
use system::{ensure_signed, RawOrigin};
|
||||
use runtime_io::{blake2_256, twox_128};
|
||||
|
||||
pub type CodeHash<T> = <T as system::Trait>::Hash;
|
||||
|
||||
/// A function that generates an `AccountId` for a contract upon instantiation.
|
||||
pub trait ContractAddressFor<CodeHash, AccountId: Sized> {
|
||||
fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId;
|
||||
}
|
||||
|
||||
/// A function that returns the fee for dispatching a `Call`.
|
||||
pub trait ComputeDispatchFee<Call, Balance> {
|
||||
fn compute_dispatch_fee(call: &Call) -> Balance;
|
||||
}
|
||||
|
||||
pub trait Trait: balances::Trait {
|
||||
/// Function type to get the contract address given the creator.
|
||||
type DetermineContractAddress: ContractAddressFor<CodeHash<Self>, Self::AccountId>;
|
||||
/// The outer call dispatch type.
|
||||
type Call: Parameter + Dispatchable<Origin=<Self as system::Trait>::Origin>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
|
||||
// As<u32> is needed for wasm-utils
|
||||
type Gas: Parameter + Default + Codec + SimpleArithmetic + Bounded + Copy + As<Self::Balance> + As<u64> + As<u32>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
/// A function type to get the contract address given the creator.
|
||||
type DetermineContractAddress: ContractAddressFor<CodeHash<Self>, Self::AccountId>;
|
||||
|
||||
pub trait ContractAddressFor<CodeHash, AccountId: Sized> {
|
||||
fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId;
|
||||
/// A function type that computes the fee for dispatching the given `Call`.
|
||||
///
|
||||
/// It is recommended (though not required) for this function to return a fee that would be taken
|
||||
/// by executive module for regular dispatch.
|
||||
type ComputeDispatchFee: ComputeDispatchFee<Self::Call, <Self as balances::Trait>::Balance>;
|
||||
}
|
||||
|
||||
/// Simple contract address determintator.
|
||||
@@ -133,7 +148,6 @@ pub trait ContractAddressFor<CodeHash, AccountId: Sized> {
|
||||
///
|
||||
/// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)`
|
||||
pub struct SimpleAddressDeterminator<T: Trait>(PhantomData<T>);
|
||||
|
||||
impl<T: Trait> ContractAddressFor<CodeHash<T>, T::AccountId> for SimpleAddressDeterminator<T>
|
||||
where
|
||||
T::AccountId: From<T::Hash> + AsRef<[u8]>
|
||||
@@ -150,9 +164,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// The default dispatch fee computor computes the fee in the same way that
|
||||
/// implementation of `MakePayment` for balances module does.
|
||||
pub struct DefaultDispatchFeeComputor<T: Trait>(PhantomData<T>);
|
||||
impl<T: Trait> ComputeDispatchFee<T::Call, T::Balance> for DefaultDispatchFeeComputor<T> {
|
||||
fn compute_dispatch_fee(call: &T::Call) -> T::Balance {
|
||||
let encoded_len = codec::Encode::encode(&call).len();
|
||||
let base_fee = <balances::Module<T>>::transaction_base_fee();
|
||||
let byte_fee = <balances::Module<T>>::transaction_byte_fee();
|
||||
base_fee + byte_fee * <T::Balance as As<u64>>::sa(encoded_len as u64)
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
/// Contracts module.
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
|
||||
fn deposit_event<T>() = default;
|
||||
|
||||
/// Updates the schedule for metering contracts.
|
||||
@@ -231,6 +257,12 @@ decl_module! {
|
||||
// can alter the balance of the caller.
|
||||
gas::refund_unused_gas::<T>(&origin, gas_meter);
|
||||
|
||||
// Dispatch every recorded call with an appropriate origin.
|
||||
ctx.calls.into_iter().for_each(|(who, call)| {
|
||||
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
|
||||
Self::deposit_event(RawEvent::Dispatched(who, result.is_ok()));
|
||||
});
|
||||
|
||||
result.map(|_| ())
|
||||
}
|
||||
|
||||
@@ -280,6 +312,12 @@ decl_module! {
|
||||
// can alter the balance of the caller.
|
||||
gas::refund_unused_gas::<T>(&origin, gas_meter);
|
||||
|
||||
// Dispatch every recorded call with an appropriate origin.
|
||||
ctx.calls.into_iter().for_each(|(who, call)| {
|
||||
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
|
||||
Self::deposit_event(RawEvent::Dispatched(who, result.is_ok()));
|
||||
});
|
||||
|
||||
result.map(|_| ())
|
||||
}
|
||||
|
||||
@@ -307,6 +345,10 @@ decl_event! {
|
||||
|
||||
/// Triggered when the current schedule is updated.
|
||||
ScheduleUpdated(u32),
|
||||
|
||||
/// A call was dispatched from the given account. The bool signals whether it was
|
||||
/// successful execution or not.
|
||||
Dispatched(AccountId, bool),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,17 +25,13 @@ use runtime_primitives::traits::{BlakeTwo256, IdentityLookup};
|
||||
use runtime_primitives::BuildStorage;
|
||||
use runtime_support::{StorageMap, StorageDoubleMap};
|
||||
use substrate_primitives::{Blake2Hasher};
|
||||
use system::{Phase, EventRecord};
|
||||
use system::{ensure_signed, Phase, EventRecord};
|
||||
use wabt;
|
||||
use {
|
||||
balances, runtime_io, system, ContractAddressFor, GenesisConfig, Module, RawEvent, StorageOf,
|
||||
Trait,
|
||||
balances, runtime_io, system, ComputeDispatchFee, ContractAddressFor, GenesisConfig, Module, RawEvent, StorageOf,
|
||||
Trait
|
||||
};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
mod contract {
|
||||
// Re-export contents of the root. This basically
|
||||
// needs to give a name for the current crate.
|
||||
@@ -47,6 +43,15 @@ impl_outer_event! {
|
||||
balances<T>, contract<T>,
|
||||
}
|
||||
}
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test { }
|
||||
}
|
||||
impl_outer_dispatch! {
|
||||
pub enum Call for Test where origin: Origin {
|
||||
balances::Balances,
|
||||
contract::Contract,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Test;
|
||||
@@ -71,9 +76,11 @@ impl balances::Trait for Test {
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Call = Call;
|
||||
type Gas = u64;
|
||||
type DetermineContractAddress = DummyContractAddressFor;
|
||||
type Event = MetaEvent;
|
||||
type ComputeDispatchFee = DummyComputeDispatchFee;
|
||||
}
|
||||
|
||||
type Balances = balances::Module<Test>;
|
||||
@@ -87,6 +94,13 @@ impl ContractAddressFor<H256, u64> for DummyContractAddressFor {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyComputeDispatchFee;
|
||||
impl ComputeDispatchFee<Call, u64> for DummyComputeDispatchFee {
|
||||
fn compute_dispatch_fee(call: &Call) -> u64 {
|
||||
69
|
||||
}
|
||||
}
|
||||
|
||||
const ALICE: u64 = 1;
|
||||
const BOB: u64 = 2;
|
||||
const CHARLIE: u64 = 3;
|
||||
@@ -298,3 +312,111 @@ fn instantiate_and_call() {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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 11) ;; Length of the buffer
|
||||
)
|
||||
)
|
||||
(func (export "deploy"))
|
||||
|
||||
(data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8")
|
||||
)
|
||||
"#;
|
||||
const HASH_DISPATCH_CALL: [u8; 32] = hex!("49dfdcaf9c1553be10634467e95b8e71a3bc15a4f8bf5563c0312b0902e0afb9");
|
||||
|
||||
#[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 encoded = codec::Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50.into())));
|
||||
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
|
||||
|
||||
let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL).unwrap();
|
||||
|
||||
with_externalities(
|
||||
&mut ExtBuilder::default().existential_deposit(50).build(),
|
||||
|| {
|
||||
Balances::set_free_balance(&ALICE, 1_000_000);
|
||||
Balances::increase_total_stake_by(1_000_000);
|
||||
|
||||
assert_ok!(Contract::put_code(
|
||||
Origin::signed(ALICE),
|
||||
100_000.into(),
|
||||
wasm,
|
||||
));
|
||||
|
||||
// Let's keep this assert even though it's redundant. If you ever need to update the
|
||||
// wasm source this test will fail and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())),
|
||||
},
|
||||
]);
|
||||
|
||||
assert_ok!(Contract::create(
|
||||
Origin::signed(ALICE),
|
||||
100.into(),
|
||||
100_000.into(),
|
||||
HASH_DISPATCH_CALL.into(),
|
||||
vec![],
|
||||
));
|
||||
|
||||
assert_ok!(Contract::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB, // newly created account
|
||||
0.into(),
|
||||
100_000.into(),
|
||||
vec![],
|
||||
));
|
||||
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())),
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
balances::RawEvent::NewAccount(BOB, 100)
|
||||
)
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100))
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB))
|
||||
},
|
||||
|
||||
// Dispatching the call.
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
balances::RawEvent::NewAccount(CHARLIE, 50)
|
||||
)
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0)
|
||||
)
|
||||
},
|
||||
|
||||
// Event emited as a result of dispatch.
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Dispatched(BOB, true))
|
||||
}
|
||||
]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user