-
Notifications
You must be signed in to change notification settings - Fork 257
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
20 changed files
with
1,928 additions
and
505 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
[package] | ||
name = "substrate-subxt-proc-macro" | ||
version = "0.1.0" | ||
authors = ["David Craven <david@craven.ch>"] | ||
edition = "2018" | ||
autotests = false | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
heck = "0.3.1" | ||
proc-macro2 = "1.0.10" | ||
proc-macro-crate = "0.1.4" | ||
quote = "1.0.3" | ||
syn = "1.0.17" | ||
synstructure = "0.12.3" | ||
|
||
[dev-dependencies] | ||
async-std = { version = "1.5.0", features = ["attributes"] } | ||
codec = { package = "parity-scale-codec", version = "1.2", default-features = false, features = ["derive", "full"] } | ||
env_logger = "0.7.1" | ||
frame-support = "2.0.0-alpha.6" | ||
pretty_assertions = "0.6.1" | ||
sp-core = "2.0.0-alpha.6" | ||
sp-keyring = "2.0.0-alpha.6" | ||
sp-runtime = "2.0.0-alpha.6" | ||
substrate-subxt = { path = ".." } | ||
trybuild = "1.0.25" | ||
|
||
[[test]] | ||
name = "balances" | ||
path = "tests/balances.rs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use crate::utils; | ||
use heck::{ | ||
CamelCase, | ||
SnakeCase, | ||
}; | ||
use proc_macro2::TokenStream; | ||
use quote::{ | ||
format_ident, | ||
quote, | ||
}; | ||
use synstructure::Structure; | ||
|
||
pub fn call(s: Structure) -> TokenStream { | ||
let subxt = utils::use_crate("substrate-subxt"); | ||
let codec = utils::use_crate("parity-scale-codec"); | ||
let sp_core = utils::use_crate("sp-core"); | ||
let sp_runtime = utils::use_crate("sp-runtime"); | ||
let ident = &s.ast().ident; | ||
let generics = &s.ast().generics; | ||
let params = utils::type_params(generics); | ||
let module = utils::module_name(generics); | ||
let with_module = format_ident!( | ||
"with_{}", | ||
utils::path_to_ident(module).to_string().to_snake_case() | ||
); | ||
let call_name = ident.to_string().trim_end_matches("Call").to_snake_case(); | ||
let call = format_ident!("{}", call_name); | ||
let call_trait = format_ident!("{}CallExt", call_name.to_camel_case()); | ||
let bindings = utils::bindings(&s); | ||
let fields = bindings.iter().map(|bi| { | ||
let ident = bi.ast().ident.as_ref().unwrap(); | ||
quote!(#ident,) | ||
}); | ||
let args = bindings.iter().map(|bi| { | ||
let ident = bi.ast().ident.as_ref().unwrap(); | ||
let ty = &bi.ast().ty; | ||
quote!(#ident: #ty,) | ||
}); | ||
let args = quote!(#(#args)*); | ||
let ret = quote!(#subxt::ExtrinsicSuccess<T>); | ||
|
||
let expanded = quote! { | ||
impl#generics #subxt::Call<T> for #ident<#(#params),*> { | ||
const MODULE: &'static str = MODULE; | ||
const FUNCTION: &'static str = #call_name; | ||
fn events_decoder( | ||
decoder: &mut #subxt::EventsDecoder<T>, | ||
) -> Result<(), #subxt::EventsError> { | ||
decoder.#with_module()?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
pub trait #call_trait<T: #module> { | ||
fn #call<'a>( | ||
self, | ||
#args | ||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<#ret, #subxt::Error>> + Send + 'a>>; | ||
} | ||
|
||
impl<T, P, S, E> #call_trait<T> for #subxt::EventsSubscriber<T, P, S, E> | ||
where | ||
T: #module + #subxt::system::System + Send + Sync, | ||
P: #sp_core::Pair, | ||
S: #sp_runtime::traits::Verify + #codec::Codec + From<P::Signature> + Send + 'static, | ||
S::Signer: From<P::Public> + #sp_runtime::traits::IdentifyAccount<AccountId = T::AccountId>, | ||
T::Address: From<T::AccountId>, | ||
E: #subxt::SignedExtra<T> + #sp_runtime::traits::SignedExtension + 'static, | ||
{ | ||
fn #call<'a>( | ||
self, | ||
#args | ||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<#ret, #subxt::Error>> + Send + 'a>> { | ||
Box::pin(self.submit(#ident { #(#fields)* })) | ||
} | ||
} | ||
}; | ||
|
||
TokenStream::from(expanded) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_transfer_call() { | ||
let input = quote! { | ||
#[derive(Call, Encode)] | ||
pub struct TransferCall<'a, T: Balances> { | ||
pub to: &'a <T as System>::Address, | ||
#[codec(compact)] | ||
pub amount: T::Balance, | ||
} | ||
}; | ||
let expected = quote! { | ||
impl<'a, T: Balances> substrate_subxt::Call<T> for TransferCall<'a, T> { | ||
const MODULE: &'static str = MODULE; | ||
const FUNCTION: &'static str = "transfer"; | ||
fn events_decoder( | ||
decoder: &mut substrate_subxt::EventsDecoder<T>, | ||
) -> Result<(), substrate_subxt::EventsError> { | ||
decoder.with_balances()?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
pub trait TransferCallExt<T: Balances> { | ||
fn transfer<'a>( | ||
self, | ||
to: &'a <T as System>::Address, | ||
amount: T::Balance, | ||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<substrate_subxt::ExtrinsicSuccess<T>, substrate_subxt::Error>> + Send + 'a>>; | ||
} | ||
|
||
impl<T, P, S, E> TransferCallExt<T> for substrate_subxt::EventsSubscriber<T, P, S, E> | ||
where | ||
T: Balances + substrate_subxt::system::System + Send + Sync, | ||
P: sp_core::Pair, | ||
S: sp_runtime::traits::Verify + codec::Codec + From<P::Signature> + Send + 'static, | ||
S::Signer: From<P::Public> + sp_runtime::traits::IdentifyAccount< | ||
AccountId = T::AccountId>, | ||
T::Address: From<T::AccountId>, | ||
E: substrate_subxt::SignedExtra<T> + sp_runtime::traits::SignedExtension + 'static, | ||
{ | ||
fn transfer<'a>( | ||
self, | ||
to: &'a <T as System>::Address, | ||
amount: T::Balance, | ||
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<substrate_subxt::ExtrinsicSuccess<T>, substrate_subxt::Error>> + Send + 'a>> { | ||
Box::pin(self.submit(TransferCall { to, amount, })) | ||
} | ||
} | ||
}; | ||
let derive_input = syn::parse2(input).unwrap(); | ||
let s = Structure::new(&derive_input); | ||
let result = call(s); | ||
utils::assert_proc_macro(result, expected); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
use crate::utils; | ||
use heck::{ | ||
CamelCase, | ||
SnakeCase, | ||
}; | ||
use proc_macro2::TokenStream; | ||
use quote::{ | ||
format_ident, | ||
quote, | ||
}; | ||
use synstructure::Structure; | ||
|
||
pub fn event(s: Structure) -> TokenStream { | ||
let subxt = utils::use_crate("substrate-subxt"); | ||
let codec = utils::use_crate("parity-scale-codec"); | ||
let ident = &s.ast().ident; | ||
let generics = &s.ast().generics; | ||
let module = utils::module_name(generics); | ||
let event_name = ident.to_string().trim_end_matches("Event").to_camel_case(); | ||
let event = format_ident!("{}", event_name.to_snake_case()); | ||
let event_trait = format_ident!("{}EventExt", event_name); | ||
|
||
let expanded = quote! { | ||
impl<T: #module> #subxt::Event<T> for #ident<T> { | ||
const MODULE: &'static str = MODULE; | ||
const EVENT: &'static str = #event_name; | ||
} | ||
|
||
pub trait #event_trait<T: #module> { | ||
fn #event(&self) -> Result<Option<#ident<T>>, #codec::Error>; | ||
} | ||
|
||
impl<T: #module> #event_trait<T> for #subxt::ExtrinsicSuccess<T> { | ||
fn #event(&self) -> Result<Option<#ident<T>>, #codec::Error> { | ||
self.find_event() | ||
} | ||
} | ||
}; | ||
|
||
TokenStream::from(expanded) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_transfer_event() { | ||
let input = quote! { | ||
#[derive(Debug, Decode, Eq, Event, PartialEq)] | ||
pub struct TransferEvent<T: Balances> { | ||
pub from: <T as System>::AccountId, | ||
pub to: <T as System>::AccountId, | ||
pub amount: T::Balance, | ||
} | ||
}; | ||
let expected = quote! { | ||
impl<T: Balances> substrate_subxt::Event<T> for TransferEvent<T> { | ||
const MODULE: &'static str = MODULE; | ||
const EVENT: &'static str = "Transfer"; | ||
} | ||
|
||
pub trait TransferEventExt<T: Balances> { | ||
fn transfer(&self) -> Result<Option<TransferEvent<T>>, codec::Error>; | ||
} | ||
|
||
impl<T: Balances> TransferEventExt<T> for substrate_subxt::ExtrinsicSuccess<T> { | ||
fn transfer(&self) -> Result<Option<TransferEvent<T>>, codec::Error> { | ||
self.find_event() | ||
} | ||
} | ||
}; | ||
let derive_input = syn::parse2(input).unwrap(); | ||
let s = Structure::new(&derive_input); | ||
let result = event(s); | ||
utils::assert_proc_macro(result, expected); | ||
} | ||
} |
Oops, something went wrong.