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
+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)
});