Double map and plain storage support, introduce macros (#93)

* Support custom clients.

* Simplify trait bounds.

* Plain and double map storage support.

* Simplify more trait bounds.

* Add proc macro.

* Add Call, Event and Store traits.

* Update proc-macros.

* Add with_system for proc-macro.

* proc-macro: test: support signature and extra fields.

* proc-macro: test: support sharing state accross steps.

* proc-macro: test: fetch state sequentially.

* Elide lifetimes.

* Add test for plain storage.

* Run rustfmt.
This commit is contained in:
David Craven
2020-04-28 21:04:26 +02:00
committed by GitHub
parent 216b5614dd
commit 6f27489378
20 changed files with 1924 additions and 501 deletions
+57 -16
View File
@@ -16,15 +16,24 @@
//! Implements support for the pallet_balances module.
use crate::frame::{
system::System,
Call,
use crate::{
frame::{
system::System,
Call,
Event,
Store,
},
metadata::{
Metadata,
MetadataError,
},
};
use codec::{
Decode,
Encode,
};
use frame_support::Parameter;
use sp_core::storage::StorageKey;
use sp_runtime::traits::{
AtLeast32Bit,
MaybeSerialize,
@@ -32,6 +41,8 @@ use sp_runtime::traits::{
};
use std::fmt::Debug;
const MODULE: &str = "Balances";
/// The subset of the `pallet_balances::Trait` that a client must implement.
pub trait Balances: System {
/// The balance of an account.
@@ -70,15 +81,22 @@ pub struct AccountData<Balance> {
pub fee_frozen: Balance,
}
const MODULE: &str = "Balances";
const TRANSFER: &str = "transfer";
/// The total issuance of the balances module.
#[derive(Encode)]
pub struct TotalIssuance<T>(pub core::marker::PhantomData<T>);
/// Arguments for transferring a balance
#[derive(codec::Encode)]
pub struct TransferArgs<T: Balances> {
to: <T as System>::Address,
#[codec(compact)]
amount: <T as Balances>::Balance,
impl<T: Balances> Store<T> for TotalIssuance<T> {
const MODULE: &'static str = MODULE;
const FIELD: &'static str = "TotalIssuance";
type Returns = T::Balance;
fn key(&self, metadata: &Metadata) -> Result<StorageKey, MetadataError> {
Ok(metadata
.module(Self::MODULE)?
.storage(Self::FIELD)?
.plain()?
.key())
}
}
/// Transfer some liquid free balance to another account.
@@ -87,9 +105,32 @@ pub struct TransferArgs<T: Balances> {
/// 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<T: Balances>(
to: <T as System>::Address,
amount: <T as Balances>::Balance,
) -> Call<TransferArgs<T>> {
Call::new(MODULE, TRANSFER, TransferArgs { to, amount })
#[derive(Encode)]
pub struct TransferCall<'a, T: Balances> {
/// Destination of the transfer.
pub to: &'a <T as System>::Address,
/// Amount to transfer.
#[codec(compact)]
pub amount: T::Balance,
}
impl<'a, T: Balances> Call<T> for TransferCall<'a, T> {
const MODULE: &'static str = MODULE;
const FUNCTION: &'static str = "transfer";
}
/// Transfer event.
#[derive(Debug, Decode, Eq, PartialEq)]
pub struct TransferEvent<T: Balances> {
/// Account balance was transfered from.
pub from: <T as System>::AccountId,
/// Account balance was transfered to.
pub to: <T as System>::AccountId,
/// Amount of balance that was transfered.
pub amount: T::Balance,
}
impl<T: Balances> Event<T> for TransferEvent<T> {
const MODULE: &'static str = MODULE;
const EVENT: &'static str = "transfer";
}
+94 -95
View File
@@ -20,23 +20,15 @@ use crate::frame::{
balances::Balances,
system::System,
Call,
Event,
};
use codec::{
Decode,
Encode,
};
use codec::Encode;
const MODULE: &str = "Contracts";
mod calls {
pub const PUT_CODE: &str = "put_code";
pub const INSTANTIATE: &str = "instantiate";
pub const CALL: &str = "call";
}
#[allow(unused)]
mod events {
pub const CODE_STORED: &str = "CodeStored";
pub const INSTANTIATED: &str = "Instantiated";
}
/// Gas units are chosen to be represented by u64 so that gas metering
/// instructions can operate on them efficiently.
pub type Gas = u64;
@@ -44,40 +36,21 @@ pub type Gas = u64;
/// The subset of the `pallet_contracts::Trait` that a client must implement.
pub trait Contracts: System + Balances {}
/// Arguments for uploading contract code to the chain
#[derive(Encode)]
pub struct PutCodeArgs {
#[codec(compact)]
gas_limit: Gas,
code: Vec<u8>,
}
/// Arguments for creating an instance of a contract
#[derive(Encode)]
pub struct InstantiateArgs<T: Contracts> {
#[codec(compact)]
endowment: <T as Balances>::Balance,
#[codec(compact)]
gas_limit: Gas,
code_hash: <T as System>::Hash,
data: Vec<u8>,
}
/// Arguments for calling a contract
#[derive(Encode)]
pub struct CallArgs<T: Contracts> {
dest: <T as System>::Address,
value: <T as Balances>::Balance,
#[codec(compact)]
gas_limit: Gas,
data: Vec<u8>,
}
/// Stores the given binary Wasm code into the chain's storage and returns
/// its `codehash`.
/// You can instantiate contracts only with stored code.
pub fn put_code(gas_limit: Gas, code: Vec<u8>) -> Call<PutCodeArgs> {
Call::new(MODULE, calls::PUT_CODE, PutCodeArgs { gas_limit, code })
#[derive(Debug, Encode)]
pub struct PutCodeCall<'a> {
/// Gas limit.
#[codec(compact)]
pub gas_limit: Gas,
/// Wasm blob.
pub code: &'a [u8],
}
impl<'a, T: Contracts> Call<T> for PutCodeCall<'a> {
const MODULE: &'static str = MODULE;
const FUNCTION: &'static str = "put_code";
}
/// Creates a new contract from the `codehash` generated by `put_code`,
@@ -93,22 +66,23 @@ pub fn put_code(gas_limit: Gas, code: Vec<u8>) -> Call<PutCodeArgs> {
/// of the account. That code will be invoked upon any call received by
/// this account.
/// - The contract is initialized.
pub fn instantiate<T: Contracts>(
endowment: <T as Balances>::Balance,
gas_limit: Gas,
code_hash: <T as System>::Hash,
data: Vec<u8>,
) -> Call<InstantiateArgs<T>> {
Call::new(
MODULE,
calls::INSTANTIATE,
InstantiateArgs {
endowment,
gas_limit,
code_hash,
data,
},
)
#[derive(Debug, Encode)]
pub struct InstantiateCall<'a, T: Contracts> {
/// Initial balance transfered to the contract.
#[codec(compact)]
pub endowment: <T as Balances>::Balance,
/// Gas limit.
#[codec(compact)]
pub gas_limit: Gas,
/// Code hash returned by the put_code call.
pub code_hash: &'a <T as System>::Hash,
/// Data to initialize the contract with.
pub data: &'a [u8],
}
impl<'a, T: Contracts> Call<T> for InstantiateCall<'a, T> {
const MODULE: &'static str = MODULE;
const FUNCTION: &'static str = "instantiate";
}
/// Makes a call to an account, optionally transferring some balance.
@@ -119,22 +93,46 @@ pub fn instantiate<T: Contracts>(
/// * 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,
calls::CALL,
CallArgs {
dest,
value,
gas_limit,
data,
},
)
#[derive(Debug, Encode)]
pub struct CallCall<'a, T: Contracts> {
/// Address of the contract.
pub dest: &'a <T as System>::Address,
/// Value to transfer to the contract.
pub value: <T as Balances>::Balance,
/// Gas limit.
#[codec(compact)]
pub gas_limit: Gas,
/// Data to send to the contract.
pub data: &'a [u8],
}
impl<'a, T: Contracts> Call<T> for CallCall<'a, T> {
const MODULE: &'static str = MODULE;
const FUNCTION: &'static str = "call";
}
/// Code stored event.
#[derive(Debug, Decode)]
pub struct CodeStoredEvent<T: Contracts> {
/// Code hash of the contract.
pub code_hash: T::Hash,
}
impl<T: Contracts> Event<T> for CodeStoredEvent<T> {
const MODULE: &'static str = MODULE;
const EVENT: &'static str = "CodeStored";
}
/// Instantiated event.
#[derive(Debug, Decode)]
pub struct InstantiatedEvent<T: Contracts>(
pub <T as System>::AccountId,
pub <T as System>::AccountId,
);
impl<T: Contracts> Event<T> for InstantiatedEvent<T> {
const MODULE: &'static str = MODULE;
const EVENT: &'static str = "Instantiated";
}
#[cfg(test)]
@@ -147,22 +145,16 @@ mod tests {
Verify,
};
use super::events;
use super::*;
use crate::{
frame::contracts::MODULE,
tests::test_client,
Balances,
Client,
DefaultNodeRuntime as Runtime,
Error,
System,
};
type AccountId = <Runtime as System>::AccountId;
async fn put_code<T, P, S>(client: &Client<T, S>, signer: P) -> Result<T::Hash, Error>
where
T: System + Balances + Send + Sync,
T: Contracts + Send + Sync,
T::Address: From<T::AccountId>,
P: Pair,
P::Signature: Codec,
@@ -179,10 +171,17 @@ mod tests {
let xt = client.xt(signer, None).await?;
let result = xt.watch().submit(super::put_code(500_000, wasm)).await?;
let result = xt
.watch()
.submit(PutCodeCall {
gas_limit: 500_000,
code: &wasm,
})
.await?;
let code_hash = result
.find_event::<T::Hash>(MODULE, events::CODE_STORED)
.ok_or(Error::Other("Failed to find CodeStored event".into()))??;
.find_event::<CodeStoredEvent<T>>()?
.ok_or(Error::Other("Failed to find CodeStored event".into()))?
.code_hash;
Ok(code_hash)
}
@@ -219,16 +218,16 @@ mod tests {
let xt = client.xt(signer, None).await?;
let result = xt
.watch()
.submit(super::instantiate::<Runtime>(
100_000_000_000_000,
500_000,
code_hash,
Vec::new(),
))
.submit(InstantiateCall {
endowment: 100_000_000_000_000,
gas_limit: 500_000,
code_hash: &code_hash,
data: &[],
})
.await?;
let event = result
.find_event::<(AccountId, AccountId)>(MODULE, events::INSTANTIATED)
.ok_or(Error::Other("Failed to find Instantiated event".into()))??;
.find_event::<InstantiatedEvent<_>>()?
.ok_or(Error::Other("Failed to find Instantiated event".into()))?;
Ok(event)
});
+54 -19
View File
@@ -16,29 +16,64 @@
//! Implements support for built-in runtime modules.
use codec::Encode;
use crate::{
events::{
EventsDecoder,
EventsError,
},
metadata::{
Metadata,
MetadataError,
},
};
use codec::{
Decode,
Encode,
};
use sp_core::storage::StorageKey;
pub mod balances;
pub mod contracts;
pub mod system;
/// Creates module calls
pub struct Call<T: Encode> {
/// Module name
pub module: &'static str,
/// Function name
pub function: &'static str,
/// Call arguments
pub args: T,
}
impl<T: Encode> Call<T> {
/// Create a module call
pub fn new(module: &'static str, function: &'static str, args: T) -> Self {
Call {
module,
function,
args,
}
/// Store trait.
pub trait Store<T>: Encode {
/// Module name.
const MODULE: &'static str;
/// Field name.
const FIELD: &'static str;
/// Return type.
type Returns: Decode;
/// Returns the `StorageKey`.
fn key(&self, metadata: &Metadata) -> Result<StorageKey, MetadataError>;
/// Returns the default value.
fn default(
&self,
metadata: &Metadata,
) -> Result<Option<Self::Returns>, MetadataError> {
Ok(metadata
.module(Self::MODULE)?
.storage(Self::FIELD)?
.default())
}
}
/// Call trait.
pub trait Call<T>: Encode {
/// Module name.
const MODULE: &'static str;
/// Function name.
const FUNCTION: &'static str;
/// Load event decoder.
fn events_decoder(_decoder: &mut EventsDecoder<T>) -> Result<(), EventsError> {
Ok(())
}
}
/// Event trait.
pub trait Event<T>: Decode {
/// Module name.
const MODULE: &'static str;
/// Event name.
const EVENT: &'static str;
}
+27 -59
View File
@@ -22,11 +22,8 @@ use codec::{
Encode,
};
use frame_support::Parameter;
use futures::future::{
self,
Future,
};
use serde::de::DeserializeOwned;
use sp_core::storage::StorageKey;
use sp_runtime::{
traits::{
AtLeast32Bit,
@@ -40,24 +37,21 @@ use sp_runtime::{
MaybeSerialize,
MaybeSerializeDeserialize,
Member,
SignedExtension,
SimpleBitOps,
},
RuntimeDebug,
};
use std::{
fmt::Debug,
pin::Pin,
};
use std::fmt::Debug;
use crate::{
error::Error,
extrinsic::SignedExtra,
frame::{
balances::Balances,
Call,
Store,
},
metadata::{
Metadata,
MetadataError,
},
Client,
};
/// The subset of the `frame::Trait` that a client must implement.
@@ -116,7 +110,7 @@ pub trait System: 'static + Eq + Clone + Debug {
+ Default;
/// The address type. This instead of `<frame_system::Trait::Lookup as StaticLookup>::Source`.
type Address: Codec + Clone + PartialEq + Debug;
type Address: Codec + Clone + PartialEq + Debug + Send + Sync;
/// The block header.
type Header: Parameter
@@ -147,59 +141,33 @@ pub struct AccountInfo<T: System> {
pub data: T::AccountData,
}
/// The System extension trait for the Client.
pub trait SystemStore {
/// System type.
type System: System;
const MODULE: &str = "System";
/// Returns the nonce and account data for an account_id.
fn account(
&self,
account_id: <Self::System as System>::AccountId,
) -> Pin<Box<dyn Future<Output = Result<AccountInfo<Self::System>, Error>> + Send>>;
}
/// Account field of the `System` module.
#[derive(Encode)]
pub struct AccountStore<'a, T: System>(pub &'a T::AccountId);
impl<T: System + Balances + Sync + Send + 'static, S: 'static, E> SystemStore
for Client<T, S, E>
where
E: SignedExtra<T> + SignedExtension + 'static,
{
type System = T;
impl<'a, T: System> Store<T> for AccountStore<'a, T> {
const MODULE: &'static str = MODULE;
const FIELD: &'static str = "Account";
type Returns = AccountInfo<T>;
fn account(
&self,
account_id: <Self::System as System>::AccountId,
) -> Pin<Box<dyn Future<Output = Result<AccountInfo<Self::System>, Error>> + Send>>
{
let account_map = || {
Ok(self
.metadata
.module("System")?
.storage("Account")?
.get_map()?)
};
let map = match account_map() {
Ok(map) => map,
Err(err) => return Box::pin(future::err(err)),
};
let client = self.clone();
Box::pin(async move {
client
.fetch_or(map.key(account_id), None, map.default())
.await
})
fn key(&self, metadata: &Metadata) -> Result<StorageKey, MetadataError> {
Ok(metadata
.module(Self::MODULE)?
.storage(Self::FIELD)?
.map()?
.key(self.0))
}
}
const MODULE: &str = "System";
const SET_CODE: &str = "set_code";
/// Arguments for updating the runtime code
pub type SetCode = Vec<u8>;
#[derive(Encode)]
pub struct SetCodeCall<'a>(pub &'a Vec<u8>);
/// Sets the new code.
pub fn set_code(code: Vec<u8>) -> Call<SetCode> {
Call::new(MODULE, SET_CODE, code)
impl<'a, T: System> Call<T> for SetCodeCall<'a> {
const MODULE: &'static str = MODULE;
const FUNCTION: &'static str = "set_code";
}
use frame_support::weights::DispatchInfo;