Refactor: simplify module call definitions (#24)

* Refactor: simplify module call definitions

* Fix compilation errors after merge

* Add missing comments and remove unused imports

* Now it compiles
This commit is contained in:
Andrew Jones
2019-11-21 17:53:45 +00:00
committed by GitHub
parent 5242939f4d
commit 17abfb49b2
6 changed files with 136 additions and 323 deletions
+1 -11
View File
@@ -1,8 +1,4 @@
use parity_scale_codec::{ use parity_scale_codec::Encode;
Encode,
EncodeAsRef,
HasCompact,
};
#[derive(Clone)] #[derive(Clone)]
pub struct Encoded(pub Vec<u8>); pub struct Encoded(pub Vec<u8>);
@@ -12,9 +8,3 @@ impl Encode for Encoded {
self.0.to_owned() self.0.to_owned()
} }
} }
pub fn compact<T: HasCompact>(t: T) -> Encoded {
let encodable: <<T as HasCompact>::Type as EncodeAsRef<'_, T>>::RefType =
From::from(&t);
Encoded(encodable.encode())
}
+27 -57
View File
@@ -31,6 +31,7 @@ use metadata::Metadata;
use parity_scale_codec::{ use parity_scale_codec::{
Codec, Codec,
Decode, Decode,
Encode,
}; };
use runtime_primitives::{ use runtime_primitives::{
generic::UncheckedExtrinsic, generic::UncheckedExtrinsic,
@@ -61,15 +62,14 @@ use crate::{
DefaultExtra, DefaultExtra,
SignedExtra, SignedExtra,
}, },
metadata::MetadataError,
palette::{ palette::{
Call,
balances::Balances, balances::Balances,
system::{ system::{
System, System,
SystemEvent, SystemEvent,
SystemStore, SystemStore,
}, },
ModuleCalls,
}, },
rpc::{ rpc::{
BlockNumber, BlockNumber,
@@ -266,30 +266,21 @@ impl<T: System + Balances + 'static, S: 'static> Client<T, S> {
runtime_version, runtime_version,
genesis_hash, genesis_hash,
signer, signer,
call: None,
marker: PhantomData,
} }
}) })
} }
} }
/// The extrinsic builder is ready to finalize construction.
pub enum Valid {}
/// The extrinsic builder is not ready to finalize construction.
pub enum Invalid {}
/// Transaction builder. /// Transaction builder.
pub struct XtBuilder<T: System, P, S, V = Invalid> { pub struct XtBuilder<T: System, P, S> {
client: Client<T, S>, client: Client<T, S>,
nonce: T::Index, nonce: T::Index,
runtime_version: RuntimeVersion, runtime_version: RuntimeVersion,
genesis_hash: T::Hash, genesis_hash: T::Hash,
signer: P, signer: P,
call: Option<Result<Encoded, MetadataError>>,
marker: PhantomData<fn() -> V>,
} }
impl<T: System + Balances + 'static, P, S: 'static, V> XtBuilder<T, P, S, V> impl<T: System + Balances + 'static, P, S: 'static> XtBuilder<T, P, S>
where where
P: Pair, P: Pair,
{ {
@@ -304,42 +295,19 @@ where
} }
/// Sets the nonce to a new value. /// Sets the nonce to a new value.
pub fn set_nonce(&mut self, nonce: T::Index) -> &mut XtBuilder<T, P, S, V> { pub fn set_nonce(&mut self, nonce: T::Index) -> &mut XtBuilder<T, P, S> {
self.nonce = nonce; self.nonce = nonce;
self self
} }
/// Increment the nonce /// Increment the nonce
pub fn increment_nonce(&mut self) -> &mut XtBuilder<T, P, S, V> { pub fn increment_nonce(&mut self) -> &mut XtBuilder<T, P, S> {
self.set_nonce(self.nonce() + 1.into()); self.set_nonce(self.nonce() + 1.into());
self self
} }
/// Sets the module call to a new value
pub fn set_call<F>(&self, module: &'static str, f: F) -> XtBuilder<T, P, S, Valid>
where
F: FnOnce(ModuleCalls<T, P>) -> Result<Encoded, MetadataError>,
{
let call = self
.metadata()
.module(module)
.and_then(|module| f(ModuleCalls::new(module)))
.map_err(Into::into);
XtBuilder {
client: self.client.clone(),
nonce: self.nonce.clone(),
runtime_version: self.runtime_version.clone(),
genesis_hash: self.genesis_hash.clone(),
signer: self.signer.clone(),
call: Some(call),
marker: PhantomData,
}
}
} }
impl<T: System + Balances + Send + Sync + 'static, P, S: 'static> impl<T: System + Balances + Send + Sync + 'static, P, S: 'static> XtBuilder<T, P, S>
XtBuilder<T, P, S, Valid>
where where
P: Pair, P: Pair,
S: Verify + Codec + From<P::Signature>, S: Verify + Codec + From<P::Signature>,
@@ -347,8 +315,9 @@ where
T::Address: From<T::AccountId>, T::Address: From<T::AccountId>,
{ {
/// Creates and signs an Extrinsic for the supplied `Call` /// Creates and signs an Extrinsic for the supplied `Call`
pub fn create_and_sign( pub fn create_and_sign<C>(
&self, &self,
call: Call<C>,
) -> Result< ) -> Result<
UncheckedExtrinsic< UncheckedExtrinsic<
T::Address, T::Address,
@@ -357,15 +326,18 @@ where
<DefaultExtra<T> as SignedExtra<T>>::Extra, <DefaultExtra<T> as SignedExtra<T>>::Extra,
>, >,
Error, Error,
> { >
where
C: parity_scale_codec::Encode,
{
let signer = self.signer.clone(); let signer = self.signer.clone();
let account_nonce = self.nonce.clone(); let account_nonce = self.nonce.clone();
let version = self.runtime_version.spec_version; let version = self.runtime_version.spec_version;
let genesis_hash = self.genesis_hash.clone(); let genesis_hash = self.genesis_hash.clone();
let call = self let call = self
.call .metadata()
.clone() .module(&call.module)
.expect("A Valid extrinisic builder has a call; qed")?; .and_then(|module| module.call(&call.function, call.args))?;
log::info!( log::info!(
"Creating Extrinsic with genesis hash {:?} and account nonce {:?}", "Creating Extrinsic with genesis hash {:?} and account nonce {:?}",
@@ -379,9 +351,9 @@ where
} }
/// Submits a transaction to the chain. /// Submits a transaction to the chain.
pub fn submit(&self) -> impl Future<Item = T::Hash, Error = Error> { pub fn submit<C: Encode>(&self, call: Call<C>) -> impl Future<Item = T::Hash, Error = Error> {
let cli = self.client.connect(); let cli = self.client.connect();
self.create_and_sign() self.create_and_sign(call)
.into_future() .into_future()
.map_err(Into::into) .map_err(Into::into)
.and_then(move |extrinsic| { .and_then(move |extrinsic| {
@@ -390,8 +362,9 @@ where
} }
/// Submits transaction to the chain and watch for events. /// Submits transaction to the chain and watch for events.
pub fn submit_and_watch( pub fn submit_and_watch<C: Encode>(
&self, &self,
call: Call<C>
) -> impl Future<Item = ExtrinsicSuccess<T>, Error = Error> { ) -> impl Future<Item = ExtrinsicSuccess<T>, Error = Error> {
let cli = self.client.connect(); let cli = self.client.connect();
let metadata = self.client.metadata().clone(); let metadata = self.client.metadata().clone();
@@ -399,7 +372,7 @@ where
.into_future() .into_future()
.map_err(Into::into); .map_err(Into::into);
self.create_and_sign() self.create_and_sign(call)
.into_future() .into_future()
.map_err(Into::into) .map_err(Into::into)
.join(decoder) .join(decoder)
@@ -419,9 +392,7 @@ mod tests {
balances::{ balances::{
Balances, Balances,
BalancesStore, BalancesStore,
BalancesXt,
}, },
contracts::ContractsXt,
}, },
DefaultNodeRuntime as Runtime, DefaultNodeRuntime as Runtime,
}; };
@@ -454,15 +425,14 @@ mod tests {
let dest = AccountKeyring::Bob.to_account_id(); let dest = AccountKeyring::Bob.to_account_id();
let transfer = xt let transfer = xt
.balances(|calls| calls.transfer(dest.clone().into(), 10_000)) .submit(balances::transfer::<Runtime>(dest.clone().into(), 10_000));
.submit();
rt.block_on(transfer).unwrap(); rt.block_on(transfer).unwrap();
// check that nonce is handled correctly // check that nonce is handled correctly
let transfer = xt let transfer = xt
.increment_nonce() .increment_nonce()
.balances(|calls| calls.transfer(dest.clone().into(), 10_000)) .submit(balances::transfer::<Runtime>(dest.clone().into(), 10_000));
.submit();
rt.block_on(transfer).unwrap(); rt.block_on(transfer).unwrap();
} }
@@ -483,8 +453,7 @@ mod tests {
let wasm = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); let wasm = wabt::wat2wasm(CONTRACT).expect("invalid wabt");
let put_code = xt let put_code = xt
.contracts(|call| call.put_code(500_000, wasm)) .submit_and_watch(contracts::put_code(500_000, wasm));
.submit_and_watch();
let success = rt let success = rt
.block_on(put_code) .block_on(put_code)
@@ -561,8 +530,9 @@ mod tests {
let transfer = pallet_balances::Call::transfer(address.clone(), amount); let transfer = pallet_balances::Call::transfer(address.clone(), amount);
let call = node_runtime::Call::Balances(transfer); let call = node_runtime::Call::Balances(transfer);
let subxt_transfer = crate::palette::balances::transfer::<Runtime>(address, amount);
let call2 = balances let call2 = balances
.call("transfer", (address, codec::compact(amount))) .call("transfer", subxt_transfer.args)
.unwrap(); .unwrap();
assert_eq!(call.encode().to_vec(), call2.0); assert_eq!(call.encode().to_vec(), call2.0);
+21 -66
View File
@@ -1,34 +1,24 @@
//! Implements support for the pallet_balances module. //! Implements support for the pallet_balances module.
use crate::{ use crate::{
codec::{
compact,
Encoded,
},
error::Error, error::Error,
metadata::MetadataError,
palette::{ palette::{
Call,
system::System, system::System,
ModuleCalls,
}, },
Client, Client,
Valid,
XtBuilder,
}; };
use futures::future::{ use futures::future::{
self, self,
Future, Future,
}; };
use parity_scale_codec::Codec; use parity_scale_codec::{Encode, Codec};
use runtime_primitives::traits::{ use runtime_primitives::traits::{
IdentifyAccount,
MaybeSerialize, MaybeSerialize,
Member, Member,
SimpleArithmetic, SimpleArithmetic,
Verify,
}; };
use runtime_support::Parameter; use runtime_support::Parameter;
use std::fmt::Debug; use std::fmt::Debug;
use substrate_primitives::Pair;
/// The subset of the `pallet_balances::Trait` that a client must implement. /// The subset of the `pallet_balances::Trait` that a client must implement.
pub trait Balances: System { pub trait Balances: System {
@@ -101,61 +91,26 @@ impl<T: Balances + 'static, S: 'static> BalancesStore for Client<T, S> {
} }
} }
/// The Balances extension trait for the XtBuilder. const MODULE: &str = "Balances";
pub trait BalancesXt { const TRANSFER: &str = "transfer";
/// Balances type.
type Balances: Balances;
/// Keypair type
type Pair: Pair;
/// Signature type
type Signature: Verify;
/// Create a call for the pallet balances module /// Arguments for transferring a balance
fn balances<F>( #[derive(Encode)]
&self, pub struct TransferArgs<T: Balances> {
f: F, to: <T as System>::Address,
) -> XtBuilder<Self::Balances, Self::Pair, Self::Signature, Valid> #[codec(compact)]
where amount: <T as Balances>::Balance
F: FnOnce(
ModuleCalls<Self::Balances, Self::Pair>,
) -> Result<Encoded, MetadataError>;
} }
impl<T: Balances + 'static, P, S: 'static, V> BalancesXt for XtBuilder<T, P, S, V> /// Transfer some liquid free balance to another account.
where ///
P: Pair, /// `transfer` will set the `FreeBalance` of the sender and receiver.
S: Verify, /// It will decrease the total issuance of the system by the `TransferFee`.
S::Signer: From<P::Public> + IdentifyAccount<AccountId = T::AccountId>, /// If the sender's account is below the existential deposit as a result
{ /// of the transfer, the account will be reaped.
type Balances = T; pub fn transfer<T: Balances>(
type Pair = P; to: <T as System>::Address,
type Signature = S; amount: <T as Balances>::Balance,
) -> Call<TransferArgs<T>> {
fn balances<F>(&self, f: F) -> XtBuilder<T, P, S, Valid> Call::new(MODULE, TRANSFER, TransferArgs { to, amount })
where
F: FnOnce(
ModuleCalls<Self::Balances, Self::Pair>,
) -> Result<Encoded, MetadataError>,
{
self.set_call("Balances", f)
}
}
impl<T: Balances + 'static, P> ModuleCalls<T, P>
where
P: Pair,
{
/// Transfer some liquid free balance to another account.
///
/// `transfer` will set the `FreeBalance` of the sender and receiver.
/// It will decrease the total issuance of the system by the `TransferFee`.
/// If the sender's account is below the existential deposit as a result
/// of the transfer, the account will be reaped.
pub fn transfer(
self,
to: <T as System>::Address,
amount: <T as Balances>::Balance,
) -> Result<Encoded, MetadataError> {
self.module.call("transfer", (to, compact(amount)))
}
} }
+71 -123
View File
@@ -1,23 +1,17 @@
//! Implements support for the pallet_contracts module. //! Implements support for the pallet_contracts module.
use crate::{ use crate::{
codec::{
compact,
Encoded,
},
metadata::MetadataError,
palette::{ palette::{
Call,
balances::Balances, balances::Balances,
system::System, system::System,
ModuleCalls,
}, },
Valid,
XtBuilder,
}; };
use runtime_primitives::traits::{ use parity_scale_codec::Encode;
IdentifyAccount,
Verify, const MODULE: &str = "Contracts";
}; const PUT_CODE: &str = "put_code";
use substrate_primitives::Pair; const CREATE: &str = "create";
const CALL: &str = "call";
/// Gas units are chosen to be represented by u64 so that gas metering /// Gas units are chosen to be represented by u64 so that gas metering
/// instructions can operate on them efficiently. /// instructions can operate on them efficiently.
@@ -26,122 +20,76 @@ pub type Gas = u64;
/// The subset of the `pallet_contracts::Trait` that a client must implement. /// The subset of the `pallet_contracts::Trait` that a client must implement.
pub trait Contracts: System + Balances {} pub trait Contracts: System + Balances {}
/// Blanket impl for using existing runtime types /// Arguments for uploading contract code to the chain
impl< #[derive(Encode)]
T: pallet_contracts::Trait pub struct PutCodeArgs {
+ palette_system::Trait #[codec(compact)]
+ pallet_balances::Trait gas_limit: Gas,
+ std::fmt::Debug, code: Vec<u8>,
> Contracts for T
where
<T as palette_system::Trait>::Header: serde::de::DeserializeOwned,
{
} }
/// The Contracts extension trait for the XtBuilder. /// Arguments for creating an instance of a contract
pub trait ContractsXt { #[derive(Encode)]
/// Contracts type. pub struct CreateArgs<T: Contracts> {
type Contracts: Contracts; endowment: <T as Balances>::Balance,
/// Key Pair Type #[codec(compact)]
type Pair: Pair; gas_limit: Gas,
/// Signature type code_hash: <T as System>::Hash,
type Signature: Verify; data: Vec<u8>,
/// Create a call for the pallet contracts module
fn contracts<F>(
&self,
f: F,
) -> XtBuilder<Self::Contracts, Self::Pair, Self::Signature, Valid>
where
F: FnOnce(
ModuleCalls<Self::Contracts, Self::Pair>,
) -> Result<Encoded, MetadataError>;
} }
impl<T: Contracts + 'static, P, S: 'static, V> ContractsXt for XtBuilder<T, P, S, V> /// Arguments for calling a contract
where #[derive(Encode)]
P: Pair, pub struct CallArgs<T: Contracts> {
S: Verify, dest: <T as System>::Address,
S::Signer: From<P::Public> + IdentifyAccount<AccountId = T::AccountId>, value: <T as Balances>::Balance,
{ #[codec(compact)]
type Contracts = T; gas_limit: Gas,
type Pair = P; data: Vec<u8>,
type Signature = S;
fn contracts<F>(&self, f: F) -> XtBuilder<T, P, S, Valid>
where
F: FnOnce(
ModuleCalls<Self::Contracts, Self::Pair>,
) -> Result<Encoded, MetadataError>,
{
self.set_call("Contracts", f)
}
} }
impl<T: Contracts + 'static, P> ModuleCalls<T, P> /// Stores the given binary Wasm code into the chain's storage and returns
where /// its `codehash`.
P: Pair, /// You can instantiate contracts only with stored code.
{ pub fn put_code(gas_limit: Gas, code: Vec<u8>) -> Call<PutCodeArgs> {
/// Stores the given binary Wasm code into the chain's storage and returns Call::new(MODULE, PUT_CODE, PutCodeArgs { gas_limit, code })
/// its `codehash`.
/// You can instantiate contracts only with stored code.
pub fn put_code(
&self,
gas_limit: Gas,
code: Vec<u8>,
) -> Result<Encoded, MetadataError> {
self.module.call("put_code", (compact(gas_limit), code))
}
/// Creates a new contract from the `codehash` generated by `put_code`,
/// optionally transferring some balance.
///
/// Creation is executed as follows:
///
/// - The destination address is computed based on the sender and hash of
/// the code.
/// - The smart-contract account is created at the computed address.
/// - The `ctor_code` is executed in the context of the newly-created
/// account. Buffer returned after the execution is saved as the `code`
/// of the account. That code will be invoked upon any call received by
/// this account.
/// - The contract is initialized.
pub fn create(
&self,
endowment: <T as Balances>::Balance,
gas_limit: Gas,
code_hash: <T as System>::Hash,
data: Vec<u8>,
) -> Result<Encoded, MetadataError> {
self.module.call(
"create",
(compact(endowment), compact(gas_limit), code_hash, data),
)
}
/// Makes a call to an account, optionally transferring some balance.
///
/// * If the account is a smart-contract account, the associated code will
/// be executed and any value will be transferred.
/// * If the account is a regular account, any value will be transferred.
/// * If no account exists and the call value is not less than
/// `existential_deposit`, a regular account will be created and any value
/// will be transferred.
pub fn call(
&self,
dest: <T as System>::Address,
value: <T as Balances>::Balance,
gas_limit: Gas,
data: Vec<u8>,
) -> Result<Encoded, MetadataError> {
self.module
.call("call", (dest, compact(value), compact(gas_limit), data))
}
} }
/// Contracts Events /// Creates a new contract from the `codehash` generated by `put_code`,
#[derive(parity_scale_codec::Decode)] /// optionally transferring some balance.
pub enum Event<T: System> { ///
/// Contract code stored /// Creation is executed as follows:
CodeStored(T::Hash), ///
/// - The destination address is computed based on the sender and hash of
/// the code.
/// - The smart-contract account is created at the computed address.
/// - The `ctor_code` is executed in the context of the newly-created
/// account. Buffer returned after the execution is saved as the `code`https://www.bbc.co.uk/
/// of the account. That code will be invoked upon any call received by
/// this account.
/// - The contract is initialized.
pub fn create<T: Contracts>(
endowment: <T as Balances>::Balance,
gas_limit: Gas,
code_hash: <T as System>::Hash,
data: Vec<u8>,
) -> Call<CreateArgs<T>> {
Call::new(MODULE, CREATE, CreateArgs { endowment, gas_limit, code_hash, data })
}
/// Makes a call to an account, optionally transferring some balance.
///
/// * If the account is a smart-contract account, the associated code will
/// be executed and any value will be transferred.
/// * If the account is a regular account, any value will be transferred.
/// * If no account exists and the call value is not less than
/// `existential_deposit`, a regular account will be created and any value
/// will be transferred.
pub fn call<T: Contracts>(
dest: <T as System>::Address,
value: <T as Balances>::Balance,
gas_limit: Gas,
data: Vec<u8>,
) -> Call<CallArgs<T>> {
Call::new(MODULE, CALL, CallArgs { dest, value, gas_limit, data })
} }
+8 -12
View File
@@ -1,24 +1,20 @@
//! Implements support for built-in runtime modules. //! Implements support for built-in runtime modules.
use crate::metadata::ModuleMetadata; use parity_scale_codec::Encode;
use std::marker::PhantomData;
pub mod balances; pub mod balances;
pub mod contracts; pub mod contracts;
pub mod system; pub mod system;
/// Creates module calls /// Creates module calls
pub struct ModuleCalls<T, P> { pub struct Call<T: Encode> {
module: ModuleMetadata, pub module: &'static str,
marker: PhantomData<fn() -> (T, P)>, pub function: &'static str,
pub args: T,
} }
impl<T, P> ModuleCalls<T, P> { impl<T: Encode> Call<T> {
/// Create new module calls pub fn new(module: &'static str, function: &'static str, args: T) -> Self {
pub fn new(module: &ModuleMetadata) -> Self { Call { module, function, args }
ModuleCalls::<T, P> {
module: module.clone(),
marker: PhantomData,
}
} }
} }
+8 -54
View File
@@ -1,15 +1,11 @@
//! Implements support for the palette_system module. //! Implements support for the palette_system module.
use crate::{ use crate::{
codec::Encoded,
error::Error, error::Error,
metadata::MetadataError,
palette::{ palette::{
Call,
balances::Balances, balances::Balances,
ModuleCalls,
}, },
Client, Client,
Valid,
XtBuilder,
}; };
use futures::future::{ use futures::future::{
self, self,
@@ -21,7 +17,6 @@ use runtime_primitives::traits::{
CheckEqual, CheckEqual,
Hash, Hash,
Header, Header,
IdentifyAccount,
MaybeDisplay, MaybeDisplay,
MaybeSerialize, MaybeSerialize,
MaybeSerializeDeserialize, MaybeSerializeDeserialize,
@@ -29,12 +24,10 @@ use runtime_primitives::traits::{
SimpleArithmetic, SimpleArithmetic,
SimpleBitOps, SimpleBitOps,
StaticLookup, StaticLookup,
Verify,
}; };
use runtime_support::Parameter; use runtime_support::Parameter;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::fmt::Debug; use std::fmt::Debug;
use substrate_primitives::Pair;
/// The subset of the `palette::Trait` that a client must implement. /// The subset of the `palette::Trait` that a client must implement.
pub trait System: 'static + Eq + Clone + Debug { pub trait System: 'static + Eq + Clone + Debug {
@@ -145,54 +138,15 @@ impl<T: System + Balances + 'static, S: 'static> SystemStore for Client<T, S> {
} }
} }
/// The System extension trait for the XtBuilder. const MODULE: &str = "System";
pub trait SystemXt { const SET_CODE: &str = "set_code";
/// System type.
type System: System;
/// Keypair type
type Pair: Pair;
/// Signature type
type Signature: Verify;
/// Create a call for the pallet system module /// Arguments for updating the runtime code
fn system<F>( pub type SetCode = Vec<u8>;
&self,
f: F,
) -> XtBuilder<Self::System, Self::Pair, Self::Signature, Valid>
where
F: FnOnce(
ModuleCalls<Self::System, Self::Pair>,
) -> Result<Encoded, MetadataError>;
}
impl<T: System + Balances + 'static, P, S: 'static, V> SystemXt for XtBuilder<T, P, S, V> /// Sets the new code.
where pub fn set_code(code: Vec<u8>) -> Call<SetCode> {
P: Pair, Call::new(MODULE, SET_CODE, code)
S: Verify,
S::Signer: From<P::Public> + IdentifyAccount<AccountId = T::AccountId>,
{
type System = T;
type Pair = P;
type Signature = S;
fn system<F>(&self, f: F) -> XtBuilder<T, P, S, Valid>
where
F: FnOnce(
ModuleCalls<Self::System, Self::Pair>,
) -> Result<Encoded, MetadataError>,
{
self.set_call("System", f)
}
}
impl<T: System + 'static, P> ModuleCalls<T, P>
where
P: Pair,
{
/// Sets the new code.
pub fn set_code(&self, code: Vec<u8>) -> Result<Encoded, MetadataError> {
self.module.call("set_code", code)
}
} }
/// Event for the System module. /// Event for the System module.