From e62e1ede2b6669fd07f57fd6a31da5730df7aae1 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Thu, 11 Mar 2021 19:49:48 +0100 Subject: [PATCH 01/29] init weight_meter --- weight-meter/Cargo.toml | 15 ++ weight-meter/README.md | 3 + weight-meter/src/lib.rs | 32 +++++ .../weight-meter-procedural/Cargo.toml | 17 +++ .../weight-meter-procedural/src/lib.rs | 133 ++++++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 weight-meter/Cargo.toml create mode 100644 weight-meter/README.md create mode 100644 weight-meter/src/lib.rs create mode 100644 weight-meter/weight-meter-procedural/Cargo.toml create mode 100644 weight-meter/weight-meter-procedural/src/lib.rs diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml new file mode 100644 index 000000000..b97d2ac09 --- /dev/null +++ b/weight-meter/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "weight-meter" +version = "0.1.0" +license = "Apache-2.0" +authors = ["Laminar Developers "] +edition = "2018" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +weight-meter-procedural = { path = "weight-meter-procedural" } + +[features] +runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] \ No newline at end of file diff --git a/weight-meter/README.md b/weight-meter/README.md new file mode 100644 index 000000000..5ecc3216f --- /dev/null +++ b/weight-meter/README.md @@ -0,0 +1,3 @@ +# Weight Meter + +work in progress... \ No newline at end of file diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs new file mode 100644 index 000000000..1f4097c88 --- /dev/null +++ b/weight-meter/src/lib.rs @@ -0,0 +1,32 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// TODO: find a better way +static mut USED_WEIGHT: u64 = 0; +static mut NESTED: u64 = 0; + +pub fn using(m: u64) { + unsafe { + USED_WEIGHT = USED_WEIGHT.saturating_add(m); + } +} + +pub fn used_weight() -> u64 { + unsafe { USED_WEIGHT } +} + +pub fn start_with(m: u64) { + unsafe { + if NESTED == 0 { + USED_WEIGHT = m; + } + NESTED = NESTED.saturating_add(1); + } +} + +pub fn end() { + unsafe { + NESTED = NESTED.saturating_sub(1); + } +} + +pub use weight_meter_procedural::*; diff --git a/weight-meter/weight-meter-procedural/Cargo.toml b/weight-meter/weight-meter-procedural/Cargo.toml new file mode 100644 index 000000000..850be7581 --- /dev/null +++ b/weight-meter/weight-meter-procedural/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "weight-meter-procedural" +version = "0.1.0" +license = "Apache-2.0" +authors = ["Laminar Developers "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.6" +quote = "1.0.3" +syn = { version = "1.0.58", features = ["full"] } + +[features] +runtime-benchmarks = [] \ No newline at end of file diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs new file mode 100644 index 000000000..b6451fd68 --- /dev/null +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -0,0 +1,133 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use syn::{ItemFn, FnArg, ImplItem, ImplItemMethod, Item, ItemMod, Attribute}; +use quote::{quote, ToTokens}; + +#[proc_macro_attribute] +pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { + let base_weight: syn::Expr = syn::parse(attr).unwrap(); + let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap(); + (quote! { + #(#attrs)* + #vis #sig { + weight_meter::start_with(#base_weight); + let result = #block; + weight_meter::end(); + result + } + }).into() +} + + +#[proc_macro_attribute] +pub fn weight(attr: TokenStream, item: TokenStream) -> TokenStream { + let weight: syn::Expr = syn::parse(attr).unwrap(); + let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap(); + (quote! { + #(#attrs)* + #vis #sig { + weight_meter::using(#weight); + #block + } + }).into() +} + +#[proc_macro_attribute] +pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream { + #[cfg(feature = "runtime-benchmarks")] // Inject methods if we're benchmarking + { + let mut methods: Vec = vec![]; + + let mut item: ItemMod = syn::parse(input).unwrap(); + let (brace, content) = item.content.clone().unwrap(); + + let method_names = find_methods(&content); + + // Generate methods dynamically + content.iter().for_each(|i| { + if let Item::Impl(x) = i { // implementation + x.items.iter().for_each(|x| { + if let ImplItem::Method(i) = x { // method + let method_name = i.sig.ident.to_string(); + if method_names.contains(&method_name) { + let call_method_name: proc_macro2::TokenStream = i.sig.ident.to_string().parse().unwrap(); + let inject_method: proc_macro2::TokenStream = format!("method_{}", method_name).parse().unwrap(); + let args = i.sig.inputs.clone().into_iter().collect::>(); + let inputs: Vec = i.sig.inputs + .iter() + .map(|x| argument_name(&x)) + .filter(|x| x != &"") + .map(|x| x.parse().unwrap()) + .collect(); + + let method = quote! { + #[pallet::weight(0)] + pub fn #inject_method(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { + Self::#call_method_name(#(#inputs),*); + Ok(().into()) + } + }; + + let generated_method: ImplItemMethod = syn::parse(method.into()).unwrap(); + methods.push(ImplItem::from(generated_method)); + } + } + }); + } + }); + + // Inject methods into pallet::call impl + let new_content = content.into_iter().map(|item| { + if let Item::Impl(mut item_impl) = item { + if has_attribute(&item_impl.attrs, "pallet::call") { + println!("injected callable methods for inner methods {:?}", method_names); + item_impl.items.append(&mut methods); + } + return Item::from(item_impl); + } else { + item + } + }).collect::>(); + + // update content + item.content = Some((brace, new_content)); + + item.into_token_stream().into() + } + #[cfg(not(feature = "runtime-benchmarks"))] + input +} + +#[cfg(feature = "runtime-benchmarks")] +fn has_attribute(attrs: &Vec, attr: &str) -> bool { + if attrs.is_empty() { return false }; + let attributes = attrs.iter().map(|a| { + a.path.segments.iter().map(|p| p.ident.to_string()).collect::>().join("::") + }).collect::>(); + attributes.contains(&attr.to_string()) +} + +#[cfg(feature = "runtime-benchmarks")] +fn find_methods(content: &Vec) -> Vec { + let mut method_names = vec![]; + content.iter().for_each(|i| { + if let Item::Impl(x) = i { + x.items.iter().for_each(|x| { + if let ImplItem::Method(i) = x { + if has_attribute(&i.attrs, "weight_meter::weight") { + method_names.push(i.sig.ident.to_string()); + } + } + }) + } + }); + method_names +} + +#[cfg(feature = "runtime-benchmarks")] +fn argument_name(x: &FnArg) -> String { + match x { + FnArg::Receiver(_) => "".into(), + FnArg::Typed(a) => a.pat.to_token_stream().to_string(), + } +} \ No newline at end of file From 1ee5d6933ce1cb1fe1762bd5b064ffdfdcd3a29d Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Thu, 11 Mar 2021 22:23:30 +0100 Subject: [PATCH 02/29] update meter static var --- weight-meter/Cargo.toml | 3 +++ weight-meter/src/lib.rs | 43 +++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index b97d2ac09..15ae52344 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -9,7 +9,10 @@ edition = "2018" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +lazy_static = "1.4.0" +sp-std = {version = "3.0.0", default-fearures = false } weight-meter-procedural = { path = "weight-meter-procedural" } [features] +std = ["sp-std/std"] runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] \ No newline at end of file diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index 1f4097c88..a9f7066d1 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -1,32 +1,41 @@ +// TODO: research if there's a better way #![cfg_attr(not(feature = "std"), no_std)] -// TODO: find a better way -static mut USED_WEIGHT: u64 = 0; -static mut NESTED: u64 = 0; +#[macro_use] +extern crate lazy_static; + +use sp_std::sync::Mutex; + +pub use weight_meter_procedural::*; + +#[derive(Default)] +struct State { + pub used_weight: u64, + pub nested: usize, +} + +lazy_static! { + static ref STATE: Mutex = Mutex::new(State::default()); +} pub fn using(m: u64) { - unsafe { - USED_WEIGHT = USED_WEIGHT.saturating_add(m); - } + let mut s = STATE.lock().unwrap(); + s.used_weight = s.used_weight.saturating_add(m); } pub fn used_weight() -> u64 { - unsafe { USED_WEIGHT } + STATE.lock().unwrap().used_weight } pub fn start_with(m: u64) { - unsafe { - if NESTED == 0 { - USED_WEIGHT = m; - } - NESTED = NESTED.saturating_add(1); + let mut s = STATE.lock().unwrap(); + if s.nested == 0 { + s.used_weight = m; } + s.nested = s.nested.saturating_add(1); } pub fn end() { - unsafe { - NESTED = NESTED.saturating_sub(1); - } + let mut s = STATE.lock().unwrap(); + s.nested = s.nested.saturating_sub(1); } - -pub use weight_meter_procedural::*; From 9250171e668f7c1018eb27d4fdd5abc0d2793b91 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Thu, 11 Mar 2021 22:26:20 +0100 Subject: [PATCH 03/29] update workspace --- Cargo.dev.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.dev.toml b/Cargo.dev.toml index a38c164db..4b8025012 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -16,6 +16,7 @@ members = [ "nft", "xtokens", "xcm-support", + "weight-meter", ] resolver = "2" From 8f85cf80962a7b85a4731f618b84d5760d1090fb Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Thu, 11 Mar 2021 22:35:15 +0100 Subject: [PATCH 04/29] fmt --- .../weight-meter-procedural/src/lib.rs | 197 ++++++++++-------- 1 file changed, 108 insertions(+), 89 deletions(-) diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index b6451fd68..633616fc0 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -1,133 +1,152 @@ extern crate proc_macro; use proc_macro::TokenStream; -use syn::{ItemFn, FnArg, ImplItem, ImplItemMethod, Item, ItemMod, Attribute}; use quote::{quote, ToTokens}; +use syn::{Attribute, FnArg, ImplItem, ImplItemMethod, Item, ItemFn, ItemMod}; #[proc_macro_attribute] pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { - let base_weight: syn::Expr = syn::parse(attr).unwrap(); - let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap(); - (quote! { + let base_weight: syn::Expr = syn::parse(attr).unwrap(); + let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap(); + (quote! { #(#attrs)* #vis #sig { - weight_meter::start_with(#base_weight); + weight_meter::start_with(#base_weight); let result = #block; weight_meter::end(); result } - }).into() + }) + .into() } - #[proc_macro_attribute] pub fn weight(attr: TokenStream, item: TokenStream) -> TokenStream { - let weight: syn::Expr = syn::parse(attr).unwrap(); - let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap(); - (quote! { + let weight: syn::Expr = syn::parse(attr).unwrap(); + let ItemFn { attrs, vis, sig, block } = syn::parse(item).unwrap(); + (quote! { #(#attrs)* #vis #sig { - weight_meter::using(#weight); + weight_meter::using(#weight); #block } - }).into() + }) + .into() } #[proc_macro_attribute] pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream { - #[cfg(feature = "runtime-benchmarks")] // Inject methods if we're benchmarking - { - let mut methods: Vec = vec![]; + #[cfg(feature = "runtime-benchmarks")] // Inject methods if we're benchmarking + { + let mut methods: Vec = vec![]; - let mut item: ItemMod = syn::parse(input).unwrap(); - let (brace, content) = item.content.clone().unwrap(); + let mut item: ItemMod = syn::parse(input).unwrap(); + let (brace, content) = item.content.clone().unwrap(); - let method_names = find_methods(&content); + let method_names = find_methods(&content); - // Generate methods dynamically - content.iter().for_each(|i| { - if let Item::Impl(x) = i { // implementation - x.items.iter().for_each(|x| { - if let ImplItem::Method(i) = x { // method - let method_name = i.sig.ident.to_string(); - if method_names.contains(&method_name) { - let call_method_name: proc_macro2::TokenStream = i.sig.ident.to_string().parse().unwrap(); - let inject_method: proc_macro2::TokenStream = format!("method_{}", method_name).parse().unwrap(); - let args = i.sig.inputs.clone().into_iter().collect::>(); - let inputs: Vec = i.sig.inputs - .iter() - .map(|x| argument_name(&x)) - .filter(|x| x != &"") - .map(|x| x.parse().unwrap()) - .collect(); + // Generate methods dynamically + content.iter().for_each(|i| { + if let Item::Impl(x) = i { + // implementation + x.items.iter().for_each(|x| { + if let ImplItem::Method(i) = x { + // method + let method_name = i.sig.ident.to_string(); + if method_names.contains(&method_name) { + let call_method_name: proc_macro2::TokenStream = i.sig.ident.to_string().parse().unwrap(); + let inject_method: proc_macro2::TokenStream = + format!("method_{}", method_name).parse().unwrap(); + let args = i.sig.inputs.clone().into_iter().collect::>(); + let inputs: Vec = i + .sig + .inputs + .iter() + .map(|x| argument_name(&x)) + .filter(|x| x != &"") + .map(|x| x.parse().unwrap()) + .collect(); - let method = quote! { - #[pallet::weight(0)] - pub fn #inject_method(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { - Self::#call_method_name(#(#inputs),*); - Ok(().into()) - } - }; + let method = quote! { + #[pallet::weight(0)] + pub fn #inject_method(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { + Self::#call_method_name(#(#inputs),*); + Ok(().into()) + } + }; - let generated_method: ImplItemMethod = syn::parse(method.into()).unwrap(); - methods.push(ImplItem::from(generated_method)); - } - } - }); - } - }); + let generated_method: ImplItemMethod = syn::parse(method.into()).unwrap(); + methods.push(ImplItem::from(generated_method)); + } + } + }); + } + }); - // Inject methods into pallet::call impl - let new_content = content.into_iter().map(|item| { - if let Item::Impl(mut item_impl) = item { - if has_attribute(&item_impl.attrs, "pallet::call") { - println!("injected callable methods for inner methods {:?}", method_names); - item_impl.items.append(&mut methods); - } - return Item::from(item_impl); - } else { - item - } - }).collect::>(); + // Inject methods into pallet::call impl + let new_content = content + .into_iter() + .map(|item| { + if let Item::Impl(mut item_impl) = item { + if has_attribute(&item_impl.attrs, "pallet::call") { + println!("injected callable methods for inner methods {:?}", method_names); + item_impl.items.append(&mut methods); + } + return Item::from(item_impl); + } else { + item + } + }) + .collect::>(); - // update content - item.content = Some((brace, new_content)); + // update content + item.content = Some((brace, new_content)); - item.into_token_stream().into() - } - #[cfg(not(feature = "runtime-benchmarks"))] - input + item.into_token_stream().into() + } + #[cfg(not(feature = "runtime-benchmarks"))] + input } #[cfg(feature = "runtime-benchmarks")] fn has_attribute(attrs: &Vec, attr: &str) -> bool { - if attrs.is_empty() { return false }; - let attributes = attrs.iter().map(|a| { - a.path.segments.iter().map(|p| p.ident.to_string()).collect::>().join("::") - }).collect::>(); - attributes.contains(&attr.to_string()) + if attrs.is_empty() { + return false; + }; + let attributes = attrs + .iter() + .map(|a| { + a.path + .segments + .iter() + .map(|p| p.ident.to_string()) + .collect::>() + .join("::") + }) + .collect::>(); + attributes.contains(&attr.to_string()) } #[cfg(feature = "runtime-benchmarks")] fn find_methods(content: &Vec) -> Vec { - let mut method_names = vec![]; - content.iter().for_each(|i| { - if let Item::Impl(x) = i { - x.items.iter().for_each(|x| { - if let ImplItem::Method(i) = x { - if has_attribute(&i.attrs, "weight_meter::weight") { - method_names.push(i.sig.ident.to_string()); - } - } - }) - } - }); - method_names + let mut method_names = vec![]; + content.iter().for_each(|i| { + if let Item::Impl(x) = i { + x.items.iter().for_each(|x| { + if let ImplItem::Method(i) = x { + if has_attribute(&i.attrs, "weight_meter::weight") { + method_names.push(i.sig.ident.to_string()); + } + } + }) + } + }); + method_names } #[cfg(feature = "runtime-benchmarks")] fn argument_name(x: &FnArg) -> String { - match x { - FnArg::Receiver(_) => "".into(), - FnArg::Typed(a) => a.pat.to_token_stream().to_string(), - } -} \ No newline at end of file + match x { + FnArg::Receiver(_) => "".into(), + FnArg::Typed(a) => a.pat.to_token_stream().to_string(), + } +} From 61615e5caddf63672b5467ddb8ac742491f04de0 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Thu, 11 Mar 2021 22:40:31 +0100 Subject: [PATCH 05/29] clippy allow unused imports --- weight-meter/weight-meter-procedural/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index 633616fc0..a2f5fb323 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports)] + extern crate proc_macro; use proc_macro::TokenStream; use quote::{quote, ToTokens}; From 8b465d9042f82f6d6946e506b118260b4a782880 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Thu, 11 Mar 2021 23:40:39 +0100 Subject: [PATCH 06/29] default std --- weight-meter/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 15ae52344..7da2a1b13 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -14,5 +14,6 @@ sp-std = {version = "3.0.0", default-fearures = false } weight-meter-procedural = { path = "weight-meter-procedural" } [features] +default = ["std"] std = ["sp-std/std"] -runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] \ No newline at end of file +runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] From 8e40a045ca5c7f0b01249327090474a59fda97c5 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 00:47:19 +0100 Subject: [PATCH 07/29] update cargo --- weight-meter/Cargo.toml | 8 +++++--- weight-meter/src/lib.rs | 4 +--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 7da2a1b13..5601be44f 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -9,11 +9,13 @@ edition = "2018" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -lazy_static = "1.4.0" -sp-std = {version = "3.0.0", default-fearures = false } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +sp-std = { version = "3.0.0", default-fearures = false } +sp-io = { version = "3.0.0", default-fearures = false } weight-meter-procedural = { path = "weight-meter-procedural" } + [features] default = ["std"] -std = ["sp-std/std"] +std=["sp-std/std", "sp-io/std"] runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index a9f7066d1..1584517b1 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -1,9 +1,7 @@ // TODO: research if there's a better way #![cfg_attr(not(feature = "std"), no_std)] -#[macro_use] -extern crate lazy_static; - +use lazy_static::lazy_static; use sp_std::sync::Mutex; pub use weight_meter_procedural::*; From 04f659b60db2a8c2b49d092454cbf1acf660c7f5 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 00:55:41 +0100 Subject: [PATCH 08/29] remove std --- weight-meter/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 5601be44f..6437aa7b4 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -16,6 +16,4 @@ weight-meter-procedural = { path = "weight-meter-procedural" } [features] -default = ["std"] -std=["sp-std/std", "sp-io/std"] runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] From 8edec36658e9f744e7d1a02902c08e133473b009 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 01:06:55 +0100 Subject: [PATCH 09/29] revert until fix no_std issue --- weight-meter/Cargo.toml | 4 ---- weight-meter/src/lib.rs | 35 ++++++++++++++--------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 6437aa7b4..12f4e9228 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -9,11 +9,7 @@ edition = "2018" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -lazy_static = { version = "1.4.0", features = ["spin_no_std"] } -sp-std = { version = "3.0.0", default-fearures = false } -sp-io = { version = "3.0.0", default-fearures = false } weight-meter-procedural = { path = "weight-meter-procedural" } - [features] runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index 1584517b1..22400388b 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -1,39 +1,32 @@ // TODO: research if there's a better way #![cfg_attr(not(feature = "std"), no_std)] -use lazy_static::lazy_static; -use sp_std::sync::Mutex; - pub use weight_meter_procedural::*; -#[derive(Default)] -struct State { - pub used_weight: u64, - pub nested: usize, -} - -lazy_static! { - static ref STATE: Mutex = Mutex::new(State::default()); -} +static mut USED_WEIGHT: u64 = 0; +static mut NESTED: u64 = 0; pub fn using(m: u64) { - let mut s = STATE.lock().unwrap(); - s.used_weight = s.used_weight.saturating_add(m); + unsafe { + USED_WEIGHT = USED_WEIGHT.saturating_add(m); + } } pub fn used_weight() -> u64 { - STATE.lock().unwrap().used_weight + unsafe { USED_WEIGHT } } pub fn start_with(m: u64) { - let mut s = STATE.lock().unwrap(); - if s.nested == 0 { - s.used_weight = m; + unsafe { + if NESTED == 0 { + USED_WEIGHT = m; + } + NESTED = NESTED.saturating_add(1); } - s.nested = s.nested.saturating_add(1); } pub fn end() { - let mut s = STATE.lock().unwrap(); - s.nested = s.nested.saturating_sub(1); + unsafe { + NESTED = NESTED.saturating_sub(1); + } } From 41136b85a1801a8e60402fb95d3694c4644f2ca8 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 12:09:43 +0100 Subject: [PATCH 10/29] improvements --- .../weight-meter-procedural/src/lib.rs | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index a2f5fb323..fde836ce0 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -2,8 +2,8 @@ extern crate proc_macro; use proc_macro::TokenStream; -use quote::{quote, ToTokens}; -use syn::{Attribute, FnArg, ImplItem, ImplItemMethod, Item, ItemFn, ItemMod}; +use quote::{quote, ToTokens, format_ident}; +use syn::{Attribute, FnArg, Ident, ItemImpl, ImplItem, ImplItemMethod, Item, ItemFn, ItemMod}; #[proc_macro_attribute] pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { @@ -44,54 +44,54 @@ pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream let mut item: ItemMod = syn::parse(input).unwrap(); let (brace, content) = item.content.clone().unwrap(); - let method_names = find_methods(&content); - - // Generate methods dynamically - content.iter().for_each(|i| { - if let Item::Impl(x) = i { - // implementation - x.items.iter().for_each(|x| { - if let ImplItem::Method(i) = x { - // method - let method_name = i.sig.ident.to_string(); - if method_names.contains(&method_name) { - let call_method_name: proc_macro2::TokenStream = i.sig.ident.to_string().parse().unwrap(); - let inject_method: proc_macro2::TokenStream = - format!("method_{}", method_name).parse().unwrap(); - let args = i.sig.inputs.clone().into_iter().collect::>(); - let inputs: Vec = i - .sig - .inputs - .iter() + let whitelist = find_methods(&content); + + // Generate callable methods dynamically + content.iter().for_each(|item| { + if let Item::Impl(ItemImpl {items, ..}) = item { + items.iter().for_each(|item_impl| { + if let ImplItem::Method(ImplItemMethod { sig, ..} ) = item_impl { + + let method_name = sig.ident.clone(); + + // generate call method if whitelisted + if whitelist.contains(&method_name) { + let call_method_name = format_ident!("method_{}", method_name); + let args = sig.inputs.clone().into_iter().collect::>(); + let inputs: Vec = sig.inputs.iter() .map(|x| argument_name(&x)) .filter(|x| x != &"") .map(|x| x.parse().unwrap()) .collect(); + // construct call method let method = quote! { #[pallet::weight(0)] - pub fn #inject_method(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { - Self::#call_method_name(#(#inputs),*); + pub fn #call_method_name(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { + Self::#method_name(#(#inputs),*); Ok(().into()) } }; - let generated_method: ImplItemMethod = syn::parse(method.into()).unwrap(); - methods.push(ImplItem::from(generated_method)); + let call_method: ImplItemMethod = syn::parse(method.into()).unwrap(); + methods.push(ImplItem::from(call_method)); } } }); } }); - // Inject methods into pallet::call impl + // Inject methods into #[pallet::call] impl let new_content = content .into_iter() .map(|item| { if let Item::Impl(mut item_impl) = item { if has_attribute(&item_impl.attrs, "pallet::call") { - println!("injected callable methods for inner methods {:?}", method_names); item_impl.items.append(&mut methods); + + //// debug + // let method_names = whitelist.iter().map(|x| x.to_string()).collect::>(); + // println!("injected callable methods for inner methods {:?}", method_names); } return Item::from(item_impl); } else { @@ -113,7 +113,7 @@ pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream fn has_attribute(attrs: &Vec, attr: &str) -> bool { if attrs.is_empty() { return false; - }; + } let attributes = attrs .iter() .map(|a| { @@ -128,23 +128,25 @@ fn has_attribute(attrs: &Vec, attr: &str) -> bool { attributes.contains(&attr.to_string()) } +// Find methods with attribute `#[weight_meter::weight]` #[cfg(feature = "runtime-benchmarks")] -fn find_methods(content: &Vec) -> Vec { - let mut method_names = vec![]; - content.iter().for_each(|i| { - if let Item::Impl(x) = i { - x.items.iter().for_each(|x| { - if let ImplItem::Method(i) = x { - if has_attribute(&i.attrs, "weight_meter::weight") { - method_names.push(i.sig.ident.to_string()); +fn find_methods(content: &Vec) -> Vec { + let mut methods = vec![]; + content.iter().for_each(|content| { + if let Item::Impl(item_impl) = content { + item_impl.items.iter().for_each(|item| { + if let ImplItem::Method(ImplItemMethod {attrs, sig, ..}) = item { + if has_attribute(&attrs, "weight_meter::weight") { + methods.push(sig.ident.clone()); } } - }) + }); } }); - method_names + methods } +// Extract name from function argument #[cfg(feature = "runtime-benchmarks")] fn argument_name(x: &FnArg) -> String { match x { From 14850350342e0264e17408fe907ad3be3378e2e9 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 14:28:00 +0100 Subject: [PATCH 11/29] fmt --- .../weight-meter-procedural/src/lib.rs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index fde836ce0..590bf284b 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -2,8 +2,8 @@ extern crate proc_macro; use proc_macro::TokenStream; -use quote::{quote, ToTokens, format_ident}; -use syn::{Attribute, FnArg, Ident, ItemImpl, ImplItem, ImplItemMethod, Item, ItemFn, ItemMod}; +use quote::{format_ident, quote, ToTokens}; +use syn::{Attribute, FnArg, Ident, ImplItem, ImplItemMethod, Item, ItemFn, ItemImpl, ItemMod}; #[proc_macro_attribute] pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { @@ -48,17 +48,18 @@ pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream // Generate callable methods dynamically content.iter().for_each(|item| { - if let Item::Impl(ItemImpl {items, ..}) = item { + if let Item::Impl(ItemImpl { items, .. }) = item { items.iter().for_each(|item_impl| { - if let ImplItem::Method(ImplItemMethod { sig, ..} ) = item_impl { - + if let ImplItem::Method(ImplItemMethod { sig, .. }) = item_impl { let method_name = sig.ident.clone(); // generate call method if whitelisted if whitelist.contains(&method_name) { let call_method_name = format_ident!("method_{}", method_name); let args = sig.inputs.clone().into_iter().collect::>(); - let inputs: Vec = sig.inputs.iter() + let inputs: Vec = sig + .inputs + .iter() .map(|x| argument_name(&x)) .filter(|x| x != &"") .map(|x| x.parse().unwrap()) @@ -89,9 +90,11 @@ pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream if has_attribute(&item_impl.attrs, "pallet::call") { item_impl.items.append(&mut methods); - //// debug - // let method_names = whitelist.iter().map(|x| x.to_string()).collect::>(); - // println!("injected callable methods for inner methods {:?}", method_names); + // debug + // println!( + // "injected callable methods for inner methods {:?}", + // whitelist.iter().map(|x| x.to_string()).collect::>() + // ); } return Item::from(item_impl); } else { @@ -135,7 +138,7 @@ fn find_methods(content: &Vec) -> Vec { content.iter().for_each(|content| { if let Item::Impl(item_impl) = content { item_impl.items.iter().for_each(|item| { - if let ImplItem::Method(ImplItemMethod {attrs, sig, ..}) = item { + if let ImplItem::Method(ImplItemMethod { attrs, sig, .. }) = item { if has_attribute(&attrs, "weight_meter::weight") { methods.push(sig.ident.clone()); } From 0d6c751ec9096b0ce5643b1f9be29c134729d3f8 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 20:40:40 +0100 Subject: [PATCH 12/29] update argument extract --- weight-meter/Cargo.toml | 6 ++++- .../weight-meter-procedural/Cargo.toml | 2 ++ .../weight-meter-procedural/src/lib.rs | 25 +++++-------------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 12f4e9228..72c50afcf 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -9,7 +9,11 @@ edition = "2018" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -weight-meter-procedural = { path = "weight-meter-procedural" } +weight-meter-procedural = { path = "weight-meter-procedural", default-features = false } [features] +default = ["std"] +std = [ + "weight-meter-procedural/std", +] runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] diff --git a/weight-meter/weight-meter-procedural/Cargo.toml b/weight-meter/weight-meter-procedural/Cargo.toml index 850be7581..c72305821 100644 --- a/weight-meter/weight-meter-procedural/Cargo.toml +++ b/weight-meter/weight-meter-procedural/Cargo.toml @@ -14,4 +14,6 @@ quote = "1.0.3" syn = { version = "1.0.58", features = ["full"] } [features] +default = ["std"] +std = [] runtime-benchmarks = [] \ No newline at end of file diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index 590bf284b..04dd80641 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -1,9 +1,8 @@ #![allow(unused_imports)] -extern crate proc_macro; use proc_macro::TokenStream; use quote::{format_ident, quote, ToTokens}; -use syn::{Attribute, FnArg, Ident, ImplItem, ImplItemMethod, Item, ItemFn, ItemImpl, ItemMod}; +use syn::{Attribute, FnArg, Ident, ImplItem, ImplItemMethod, Item, ItemFn, ItemImpl, ItemMod, Pat}; #[proc_macro_attribute] pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { @@ -56,14 +55,8 @@ pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream // generate call method if whitelisted if whitelist.contains(&method_name) { let call_method_name = format_ident!("method_{}", method_name); - let args = sig.inputs.clone().into_iter().collect::>(); - let inputs: Vec = sig - .inputs - .iter() - .map(|x| argument_name(&x)) - .filter(|x| x != &"") - .map(|x| x.parse().unwrap()) - .collect(); + let args = sig.inputs.iter().collect::>(); + let inputs = sig.inputs.iter().map(|x| argument_name(&x)).collect::>(); // construct call method let method = quote! { @@ -89,12 +82,6 @@ pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream if let Item::Impl(mut item_impl) = item { if has_attribute(&item_impl.attrs, "pallet::call") { item_impl.items.append(&mut methods); - - // debug - // println!( - // "injected callable methods for inner methods {:?}", - // whitelist.iter().map(|x| x.to_string()).collect::>() - // ); } return Item::from(item_impl); } else { @@ -151,9 +138,9 @@ fn find_methods(content: &Vec) -> Vec { // Extract name from function argument #[cfg(feature = "runtime-benchmarks")] -fn argument_name(x: &FnArg) -> String { +fn argument_name(x: &FnArg) -> Box { match x { - FnArg::Receiver(_) => "".into(), - FnArg::Typed(a) => a.pat.to_token_stream().to_string(), + FnArg::Receiver(_) => panic!("unexpected argument self"), + FnArg::Typed(ty) => ty.pat.clone(), } } From 135d43d396744524c06a5b0b2d2b08b9377ff95b Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 12 Mar 2021 20:53:58 +0100 Subject: [PATCH 13/29] package name with orml prefix --- weight-meter/Cargo.toml | 2 +- weight-meter/weight-meter-procedural/src/lib.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 72c50afcf..2bd65eb76 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "weight-meter" +name = "orml-weight-meter" version = "0.1.0" license = "Apache-2.0" authors = ["Laminar Developers "] diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index 04dd80641..e5c9e9fed 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -11,9 +11,9 @@ pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { (quote! { #(#attrs)* #vis #sig { - weight_meter::start_with(#base_weight); + orml_weight_meter::start_with(#base_weight); let result = #block; - weight_meter::end(); + orml_weight_meter::end(); result } }) @@ -27,7 +27,7 @@ pub fn weight(attr: TokenStream, item: TokenStream) -> TokenStream { (quote! { #(#attrs)* #vis #sig { - weight_meter::using(#weight); + orml_weight_meter::using(#weight); #block } }) @@ -118,7 +118,7 @@ fn has_attribute(attrs: &Vec, attr: &str) -> bool { attributes.contains(&attr.to_string()) } -// Find methods with attribute `#[weight_meter::weight]` +// Find methods with attribute `#[orml_weight_meter::weight]` #[cfg(feature = "runtime-benchmarks")] fn find_methods(content: &Vec) -> Vec { let mut methods = vec![]; @@ -126,7 +126,7 @@ fn find_methods(content: &Vec) -> Vec { if let Item::Impl(item_impl) = content { item_impl.items.iter().for_each(|item| { if let ImplItem::Method(ImplItemMethod { attrs, sig, .. }) = item { - if has_attribute(&attrs, "weight_meter::weight") { + if has_attribute(&attrs, "orml_weight_meter::weight") { methods.push(sig.ident.clone()); } } From c6040e1d2f4248e61a98b0cf42f806fe674b53e8 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Mon, 15 Mar 2021 18:12:17 +0100 Subject: [PATCH 14/29] update meter to be Mutex --- weight-meter/Cargo.toml | 3 ++ weight-meter/src/lib.rs | 48 ++++++++++++------- .../weight-meter-procedural/src/lib.rs | 2 +- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 2bd65eb76..2265bb15e 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -9,11 +9,14 @@ edition = "2018" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +spin = "0.7.1" +frame-support = { version = "3.0.0", default-features = false } weight-meter-procedural = { path = "weight-meter-procedural", default-features = false } [features] default = ["std"] std = [ + "frame-support/std", "weight-meter-procedural/std", ] runtime-benchmarks = ["weight-meter-procedural/runtime-benchmarks"] diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index 22400388b..6a59bf3e3 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -1,32 +1,44 @@ // TODO: research if there's a better way #![cfg_attr(not(feature = "std"), no_std)] +use frame_support::weights::Weight; + pub use weight_meter_procedural::*; -static mut USED_WEIGHT: u64 = 0; -static mut NESTED: u64 = 0; +struct Meter { + used_weight: Weight, + nested: usize, +} + +static METER: spin::Mutex = spin::Mutex::new(Meter { + used_weight: 0, + nested: 0, +}); -pub fn using(m: u64) { - unsafe { - USED_WEIGHT = USED_WEIGHT.saturating_add(m); +pub fn start_with(base: Weight) { + let mut meter = METER.lock(); + if meter.nested == 0 { + meter.used_weight = base; } + meter.nested = meter.nested.saturating_add(1); + drop(meter); } -pub fn used_weight() -> u64 { - unsafe { USED_WEIGHT } +pub fn using(weight: Weight) { + let mut meter = METER.lock(); + meter.used_weight = meter.used_weight.saturating_add(weight); + drop(meter); } -pub fn start_with(m: u64) { - unsafe { - if NESTED == 0 { - USED_WEIGHT = m; - } - NESTED = NESTED.saturating_add(1); - } +pub fn finish() { + let mut meter = METER.lock(); + meter.nested = meter.nested.saturating_sub(1); + drop(meter); } -pub fn end() { - unsafe { - NESTED = NESTED.saturating_sub(1); - } +pub fn used_weight() -> Weight { + let meter = METER.lock(); + let used_weight = meter.used_weight; + drop(meter); + used_weight } diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index e5c9e9fed..53648488b 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -13,7 +13,7 @@ pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { #vis #sig { orml_weight_meter::start_with(#base_weight); let result = #block; - orml_weight_meter::end(); + orml_weight_meter::finish(); result } }) From c8d7d739d9afaaef53daace4e05fdfb2809e36c8 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Mon, 15 Mar 2021 22:52:33 +0100 Subject: [PATCH 15/29] use mutex only in std --- weight-meter/README.md | 45 +++++++++++++++++++++++++++++++- weight-meter/src/lib.rs | 43 +++++++----------------------- weight-meter/src/meter_no_std.rs | 34 ++++++++++++++++++++++++ weight-meter/src/meter_std.rs | 37 ++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 34 deletions(-) create mode 100644 weight-meter/src/meter_no_std.rs create mode 100644 weight-meter/src/meter_std.rs diff --git a/weight-meter/README.md b/weight-meter/README.md index 5ecc3216f..d65320e6e 100644 --- a/weight-meter/README.md +++ b/weight-meter/README.md @@ -1,3 +1,46 @@ # Weight Meter -work in progress... \ No newline at end of file +## How to use WeightMeter +1. Include `WeightMeter` into your module Cargo.toml +``` +orml-weight-meter = { version = "0.1.0", default-features = false } + +std = [ + ... + 'orml-weight-meter/std', +] +runtime-benchmarks = [ + ... + 'orml-weight-meter/runtime-benchmarks', +] + +``` + +2. Add macro attribute on top of the module declaration +``` +#[orml_weight_meter::method_benchmarks] +#[frame_support::pallet] +pub mod module { + use super::*; + ... +``` + +3. Add macro attribute on method you want to benchmark. +``` +#[orml_weight_meter::weight(0)] +fn inner_do_something(something: u32) { + // Update storage. + Something::::put(something); +} +``` +Start with `0` after the weights is generated then it can be replaced with generated weight. Macro will inject callable methods that wraps inner methods. Generated call will start with prefix `method_` followed by method name. This only works for methods with `orml_weight_meter::weight` attribute and only when running benchmarks. + +4. Create benchmarks as we normally do. Just need to use prefix `method_` followed by +method name. +``` +method_inner_do_something { + let caller = whitelisted_caller(); +}: _(frame_system::RawOrigin::Signed(caller), 10) +``` +After running benchmarks we can replace `#[orml_weight_meter::weight(0)]` with + `#[orml_weight_meter::weight(T::WeightInfo::method_inner_do_something())]`. \ No newline at end of file diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index 6a59bf3e3..de03b0c0a 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -1,44 +1,21 @@ -// TODO: research if there's a better way #![cfg_attr(not(feature = "std"), no_std)] use frame_support::weights::Weight; -pub use weight_meter_procedural::*; - struct Meter { used_weight: Weight, - nested: usize, + // Deep gets incremented when entering call or a sub-call + // This is used to avoid miscalculation during sub-calls + deep: usize, } -static METER: spin::Mutex = spin::Mutex::new(Meter { - used_weight: 0, - nested: 0, -}); - -pub fn start_with(base: Weight) { - let mut meter = METER.lock(); - if meter.nested == 0 { - meter.used_weight = base; - } - meter.nested = meter.nested.saturating_add(1); - drop(meter); -} +mod meter_no_std; +mod meter_std; -pub fn using(weight: Weight) { - let mut meter = METER.lock(); - meter.used_weight = meter.used_weight.saturating_add(weight); - drop(meter); -} +#[cfg(feature = "std")] +pub use meter_std::*; -pub fn finish() { - let mut meter = METER.lock(); - meter.nested = meter.nested.saturating_sub(1); - drop(meter); -} +#[cfg(not(feature = "std"))] +pub use meter_no_std::*; -pub fn used_weight() -> Weight { - let meter = METER.lock(); - let used_weight = meter.used_weight; - drop(meter); - used_weight -} +pub use weight_meter_procedural::*; diff --git a/weight-meter/src/meter_no_std.rs b/weight-meter/src/meter_no_std.rs new file mode 100644 index 000000000..934885ad7 --- /dev/null +++ b/weight-meter/src/meter_no_std.rs @@ -0,0 +1,34 @@ +// TODO: research if there's a better way +#![cfg(not(feature = "std"))] + +use super::{Meter, Weight}; + +static mut METER: Meter = Meter { + used_weight: 0, + deep: 0, +}; + +pub fn start_with(base: Weight) { + unsafe { + if METER.deep == 0 { + METER.used_weight = base; + } + METER.deep = METER.deep.saturating_add(1); + } +} + +pub fn using(weight: Weight) { + unsafe { + METER.used_weight = METER.used_weight.saturating_add(weight); + } +} + +pub fn finish() { + unsafe { + METER.deep = METER.deep.saturating_sub(1); + } +} + +pub fn used_weight() -> Weight { + unsafe { METER.used_weight } +} diff --git a/weight-meter/src/meter_std.rs b/weight-meter/src/meter_std.rs new file mode 100644 index 000000000..50aba551d --- /dev/null +++ b/weight-meter/src/meter_std.rs @@ -0,0 +1,37 @@ +// TODO: research if there's a better way +#![cfg(feature = "std")] + +use super::{Meter, Weight}; + +static METER: spin::Mutex = spin::Mutex::new(Meter { + used_weight: 0, + deep: 0, +}); + +pub fn start_with(base: Weight) { + let mut meter = METER.lock(); + if meter.deep == 0 { + meter.used_weight = base; + } + meter.deep = meter.deep.saturating_add(1); + drop(meter); +} + +pub fn using(weight: Weight) { + let mut meter = METER.lock(); + meter.used_weight = meter.used_weight.saturating_add(weight); + drop(meter); +} + +pub fn finish() { + let mut meter = METER.lock(); + meter.deep = meter.deep.saturating_sub(1); + drop(meter); +} + +pub fn used_weight() -> Weight { + let meter = METER.lock(); + let used_weight = meter.used_weight; + drop(meter); + used_weight +} From b857553fcdbd8c2d6a93b7717cd938b27fd23d86 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Mon, 15 Mar 2021 23:04:12 +0100 Subject: [PATCH 16/29] update README --- weight-meter/README.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/weight-meter/README.md b/weight-meter/README.md index d65320e6e..df4430282 100644 --- a/weight-meter/README.md +++ b/weight-meter/README.md @@ -33,7 +33,7 @@ fn inner_do_something(something: u32) { Something::::put(something); } ``` -Start with `0` after the weights is generated then it can be replaced with generated weight. Macro will inject callable methods that wraps inner methods. Generated call will start with prefix `method_` followed by method name. This only works for methods with `orml_weight_meter::weight` attribute and only when running benchmarks. +Start with `0` and after the weights is generated then it can be replaced with generated weight. Macro will inject callable methods that wraps inner methods. Generated call will start with prefix `method_` followed by method name. This only works for methods with `orml_weight_meter::weight` attribute and only when running benchmarks. 4. Create benchmarks as we normally do. Just need to use prefix `method_` followed by method name. @@ -43,4 +43,23 @@ method_inner_do_something { }: _(frame_system::RawOrigin::Signed(caller), 10) ``` After running benchmarks we can replace `#[orml_weight_meter::weight(0)]` with - `#[orml_weight_meter::weight(T::WeightInfo::method_inner_do_something())]`. \ No newline at end of file + `#[orml_weight_meter::weight(T::WeightInfo::method_inner_do_something())]`. + +5. Use WeightMeter on your calls by adding macro `#[orml_weight_meter::start_with()]` and at the end use `orml_weight_meter::used_weight()` to get used weight. +``` +#[pallet::call] +impl Pallet { + #[pallet::weight(T::WeightInfo::do_something())] + #[orml_weight_meter::start_with(1_000_000)] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + Self::inner_do_something(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored(something, who)); + + Ok(PostDispatchInfo::from(Some(orml_weight_meter::used_weight()))) + } +} +``` From d7a3af09b461709bb96571010204d2dece3b1d7c Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Mon, 15 Mar 2021 23:05:53 +0100 Subject: [PATCH 17/29] update README --- weight-meter/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/weight-meter/README.md b/weight-meter/README.md index df4430282..a89b4f571 100644 --- a/weight-meter/README.md +++ b/weight-meter/README.md @@ -3,6 +3,7 @@ ## How to use WeightMeter 1. Include `WeightMeter` into your module Cargo.toml ``` +[dependencies] orml-weight-meter = { version = "0.1.0", default-features = false } std = [ From c5ab5a4adf233c3de8cf25db824ad17c6dc8253f Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Tue, 16 Mar 2021 11:59:42 +0100 Subject: [PATCH 18/29] update property name --- weight-meter/src/lib.rs | 4 ++-- weight-meter/src/meter_no_std.rs | 8 ++++---- weight-meter/src/meter_std.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index de03b0c0a..f96e6b05d 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -4,9 +4,9 @@ use frame_support::weights::Weight; struct Meter { used_weight: Weight, - // Deep gets incremented when entering call or a sub-call + // Depth gets incremented when entering call or a sub-call // This is used to avoid miscalculation during sub-calls - deep: usize, + depth: u8, } mod meter_no_std; diff --git a/weight-meter/src/meter_no_std.rs b/weight-meter/src/meter_no_std.rs index 934885ad7..3628f0ede 100644 --- a/weight-meter/src/meter_no_std.rs +++ b/weight-meter/src/meter_no_std.rs @@ -5,15 +5,15 @@ use super::{Meter, Weight}; static mut METER: Meter = Meter { used_weight: 0, - deep: 0, + depth: 0, }; pub fn start_with(base: Weight) { unsafe { - if METER.deep == 0 { + if METER.depth == 0 { METER.used_weight = base; } - METER.deep = METER.deep.saturating_add(1); + METER.depth = METER.depth.saturating_add(1); } } @@ -25,7 +25,7 @@ pub fn using(weight: Weight) { pub fn finish() { unsafe { - METER.deep = METER.deep.saturating_sub(1); + METER.depth = METER.depth.saturating_sub(1); } } diff --git a/weight-meter/src/meter_std.rs b/weight-meter/src/meter_std.rs index 50aba551d..0b188de72 100644 --- a/weight-meter/src/meter_std.rs +++ b/weight-meter/src/meter_std.rs @@ -5,15 +5,15 @@ use super::{Meter, Weight}; static METER: spin::Mutex = spin::Mutex::new(Meter { used_weight: 0, - deep: 0, + depth: 0, }); pub fn start_with(base: Weight) { let mut meter = METER.lock(); - if meter.deep == 0 { + if meter.depth == 0 { meter.used_weight = base; } - meter.deep = meter.deep.saturating_add(1); + meter.depth = meter.depth.saturating_add(1); drop(meter); } @@ -25,7 +25,7 @@ pub fn using(weight: Weight) { pub fn finish() { let mut meter = METER.lock(); - meter.deep = meter.deep.saturating_sub(1); + meter.depth = meter.depth.saturating_sub(1); drop(meter); } From 5dadd780d44e5343aebacc2906846808177f7ac9 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Tue, 16 Mar 2021 12:42:10 +0100 Subject: [PATCH 19/29] separate method injector --- .../weight-meter-procedural/src/lib.rs | 123 ++---------------- .../src/method_injector.rs | 107 +++++++++++++++ 2 files changed, 117 insertions(+), 113 deletions(-) create mode 100644 weight-meter/weight-meter-procedural/src/method_injector.rs diff --git a/weight-meter/weight-meter-procedural/src/lib.rs b/weight-meter/weight-meter-procedural/src/lib.rs index 53648488b..e65dba4e0 100644 --- a/weight-meter/weight-meter-procedural/src/lib.rs +++ b/weight-meter/weight-meter-procedural/src/lib.rs @@ -1,8 +1,8 @@ -#![allow(unused_imports)] - use proc_macro::TokenStream; -use quote::{format_ident, quote, ToTokens}; -use syn::{Attribute, FnArg, Ident, ImplItem, ImplItemMethod, Item, ItemFn, ItemImpl, ItemMod, Pat}; +use quote::quote; +use syn::ItemFn; + +mod method_injector; #[proc_macro_attribute] pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { @@ -11,9 +11,9 @@ pub fn start_with(attr: TokenStream, item: TokenStream) -> TokenStream { (quote! { #(#attrs)* #vis #sig { - orml_weight_meter::start_with(#base_weight); + ::orml_weight_meter::start_with(#base_weight); let result = #block; - orml_weight_meter::finish(); + ::orml_weight_meter::finish(); result } }) @@ -27,7 +27,7 @@ pub fn weight(attr: TokenStream, item: TokenStream) -> TokenStream { (quote! { #(#attrs)* #vis #sig { - orml_weight_meter::using(#weight); + ::orml_weight_meter::using(#weight); #block } }) @@ -36,111 +36,8 @@ pub fn weight(attr: TokenStream, item: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn method_benchmarks(_attr: TokenStream, input: TokenStream) -> TokenStream { - #[cfg(feature = "runtime-benchmarks")] // Inject methods if we're benchmarking - { - let mut methods: Vec = vec![]; - - let mut item: ItemMod = syn::parse(input).unwrap(); - let (brace, content) = item.content.clone().unwrap(); - - let whitelist = find_methods(&content); - - // Generate callable methods dynamically - content.iter().for_each(|item| { - if let Item::Impl(ItemImpl { items, .. }) = item { - items.iter().for_each(|item_impl| { - if let ImplItem::Method(ImplItemMethod { sig, .. }) = item_impl { - let method_name = sig.ident.clone(); - - // generate call method if whitelisted - if whitelist.contains(&method_name) { - let call_method_name = format_ident!("method_{}", method_name); - let args = sig.inputs.iter().collect::>(); - let inputs = sig.inputs.iter().map(|x| argument_name(&x)).collect::>(); - - // construct call method - let method = quote! { - #[pallet::weight(0)] - pub fn #call_method_name(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { - Self::#method_name(#(#inputs),*); - Ok(().into()) - } - }; - - let call_method: ImplItemMethod = syn::parse(method.into()).unwrap(); - methods.push(ImplItem::from(call_method)); - } - } - }); - } - }); - - // Inject methods into #[pallet::call] impl - let new_content = content - .into_iter() - .map(|item| { - if let Item::Impl(mut item_impl) = item { - if has_attribute(&item_impl.attrs, "pallet::call") { - item_impl.items.append(&mut methods); - } - return Item::from(item_impl); - } else { - item - } - }) - .collect::>(); - - // update content - item.content = Some((brace, new_content)); - - item.into_token_stream().into() - } + #[cfg(feature = "runtime-benchmarks")] + return method_injector::inject_methods(input); #[cfg(not(feature = "runtime-benchmarks"))] - input -} - -#[cfg(feature = "runtime-benchmarks")] -fn has_attribute(attrs: &Vec, attr: &str) -> bool { - if attrs.is_empty() { - return false; - } - let attributes = attrs - .iter() - .map(|a| { - a.path - .segments - .iter() - .map(|p| p.ident.to_string()) - .collect::>() - .join("::") - }) - .collect::>(); - attributes.contains(&attr.to_string()) -} - -// Find methods with attribute `#[orml_weight_meter::weight]` -#[cfg(feature = "runtime-benchmarks")] -fn find_methods(content: &Vec) -> Vec { - let mut methods = vec![]; - content.iter().for_each(|content| { - if let Item::Impl(item_impl) = content { - item_impl.items.iter().for_each(|item| { - if let ImplItem::Method(ImplItemMethod { attrs, sig, .. }) = item { - if has_attribute(&attrs, "orml_weight_meter::weight") { - methods.push(sig.ident.clone()); - } - } - }); - } - }); - methods -} - -// Extract name from function argument -#[cfg(feature = "runtime-benchmarks")] -fn argument_name(x: &FnArg) -> Box { - match x { - FnArg::Receiver(_) => panic!("unexpected argument self"), - FnArg::Typed(ty) => ty.pat.clone(), - } + return input; } diff --git a/weight-meter/weight-meter-procedural/src/method_injector.rs b/weight-meter/weight-meter-procedural/src/method_injector.rs new file mode 100644 index 000000000..4e986ab64 --- /dev/null +++ b/weight-meter/weight-meter-procedural/src/method_injector.rs @@ -0,0 +1,107 @@ +#![cfg(feature = "runtime-benchmarks")] + +use proc_macro::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{Attribute, FnArg, Ident, ImplItem, ImplItemMethod, Item, ItemImpl, ItemMod, Pat}; + +pub fn inject_methods(input: TokenStream) -> TokenStream { + let mut methods: Vec = vec![]; + + let mut item: ItemMod = syn::parse(input).unwrap(); + let (brace, content) = item.content.clone().unwrap(); + + let whitelist = find_methods(&content); + + // Generate callable methods dynamically + content.iter().for_each(|item| { + if let Item::Impl(ItemImpl { items, .. }) = item { + items.iter().for_each(|item_impl| { + if let ImplItem::Method(ImplItemMethod { sig, .. }) = item_impl { + let method_name = sig.ident.clone(); + + // generate call method if whitelisted + if whitelist.contains(&method_name) { + let call_method_name = format_ident!("method_{}", method_name); + let args = sig.inputs.iter().collect::>(); + let inputs = sig.inputs.iter().map(|x| argument_name(&x)).collect::>(); + + // construct call method + let method = quote! { + #[pallet::weight(0)] + pub fn #call_method_name(_origin: OriginFor, #(#args),*) -> DispatchResultWithPostInfo { + Self::#method_name(#(#inputs),*); + Ok(().into()) + } + }; + + let call_method: ImplItemMethod = syn::parse(method.into()).unwrap(); + methods.push(ImplItem::from(call_method)); + } + } + }); + } + }); + + // Inject methods into #[pallet::call] impl + let new_content = content + .into_iter() + .map(|item| { + if let Item::Impl(mut item_impl) = item { + if has_attribute(&item_impl.attrs, "pallet::call") { + item_impl.items.append(&mut methods); + } + return Item::from(item_impl); + } else { + item + } + }) + .collect::>(); + + // update content + item.content = Some((brace, new_content)); + + item.into_token_stream().into() +} + +fn has_attribute(attrs: &Vec, attr: &str) -> bool { + if attrs.is_empty() { + return false; + } + let attributes = attrs + .iter() + .map(|a| { + a.path + .segments + .iter() + .map(|p| p.ident.to_string()) + .collect::>() + .join("::") + }) + .collect::>(); + attributes.contains(&attr.to_string()) +} + +// Find methods with attribute `#[orml_weight_meter::weight]` +fn find_methods(content: &Vec) -> Vec { + let mut methods = vec![]; + content.iter().for_each(|content| { + if let Item::Impl(item_impl) = content { + item_impl.items.iter().for_each(|item| { + if let ImplItem::Method(ImplItemMethod { attrs, sig, .. }) = item { + if has_attribute(&attrs, "orml_weight_meter::weight") { + methods.push(sig.ident.clone()); + } + } + }); + } + }); + methods +} + +// Extract name from function argument +fn argument_name(x: &FnArg) -> Box { + match x { + FnArg::Receiver(_) => panic!("unexpected argument self"), + FnArg::Typed(ty) => ty.pat.clone(), + } +} From 46a7969f331e02b69750ecf727c7b7a25242de9c Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Tue, 16 Mar 2021 16:24:09 +0100 Subject: [PATCH 20/29] docs --- weight-meter/README.md | 59 +++---------------------------- weight-meter/src/lib.rs | 66 ++++++++++++++++++++++++++++++++++- weight-meter/src/meter_std.rs | 4 +++ 3 files changed, 74 insertions(+), 55 deletions(-) diff --git a/weight-meter/README.md b/weight-meter/README.md index a89b4f571..6d30487d1 100644 --- a/weight-meter/README.md +++ b/weight-meter/README.md @@ -1,66 +1,17 @@ # Weight Meter -## How to use WeightMeter -1. Include `WeightMeter` into your module Cargo.toml +Include `WeightMeter` into your module Cargo.toml ``` [dependencies] -orml-weight-meter = { version = "0.1.0", default-features = false } +orml-weight-meter = { version = "..", default-features = false } std = [ - ... + .. 'orml-weight-meter/std', ] runtime-benchmarks = [ - ... + .. 'orml-weight-meter/runtime-benchmarks', ] -``` - -2. Add macro attribute on top of the module declaration -``` -#[orml_weight_meter::method_benchmarks] -#[frame_support::pallet] -pub mod module { - use super::*; - ... -``` - -3. Add macro attribute on method you want to benchmark. -``` -#[orml_weight_meter::weight(0)] -fn inner_do_something(something: u32) { - // Update storage. - Something::::put(something); -} -``` -Start with `0` and after the weights is generated then it can be replaced with generated weight. Macro will inject callable methods that wraps inner methods. Generated call will start with prefix `method_` followed by method name. This only works for methods with `orml_weight_meter::weight` attribute and only when running benchmarks. - -4. Create benchmarks as we normally do. Just need to use prefix `method_` followed by -method name. -``` -method_inner_do_something { - let caller = whitelisted_caller(); -}: _(frame_system::RawOrigin::Signed(caller), 10) -``` -After running benchmarks we can replace `#[orml_weight_meter::weight(0)]` with - `#[orml_weight_meter::weight(T::WeightInfo::method_inner_do_something())]`. - -5. Use WeightMeter on your calls by adding macro `#[orml_weight_meter::start_with()]` and at the end use `orml_weight_meter::used_weight()` to get used weight. -``` -#[pallet::call] -impl Pallet { - #[pallet::weight(T::WeightInfo::do_something())] - #[orml_weight_meter::start_with(1_000_000)] - pub fn do_something(origin: OriginFor, something: u32) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - Self::inner_do_something(something); - - // Emit an event. - Self::deposit_event(Event::SomethingStored(something, who)); - - Ok(PostDispatchInfo::from(Some(orml_weight_meter::used_weight()))) - } -} -``` +``` \ No newline at end of file diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index f96e6b05d..cb67ef62d 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -18,4 +18,68 @@ pub use meter_std::*; #[cfg(not(feature = "std"))] pub use meter_no_std::*; -pub use weight_meter_procedural::*; +/// Start weight meter with base weight +pub use weight_meter_procedural::start_with; + +/// Measure each methods weight +pub use weight_meter_procedural::weight; + +/// `method_benchmarks` attribute macro let's you benchmark inner methods +/// +/// 1. Add macro attribute on top of the module declaration +/// ```ignore +/// #[orml_weight_meter::method_benchmarks] +/// #[frame_support::pallet] +/// pub mod module { +/// use super::*; +/// .. +/// } +/// ``` +/// +/// 2. Add macro attribute on method you want to benchmark. +/// ```ignore +/// #[orml_weight_meter::weight(0)] +/// fn inner_do_something(something: u32) { +/// // Update storage. +/// Something::::put(something); +/// } +/// ``` +/// Start with `0` and after the weights is generated then it can be replaced +/// with generated weight. Macro will inject callable methods that wraps inner +/// methods. Generated call will start with prefix `method_` followed by method +/// name. This only works for methods with `orml_weight_meter::weight` attribute +/// and only when running benchmarks. +/// +/// 3. Create benchmarks as we normally do. Just need to use prefix `method_` +/// followed by method name. +/// ```ignore +/// method_inner_do_something { +/// let caller = whitelisted_caller(); +/// }: _(frame_system::RawOrigin::Signed(caller), 10) +/// ``` +/// After running benchmarks and weights been generated then we can replace ` +/// ```ignore +/// #[orml_weight_meter::weight(0)] +/// ``` +/// with +/// ```ignore +/// #[orml_weight_meter::weight(T::WeightInfo::method_inner_do_something())] +/// ``` +/// +/// 4. Use WeightMeter on your calls by adding macro +/// `#[orml_weight_meter::start_with()]` and at the end use +/// `orml_weight_meter::used_weight()` to get used weight. ```ignore +/// #[pallet::call] +/// impl Pallet { +/// #[pallet::weight(T::WeightInfo::do_something())] +/// #[orml_weight_meter::start_with(1_000_000)] +/// pub fn do_something(origin: OriginFor, something: u32) -> DispatchResultWithPostInfo { +/// let who = ensure_signed(origin)?; +/// Self::inner_do_something(something); +/// // Emit an event. +/// Self::deposit_event(Event::SomethingStored(something, who)); +/// Ok(PostDispatchInfo::from(Some(orml_weight_meter::used_weight()))) +/// } +/// } +/// ``` +pub use weight_meter_procedural::method_benchmarks; diff --git a/weight-meter/src/meter_std.rs b/weight-meter/src/meter_std.rs index 0b188de72..9622d05cb 100644 --- a/weight-meter/src/meter_std.rs +++ b/weight-meter/src/meter_std.rs @@ -8,6 +8,7 @@ static METER: spin::Mutex = spin::Mutex::new(Meter { depth: 0, }); +/// Start weight meter with base weight pub fn start_with(base: Weight) { let mut meter = METER.lock(); if meter.depth == 0 { @@ -17,18 +18,21 @@ pub fn start_with(base: Weight) { drop(meter); } +/// Increment used weight pub fn using(weight: Weight) { let mut meter = METER.lock(); meter.used_weight = meter.used_weight.saturating_add(weight); drop(meter); } +/// Finish weight meter pub fn finish() { let mut meter = METER.lock(); meter.depth = meter.depth.saturating_sub(1); drop(meter); } +/// Get used weight pub fn used_weight() -> Weight { let meter = METER.lock(); let used_weight = meter.used_weight; From d46f3d4ba2024c06ca721a01ba142e6cbe936df1 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Tue, 16 Mar 2021 19:22:18 +0100 Subject: [PATCH 21/29] fmt --- weight-meter/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index cb67ef62d..80ba24212 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -73,7 +73,8 @@ pub use weight_meter_procedural::weight; /// impl Pallet { /// #[pallet::weight(T::WeightInfo::do_something())] /// #[orml_weight_meter::start_with(1_000_000)] -/// pub fn do_something(origin: OriginFor, something: u32) -> DispatchResultWithPostInfo { +/// pub fn do_something(origin: OriginFor, something: u32) -> +/// DispatchResultWithPostInfo { /// let who = ensure_signed(origin)?; /// Self::inner_do_something(something); /// // Emit an event. From 31166278dfeb662ae967034964e49717f5e0675b Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Tue, 16 Mar 2021 19:51:35 +0100 Subject: [PATCH 22/29] clippy --- weight-meter/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/weight-meter/src/lib.rs b/weight-meter/src/lib.rs index 80ba24212..c63362328 100644 --- a/weight-meter/src/lib.rs +++ b/weight-meter/src/lib.rs @@ -31,7 +31,6 @@ pub use weight_meter_procedural::weight; /// #[orml_weight_meter::method_benchmarks] /// #[frame_support::pallet] /// pub mod module { -/// use super::*; /// .. /// } /// ``` @@ -74,7 +73,7 @@ pub use weight_meter_procedural::weight; /// #[pallet::weight(T::WeightInfo::do_something())] /// #[orml_weight_meter::start_with(1_000_000)] /// pub fn do_something(origin: OriginFor, something: u32) -> -/// DispatchResultWithPostInfo { +/// DispatchResultWithPostInfo { /// let who = ensure_signed(origin)?; /// Self::inner_do_something(something); /// // Emit an event. From e1c2321150257045a77b21a7549325ad13ad0543 Mon Sep 17 00:00:00 2001 From: brettkolodny Date: Thu, 15 Apr 2021 14:09:20 -0400 Subject: [PATCH 23/29] Weight meter (#450) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use MultiLocation as xtokens transfer dest type. (#396) * Use MultiLocation as xtokens transfer dest type. * Make clippy happy. * Use xcm-handler to execute XCM locally (#401) * Use cumulus xcm-handler to execute XCM locally. * Add docstring for xtokens config. * Replace XcmError::Undefined usage. (#402) * Replace XcmError::Undefined usage. * make clippy happy * Bump and unify serde versions. (#403) * xtokens and xcm-support documentations (#404) * Add xtokens module docstring. * Add xtokens readme. * Add documentations for xcm-support. * Add xtokens and xcm-support entries in main readme. * Add unit tests for xcm-support. (#405) * Added Minterest to the list of users. (#406) * update step guide link * Handle unknown assets in TransactAsset impl (#409) * Handle unknown assets in TransactAsset impl. * More documentations. * Clean code. * Renaming. * Should try to deposit known asset first. * Return error if no UnknownAsset impl. * Make clippy happy. * Fix description and repo link. (#410) * Unknown tokens pallet (#411) * Impl unknown tokens pallet. * Fix workspace pallet path. * Make clippy happy. * Clippy, be happy. * Unit tests. * Remove nonces from oracle pallet. (#413) * refactor rewards (#412) * Bump rococo v1 dependencies (#418) * Fix mocks. * Replace deprecated. * Update orml-unknown-tokens unit tests. (#421) * add build-script-utils from Substrate (#422) * Update README.md (#420) * Update README.md * Update README.md * Bump impl-trait-for-tuples to 0.2.1 (#424) * update Cargo.toml (#429) * add Handler (#431) * remove disable-tokens-by-owner (#434) * Cross-chain transfer rework (#432) * Reserve chain trait. * Rework cross-chain transfer. * Remove relay chain balance convert. * Add 'Parse' trait. * Change transfer_multiasset fn signature. * Add transfer dispatchable call. * Update doc. * Use xcm-simulator to mock network. * Send relay chain asset to sibling unit test. * Move location traits into orml-traits. * Add MultiNativeAsset filter for is reserve check. * More unit tests. * Failing edge case unit tests. * Handle zero amount asset case. * Fix mocks. * Renaming. * Update currency adapter to work with new xtokens impl (#436) * Xcm support implementations rework. * Update xtokens mock. * Use CurrencyId convert. (#437) * Use CurrencyId convert. * Apply review suggestions. * Update xtokens docs. (#438) * Update xtokens docs. * Fix typo. * Update imbalances impl. * Don't deposit failure event in orml-unknown-tokens. (#440) * Don't deposit failure event in orml-unknown-tokens. * Patch substrate/polkadot/cumulus. * Fix patch. * Update README.md (#441) Include Zeitgeist into "Projects using ORML" section * Add PoV size in benchmarking. (#442) * Bump cumulus ref in cargo patch. (#443) * fix missing features (#444) * fix missing features * test with benchmarks * update auction weight (#445) * Bump dependencies. (#448) * Updated Cargo.dev.toml Co-authored-by: Shaun Wang Co-authored-by: dzianis.ramanouski Co-authored-by: Bette <42193328+bette7@users.noreply.github.com> Co-authored-by: wangjj9219 <183318287@qq.com> Co-authored-by: Xiliang Chen Co-authored-by: transxask <68648225+transxask@users.noreply.github.com> Co-authored-by: Aaro Perämaa Co-authored-by: Harald Heckmann --- Cargo.dev.toml | 87 +++-- Makefile | 4 +- README.md | 11 +- auction/Cargo.toml | 2 +- auction/src/default_weight.rs | 10 - auction/src/lib.rs | 4 +- auction/src/mock.rs | 5 +- authority/Cargo.toml | 4 +- authority/src/lib.rs | 6 +- authority/src/mock.rs | 7 +- benchmarking/Cargo.toml | 2 +- benchmarking/src/lib.rs | 20 +- benchmarking/src/tests.rs | 5 +- build-script-utils/Cargo.toml | 14 + build-script-utils/README.md | 3 + build-script-utils/src/lib.rs | 5 + build-script-utils/src/version.rs | 55 ++++ currencies/Cargo.toml | 2 +- currencies/src/mock.rs | 17 +- gradually-update/Cargo.toml | 2 +- gradually-update/src/mock.rs | 6 +- nft/Cargo.toml | 3 +- nft/src/lib.rs | 16 +- nft/src/mock.rs | 5 +- oracle/Cargo.toml | 2 +- oracle/rpc/Cargo.toml | 4 +- oracle/rpc/runtime-api/Cargo.toml | 2 + oracle/src/lib.rs | 4 - oracle/src/mock.rs | 5 +- rewards/Cargo.toml | 2 +- rewards/src/default_weight.rs | 16 - rewards/src/lib.rs | 49 +-- rewards/src/mock.rs | 41 +-- rewards/src/tests.rs | 109 ++----- tokens/Cargo.toml | 6 +- tokens/src/imbalances.rs | 38 ++- tokens/src/lib.rs | 14 +- tokens/src/mock.rs | 21 +- traits/Cargo.toml | 6 +- traits/src/lib.rs | 18 +- traits/src/location.rs | 123 ++++++++ traits/src/rewards.rs | 20 +- unknown-tokens/Cargo.toml | 36 +++ unknown-tokens/src/lib.rs | 115 +++++++ unknown-tokens/src/mock.rs | 74 +++++ unknown-tokens/src/tests.rs | 144 +++++++++ utilities/Cargo.toml | 4 +- vesting/Cargo.toml | 6 +- vesting/src/lib.rs | 2 +- vesting/src/mock.rs | 9 +- xcm-support/Cargo.toml | 2 +- xcm-support/README.md | 6 + xcm-support/src/currency_adapter.rs | 108 +++++++ xcm-support/src/lib.rs | 168 ++++------ xcm-support/src/tests.rs | 109 +++++++ xtokens/Cargo.toml | 21 +- xtokens/README.md | 31 ++ xtokens/src/lib.rs | 425 +++++++++++-------------- xtokens/src/mock.rs | 470 ++++++++++++++++++++++++++++ xtokens/src/tests.rs | 290 +++++++++++++++++ 60 files changed, 2107 insertions(+), 688 deletions(-) create mode 100644 build-script-utils/Cargo.toml create mode 100644 build-script-utils/README.md create mode 100644 build-script-utils/src/lib.rs create mode 100644 build-script-utils/src/version.rs delete mode 100644 rewards/src/default_weight.rs create mode 100644 traits/src/location.rs create mode 100644 unknown-tokens/Cargo.toml create mode 100644 unknown-tokens/src/lib.rs create mode 100644 unknown-tokens/src/mock.rs create mode 100644 unknown-tokens/src/tests.rs create mode 100644 xcm-support/README.md create mode 100644 xcm-support/src/currency_adapter.rs create mode 100644 xcm-support/src/tests.rs create mode 100644 xtokens/README.md create mode 100644 xtokens/src/mock.rs create mode 100644 xtokens/src/tests.rs diff --git a/Cargo.dev.toml b/Cargo.dev.toml index 4b8025012..86f3665e4 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -8,6 +8,8 @@ members = [ "currencies", "gradually-update", "oracle", + "oracle/rpc", + "oracle/rpc/runtime-api", "tokens", "traits", "utilities", @@ -16,26 +18,73 @@ members = [ "nft", "xtokens", "xcm-support", + "unknown-tokens", + "build-script-utils", "weight-meter", ] resolver = "2" -[patch.crates-io] -frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -pallet-elections-phragmen = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -pallet-scheduler = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -pallet-treasury = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-api = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-application-crypto = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-arithmetic = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-inherents = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-runtime-interface = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-trie = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } -sp-version = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } +[patch.'https://github.com/paritytech/substrate'] +frame-benchmarking = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +frame-support = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +frame-system = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-balances = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-elections-phragmen = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-scheduler = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-treasury = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-authorship = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-vesting = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-timestamp = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-session = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-staking = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-offences = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-api = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-application-crypto = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-arithmetic = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-core = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-inherents = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-io = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-runtime = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-runtime-interface = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-std = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-trie = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-version = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-externalities = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-state-machine = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-wasm-interface = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-keystore = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-staking = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-storage = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-panic-handler = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-npos-elections = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-npos-elections-compact = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-debug-derive = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-allocator = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-authority-discovery = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sc-executor-common = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sc-executor = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sc-client-api = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-tasks = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-authorship = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-consensus = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-timestamp = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-session = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-blockchain = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } +sp-transaction-pool = { git = "https://github.com/paritytech//substrate", rev = "816ed3d4e77a2463c86e69ec5a26fc307ef452b9" } + +[patch.'https://github.com/paritytech/cumulus'] +cumulus-primitives-core = { git = "https://github.com/paritytech//cumulus", rev = "3db8a38cfad53c4fe742ca68d7b425b88c61813d" } +cumulus-pallet-parachain-system = { git = "https://github.com/paritytech//cumulus", rev = "3db8a38cfad53c4fe742ca68d7b425b88c61813d" } +parachain-info = { git = "https://github.com/paritytech//cumulus", rev = "3db8a38cfad53c4fe742ca68d7b425b88c61813d" } +cumulus-pallet-xcm-handler = { git = "https://github.com/paritytech//cumulus", rev = "3db8a38cfad53c4fe742ca68d7b425b88c61813d" } + +[patch.'https://github.com/paritytech/polkadot'] +xcm = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } +xcm-executor = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } +xcm-builder = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } +polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } +polkadot-parachain = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } +polkadot-primitives = { git = "https://github.com/paritytech//polkadot", rev = "8adff378fe35c850d5155732ae32260bcb5feb9b" } diff --git a/Makefile b/Makefile index afee442fd..0583cd27d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,6 @@ check-tests: githooks test: githooks ./scripts/run.sh test - cargo test --manifest-path nft/Cargo.toml -p orml-nft --features disable-tokens-by-owner GITHOOKS_SRC = $(wildcard githooks/*) GITHOOKS_DEST = $(patsubst githooks/%, $(GITHOOK)/%, $(GITHOOKS_SRC)) @@ -47,5 +46,4 @@ dev-check-tests: Cargo.toml cargo check --tests --all dev-test: Cargo.toml - cargo test --all - cargo test --manifest-path nft/Cargo.toml -p orml-nft --features disable-tokens-by-owner + cargo test --all --features runtime-benchmarks diff --git a/README.md b/README.md index 7cc205fcc..852832c49 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,11 @@ The Open Runtime Module Library (ORML) is a community maintained collection of S - Provides scheduled balance locking mechanism, in a *graded vesting* way. - [orml-gradually-update](./gradually-update) - Provides way to adjust numeric parameter gradually over a period of time. +- [orml-xtokens](./xtokens) + - Provides way to do cross-chain assets transfer. + - [Step-by-Step guide](https://github.com/open-web3-stack/open-runtime-module-library/wiki/xtokens) to make XCM cross-chain fungible asset transfer available on your parachain +- [orml-xcm-support](./xcm-support) + - Provides traits, types, and implementations to support XCM integration. ## Example @@ -48,7 +53,7 @@ ORML use `Cargo.dev.toml` to avoid workspace conflicts with project cargo config - change the command to `make dev-check` etc which does the copy. (For the full list of `make` commands, check `Makefile`) # Web3 Foundation Grant Project -ORML is part of the bigger `Open-Web3-Stack` initiative, that is currently under a General Grant from Web3 Foundation. See Application details [here](https://github.com/open-web3-stack/General-Grants-Program/blob/master/grants/speculative/open_web3_stack.md). The 1st milestone has been delivered. +ORML is part of the bigger `Open-Web3-Stack` initiative, that is currently under a General Grant from Web3 Foundation. See Application details [here](https://github.com/open-web3-stack/General-Grants-Program/blob/master/grants/speculative/open_web3_stack.md). The 1st milestone has been delivered. # Projects using ORML - [If you intend or are using ORML, please add your project here](https://github.com/open-web3-stack/open-runtime-module-library/edit/master/README.md) @@ -61,6 +66,10 @@ _In alphabetical order_ - [ChainX](https://github.com/chainx-org/ChainX) - [HydraDX](https://github.com/galacticcouncil/hack.HydraDX-node) - [Laminar Chain](https://github.com/laminar-protocol/laminar-chain) +- [Listen](https://github.com/listenofficial) +- [Minterest](https://github.com/minterest-finance/minterest-chain-node) - [Plasm Network](https://github.com/PlasmNetwork) - [Setheum Network](https://github.com/Setheum-Labs/Setheum) - [Valiu Liquidity Network](https://github.com/valibre-org/vln-node) +- [Zeitgeist](https://github.com/zeitgeistpm/zeitgeist) + diff --git a/auction/Cargo.toml b/auction/Cargo.toml index 5c568933b..64d024a3d 100644 --- a/auction/Cargo.toml +++ b/auction/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Acala Developers"] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } diff --git a/auction/src/default_weight.rs b/auction/src/default_weight.rs index 84a711aa7..26e552fef 100644 --- a/auction/src/default_weight.rs +++ b/auction/src/default_weight.rs @@ -12,16 +12,6 @@ impl crate::WeightInfo for () { .saturating_add(DbWeight::get().reads(12 as Weight)) .saturating_add(DbWeight::get().writes(12 as Weight)) } - fn bid_surplus_auction() -> Weight { - (257_830_000 as Weight) - .saturating_add(DbWeight::get().reads(6 as Weight)) - .saturating_add(DbWeight::get().writes(5 as Weight)) - } - fn bid_debit_auction() -> Weight { - (287_271_000 as Weight) - .saturating_add(DbWeight::get().reads(7 as Weight)) - .saturating_add(DbWeight::get().writes(8 as Weight)) - } fn on_finalize(c: u32) -> Weight { (50_992_000 as Weight) .saturating_add((171_653_000 as Weight).saturating_mul(c as Weight)) diff --git a/auction/src/lib.rs b/auction/src/lib.rs index 115861bbf..53b40f31c 100644 --- a/auction/src/lib.rs +++ b/auction/src/lib.rs @@ -33,8 +33,6 @@ pub mod module { pub trait WeightInfo { fn bid_collateral_auction() -> Weight; - fn bid_surplus_auction() -> Weight; - fn bid_debit_auction() -> Weight; fn on_finalize(c: u32) -> Weight; } @@ -131,7 +129,7 @@ pub mod module { Auctions::::try_mutate_exists(id, |auction| -> DispatchResult { let mut auction = auction.as_mut().ok_or(Error::::AuctionNotExist)?; - let block_number = >::block_number(); + let block_number = >::block_number(); // make sure auction is started ensure!(block_number >= auction.start, Error::::AuctionNotStarted); diff --git a/auction/src/mock.rs b/auction/src/mock.rs index ab409e6f0..718c25164 100644 --- a/auction/src/mock.rs +++ b/auction/src/mock.rs @@ -42,6 +42,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } pub struct Handler; @@ -86,8 +87,8 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - AuctionModule: auction::{Module, Storage, Call, Event}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + AuctionModule: auction::{Pallet, Storage, Call, Event}, } ); diff --git a/authority/Cargo.toml b/authority/Cargo.toml index 5d7a393f4..ecd262d0d 100644 --- a/authority/Cargo.toml +++ b/authority/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Acala Developers"] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } @@ -38,4 +38,6 @@ std = [ "orml-traits/std", ] runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", ] diff --git a/authority/src/lib.rs b/authority/src/lib.rs index 01d45f920..6481a2c01 100644 --- a/authority/src/lib.rs +++ b/authority/src/lib.rs @@ -244,7 +244,7 @@ pub mod module { *id = id.checked_add(1).ok_or(Error::::Overflow)?; Ok(current_id) })?; - let now = frame_system::Module::::block_number(); + let now = frame_system::Pallet::::block_number(); let delay = match when { DispatchTime::At(x) => x.checked_sub(&now).ok_or(Error::::Overflow)?, DispatchTime::After(x) => x, @@ -283,7 +283,7 @@ pub mod module { task_id: ScheduleTaskIndex, when: DispatchTime, ) -> DispatchResultWithPostInfo { - let now = frame_system::Module::::block_number(); + let now = frame_system::Pallet::::block_number(); let new_delay = match when { DispatchTime::At(x) => x.checked_sub(&now).ok_or(Error::::Overflow)?, DispatchTime::After(x) => x, @@ -317,7 +317,7 @@ pub mod module { ) .map_err(|_| Error::::FailedToDelay)?; - let now = frame_system::Module::::block_number(); + let now = frame_system::Pallet::::block_number(); let dispatch_at = now.saturating_add(additional_delay); Self::deposit_event(Event::Delayed(initial_origin, task_id, dispatch_at)); diff --git a/authority/src/mock.rs b/authority/src/mock.rs index a3f6fa02d..593710bc6 100644 --- a/authority/src/mock.rs +++ b/authority/src/mock.rs @@ -51,6 +51,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } parameter_types! { @@ -162,9 +163,9 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: frame_system::{Module, Call, Config, Event}, - Authority: authority::{Module, Call, Origin, Event}, - Scheduler: pallet_scheduler::{Module, Call, Storage, Event}, + System: frame_system::{Pallet, Call, Config, Event}, + Authority: authority::{Pallet, Call, Origin, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, } ); diff --git a/benchmarking/Cargo.toml b/benchmarking/Cargo.toml index e2ae5e252..5727669c6 100644 --- a/benchmarking/Cargo.toml +++ b/benchmarking/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } paste = "0.1.16" codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } diff --git a/benchmarking/src/lib.rs b/benchmarking/src/lib.rs index 71c6af4e7..0755894cd 100644 --- a/benchmarking/src/lib.rs +++ b/benchmarking/src/lib.rs @@ -844,8 +844,8 @@ macro_rules! impl_benchmark { >::instance(&selected_benchmark, c, verify)?; // Set the block number to at least 1 so events are deposited. - if $crate::Zero::is_zero(&frame_system::Module::<$runtime>::block_number()) { - frame_system::Module::<$runtime>::set_block_number(1u32.into()); + if $crate::Zero::is_zero(&frame_system::Pallet::<$runtime>::block_number()) { + frame_system::Pallet::<$runtime>::set_block_number(1u32.into()); } // Commit the externalities to the database, flushing the DB cache. @@ -864,12 +864,21 @@ macro_rules! impl_benchmark { "Start Benchmark: {:?}", c ); + let start_pov = $crate::benchmarking::proof_size(); let start_extrinsic = $crate::benchmarking::current_time(); closure_to_benchmark()?; let finish_extrinsic = $crate::benchmarking::current_time(); - let elapsed_extrinsic = finish_extrinsic - start_extrinsic; + let end_pov = $crate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + // Commit the changes to get proper write count $crate::benchmarking::commit_db(); frame_support::log::trace!( @@ -896,6 +905,7 @@ macro_rules! impl_benchmark { repeat_reads: read_write_count.1, writes: read_write_count.2, repeat_writes: read_write_count.3, + proof_size: diff_pov, }); } @@ -990,8 +1000,8 @@ macro_rules! impl_benchmark_test { >::instance(&selected_benchmark, &c, true)?; // Set the block number to at least 1 so events are deposited. - if $crate::Zero::is_zero(&frame_system::Module::<$runtime>::block_number()) { - frame_system::Module::<$runtime>::set_block_number(1u32.into()); + if $crate::Zero::is_zero(&frame_system::Pallet::<$runtime>::block_number()) { + frame_system::Pallet::<$runtime>::set_block_number(1u32.into()); } // Run execution + verification diff --git a/benchmarking/src/tests.rs b/benchmarking/src/tests.rs index 2c015f329..333a776cb 100644 --- a/benchmarking/src/tests.rs +++ b/benchmarking/src/tests.rs @@ -76,6 +76,7 @@ impl frame_system::Config for Test { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } impl tests::test::Config for Test { @@ -97,8 +98,8 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - Pallet: test::{Module, Call, Storage, Config}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Pallet: test::{Pallet, Call, Storage, Config}, } ); diff --git a/build-script-utils/Cargo.toml b/build-script-utils/Cargo.toml new file mode 100644 index 000000000..c5d311634 --- /dev/null +++ b/build-script-utils/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "orml-build-script-utils" +description = "Crate with utility functions for `build.rs` scripts." +repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/build-script-utils" +license = "Apache-2.0" +version = "0.4.1-dev" +authors = ["Parity Technologies ", "Laminar Developers "] +edition = "2018" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +platforms = "1.1" diff --git a/build-script-utils/README.md b/build-script-utils/README.md new file mode 100644 index 000000000..1c184f673 --- /dev/null +++ b/build-script-utils/README.md @@ -0,0 +1,3 @@ +Crate with utility functions for `build.rs` scripts. + +License: Apache-2.0 \ No newline at end of file diff --git a/build-script-utils/src/lib.rs b/build-script-utils/src/lib.rs new file mode 100644 index 000000000..6d3177d51 --- /dev/null +++ b/build-script-utils/src/lib.rs @@ -0,0 +1,5 @@ +//! Crate with utility functions for `build.rs` scripts. + +mod version; + +pub use version::*; diff --git a/build-script-utils/src/version.rs b/build-script-utils/src/version.rs new file mode 100644 index 000000000..599f0d617 --- /dev/null +++ b/build-script-utils/src/version.rs @@ -0,0 +1,55 @@ +use platforms::*; +use std::process::Command; + +/// Generate the `cargo:` key output +pub fn generate_cargo_keys() { + println!( + "cargo:rustc-env=SUBSTRATE_CLI_IMPL_VERSION={}", + get_version(get_commit()) + ) +} + +fn get_platform() -> String { + let env_dash = if TARGET_ENV.is_some() { "-" } else { "" }; + + format!( + "{}-{}{}{}", + TARGET_ARCH.as_str(), + TARGET_OS.as_str(), + env_dash, + TARGET_ENV.map(|x| x.as_str()).unwrap_or(""), + ) +} + +fn get_version(impl_commit: String) -> String { + let commit_dash = if impl_commit.is_empty() { "" } else { "-" }; + + format!( + "{}{}{}-{}", + std::env::var("CARGO_PKG_VERSION").unwrap_or_default(), + commit_dash, + impl_commit, + get_platform(), + ) +} + +fn get_commit() -> String { + let commit = std::env::var("GIT_COMMIT").unwrap_or_default(); + if !commit.is_empty() { + return commit; + } + + let output = Command::new("git").args(&["rev-parse", "--short", "HEAD"]).output(); + + match output { + Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().into(), + Ok(o) => { + println!("cargo:warning=Git command failed with status: {}", o.status); + "unknown".into() + } + Err(err) => { + println!("cargo:warning=Failed to execute git command: {}", err); + "unknown".into() + } + } +} diff --git a/currencies/Cargo.toml b/currencies/Cargo.toml index a7029ed79..a58fd4cf8 100644 --- a/currencies/Cargo.toml +++ b/currencies/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } diff --git a/currencies/src/mock.rs b/currencies/src/mock.rs index 621876964..ef82359e4 100644 --- a/currencies/src/mock.rs +++ b/currencies/src/mock.rs @@ -3,13 +3,13 @@ #![cfg(test)] use super::*; -use frame_support::{construct_runtime, parameter_types}; +use frame_support::{construct_runtime, parameter_types, PalletId}; use orml_traits::parameter_type_with_key; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{AccountIdConversion, IdentityLookup}, - AccountId32, ModuleId, + AccountId32, }; use crate as currencies; @@ -42,6 +42,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } type CurrencyId = u32; @@ -56,7 +57,7 @@ impl pallet_balances::Config for Runtime { type DustRemoval = (); type Event = Event; type ExistentialDeposit = ExistentialDeposit; - type AccountStore = frame_system::Module; + type AccountStore = frame_system::Pallet; type MaxLocks = (); type WeightInfo = (); } @@ -68,7 +69,7 @@ parameter_type_with_key! { } parameter_types! { - pub DustAccount: AccountId = ModuleId(*b"orml/dst").into_account(); + pub DustAccount: AccountId = PalletId(*b"orml/dst").into_account(); } impl orml_tokens::Config for Runtime { @@ -107,10 +108,10 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - Currencies: currencies::{Module, Call, Event}, - Tokens: orml_tokens::{Module, Storage, Event, Config}, - PalletBalances: pallet_balances::{Module, Call, Storage, Config, Event}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Currencies: currencies::{Pallet, Call, Event}, + Tokens: orml_tokens::{Pallet, Storage, Event, Config}, + PalletBalances: pallet_balances::{Pallet, Call, Storage, Config, Event}, } ); diff --git a/gradually-update/Cargo.toml b/gradually-update/Cargo.toml index 6df11cae4..1bde0d610 100644 --- a/gradually-update/Cargo.toml +++ b/gradually-update/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } diff --git a/gradually-update/src/mock.rs b/gradually-update/src/mock.rs index 981492564..410cb935d 100644 --- a/gradually-update/src/mock.rs +++ b/gradually-update/src/mock.rs @@ -39,6 +39,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } parameter_types! { @@ -61,9 +62,8 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - GraduallyUpdateModule: gradually_update::{Module, Storage, Call, Event}, - + System: frame_system::{Pallet, Call, Storage, Config, Event}, + GraduallyUpdateModule: gradually_update::{Pallet, Storage, Call, Event}, } ); diff --git a/nft/Cargo.toml b/nft/Cargo.toml index 1870aa65e..35f1b3cee 100644 --- a/nft/Cargo.toml +++ b/nft/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Acala Developers"] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } @@ -32,4 +32,3 @@ std = [ "frame-support/std", "frame-system/std", ] -disable-tokens-by-owner = [] diff --git a/nft/src/lib.rs b/nft/src/lib.rs index 706a76000..19110231b 100644 --- a/nft/src/lib.rs +++ b/nft/src/lib.rs @@ -136,9 +136,6 @@ pub mod module { StorageDoubleMap<_, Twox64Concat, T::ClassId, Twox64Concat, T::TokenId, TokenInfoOf>; /// Token existence check by owner and class ID. - // TODO: pallet macro doesn't support conditional compiling. Always having `TokensByOwner` storage doesn't hurt but - // it could be removed once conditional compiling supported. - // #[cfg(not(feature = "disable-tokens-by-owner"))] #[pallet::storage] #[pallet::getter(fn tokens_by_owner)] pub type TokensByOwner = @@ -216,11 +213,8 @@ impl Pallet { info.owner = to.clone(); - #[cfg(not(feature = "disable-tokens-by-owner"))] - { - TokensByOwner::::remove(from, token); - TokensByOwner::::insert(to, token, ()); - } + TokensByOwner::::remove(from, token); + TokensByOwner::::insert(to, token, ()); Ok(()) }) @@ -252,7 +246,6 @@ impl Pallet { data, }; Tokens::::insert(class_id, token_id, token_info); - #[cfg(not(feature = "disable-tokens-by-owner"))] TokensByOwner::::insert(owner, (class_id, token_id), ()); Ok(token_id) @@ -274,7 +267,6 @@ impl Pallet { Ok(()) })?; - #[cfg(not(feature = "disable-tokens-by-owner"))] TokensByOwner::::remove(owner, token); Ok(()) @@ -295,10 +287,6 @@ impl Pallet { } pub fn is_owner(account: &T::AccountId, token: (T::ClassId, T::TokenId)) -> bool { - #[cfg(feature = "disable-tokens-by-owner")] - return Tokens::::get(token.0, token.1).map_or(false, |token| token.owner == *account); - - #[cfg(not(feature = "disable-tokens-by-owner"))] TokensByOwner::::contains_key(account, token) } } diff --git a/nft/src/mock.rs b/nft/src/mock.rs index a8d1dae2a..915f67561 100644 --- a/nft/src/mock.rs +++ b/nft/src/mock.rs @@ -40,6 +40,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } impl Config for Runtime { @@ -58,8 +59,8 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - NonFungibleTokenModule: nft::{Module, Storage, Config}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + NonFungibleTokenModule: nft::{Pallet, Storage, Config}, } ); diff --git a/oracle/Cargo.toml b/oracle/Cargo.toml index 8bd2e9df9..54e7bde93 100644 --- a/oracle/Cargo.toml +++ b/oracle/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } diff --git a/oracle/rpc/Cargo.toml b/oracle/rpc/Cargo.toml index 9ab71dbee..d64349197 100644 --- a/oracle/rpc/Cargo.toml +++ b/oracle/rpc/Cargo.toml @@ -3,6 +3,8 @@ name = "orml-oracle-rpc" version = "0.4.1-dev" authors = ["Laminar Developers "] edition = "2018" +license = "Apache-2.0" +description = "RPC module for orml-oracle." [dependencies] codec = { package = "parity-scale-codec", version = "2.0.0" } @@ -13,6 +15,6 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo sp-api = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } -orml-oracle-rpc-runtime-api = { path = "runtime-api" } +orml-oracle-rpc-runtime-api = { path = "runtime-api", version = "0.4.1-dev" } funty = { version = "=1.1.0", default-features = false } # https://github.com/bitvecto-rs/bitvec/issues/105 diff --git a/oracle/rpc/runtime-api/Cargo.toml b/oracle/rpc/runtime-api/Cargo.toml index 3003f32f8..19edb53a2 100644 --- a/oracle/rpc/runtime-api/Cargo.toml +++ b/oracle/rpc/runtime-api/Cargo.toml @@ -3,6 +3,8 @@ name = "orml-oracle-rpc-runtime-api" version = "0.4.1-dev" authors = ["Laminar Developers "] edition = "2018" +license = "Apache-2.0" +description = "Runtime API module for orml-oracle-rpc." [dependencies] codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } diff --git a/oracle/src/lib.rs b/oracle/src/lib.rs index 4292be64f..0716ff3b9 100644 --- a/oracle/src/lib.rs +++ b/oracle/src/lib.rs @@ -139,10 +139,6 @@ pub mod module { #[pallet::getter(fn members)] pub type Members, I: 'static = ()> = StorageValue<_, OrderedSet, ValueQuery>; - #[pallet::storage] - #[pallet::getter(fn nonces)] - pub type Nonces, I: 'static = ()> = StorageMap<_, Twox64Concat, T::AccountId, u32>; - #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { pub members: OrderedSet, diff --git a/oracle/src/mock.rs b/oracle/src/mock.rs index b0173ac2b..569dcdc6a 100644 --- a/oracle/src/mock.rs +++ b/oracle/src/mock.rs @@ -45,6 +45,7 @@ impl frame_system::Config for Test { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } thread_local! { @@ -92,8 +93,8 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - ModuleOracle: oracle::{Module, Storage, Call, Config, Event}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + ModuleOracle: oracle::{Pallet, Storage, Call, Config, Event}, } ); diff --git a/rewards/Cargo.toml b/rewards/Cargo.toml index fb7c747f6..674018a63 100644 --- a/rewards/Cargo.toml +++ b/rewards/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Acala Developers"] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } diff --git a/rewards/src/default_weight.rs b/rewards/src/default_weight.rs deleted file mode 100644 index a2be6a5dc..000000000 --- a/rewards/src/default_weight.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 - -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(clippy::unnecessary_cast)] - -use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight}; - -impl crate::WeightInfo for () { - fn on_initialize(c: u32) -> Weight { - (33_360_000 as Weight) - .saturating_add((23_139_000 as Weight).saturating_mul(c as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) - } -} diff --git a/rewards/src/lib.rs b/rewards/src/lib.rs index 057c7e3d1..55be0d164 100644 --- a/rewards/src/lib.rs +++ b/rewards/src/lib.rs @@ -1,7 +1,6 @@ #![allow(clippy::unused_unit)] #![cfg_attr(not(feature = "std"), no_std)] -mod default_weight; mod mock; mod tests; @@ -37,10 +36,6 @@ pub use module::*; pub mod module { use super::*; - pub trait WeightInfo { - fn on_initialize(c: u32) -> Weight; - } - #[pallet::config] pub trait Config: frame_system::Config { /// The share type of pool. @@ -64,19 +59,10 @@ pub mod module { + FixedPointOperand; /// The reward pool ID type. - type PoolId: Parameter + Member + Copy + FullCodec; + type PoolId: Parameter + Member + Clone + FullCodec; /// The `RewardHandler` - type Handler: RewardHandler< - Self::AccountId, - Self::BlockNumber, - Share = Self::Share, - Balance = Self::Balance, - PoolId = Self::PoolId, - >; - - /// Weight information for extrinsics in this module. - type WeightInfo: WeightInfo; + type Handler: RewardHandler; } /// Stores reward pool info. @@ -95,27 +81,22 @@ pub mod module { pub struct Pallet(PhantomData); #[pallet::hooks] - impl Hooks for Pallet { - fn on_initialize(now: T::BlockNumber) -> Weight { - let mut count = 0; - T::Handler::accumulate_reward(now, |pool, reward_to_accumulate| { - if !reward_to_accumulate.is_zero() { - count += 1; - Pools::::mutate(pool, |pool_info| { - pool_info.total_rewards = pool_info.total_rewards.saturating_add(reward_to_accumulate) - }); - } - }); - T::WeightInfo::on_initialize(count) - } - } + impl Hooks for Pallet {} #[pallet::call] impl Pallet {} } impl Pallet { - pub fn add_share(who: &T::AccountId, pool: T::PoolId, add_amount: T::Share) { + pub fn accumulate_reward(pool: &T::PoolId, reward_increment: T::Balance) { + if !reward_increment.is_zero() { + Pools::::mutate(pool, |pool_info| { + pool_info.total_rewards = pool_info.total_rewards.saturating_add(reward_increment) + }); + } + } + + pub fn add_share(who: &T::AccountId, pool: &T::PoolId, add_amount: T::Share) { if add_amount.is_zero() { return; } @@ -135,7 +116,7 @@ impl Pallet { }); } - pub fn remove_share(who: &T::AccountId, pool: T::PoolId, remove_amount: T::Share) { + pub fn remove_share(who: &T::AccountId, pool: &T::PoolId, remove_amount: T::Share) { if remove_amount.is_zero() { return; } @@ -167,7 +148,7 @@ impl Pallet { }); } - pub fn set_share(who: &T::AccountId, pool: T::PoolId, new_share: T::Share) { + pub fn set_share(who: &T::AccountId, pool: &T::PoolId, new_share: T::Share) { let (share, _) = Self::share_and_withdrawn_reward(pool, who); if new_share > share { @@ -177,7 +158,7 @@ impl Pallet { } } - pub fn claim_rewards(who: &T::AccountId, pool: T::PoolId) { + pub fn claim_rewards(who: &T::AccountId, pool: &T::PoolId) { ShareAndWithdrawnReward::::mutate(pool, who, |(share, withdrawn_rewards)| { if share.is_zero() { return; diff --git a/rewards/src/mock.rs b/rewards/src/mock.rs index 5838f30bc..2a34337b3 100644 --- a/rewards/src/mock.rs +++ b/rewards/src/mock.rs @@ -16,14 +16,11 @@ pub type Balance = u64; pub type Share = u64; pub type PoolId = u32; pub type BlockNumber = u64; -pub type CurrencyId = u32; pub const ALICE: AccountId = 1; pub const BOB: AccountId = 2; pub const CAROL: AccountId = 3; pub const DOT_POOL: PoolId = 1; -pub const BTC_POOL: PoolId = 2; -pub const XBTC_POOL: PoolId = 3; parameter_types! { pub const BlockHashCount: u64 = 250; @@ -52,6 +49,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } thread_local! { @@ -59,41 +57,17 @@ thread_local! { } pub struct Handler; -impl RewardHandler for Handler { - type Share = Share; +impl RewardHandler for Handler { type Balance = Balance; type PoolId = PoolId; - type CurrencyId = CurrencyId; - - fn accumulate_reward( - now: BlockNumber, - mut callback: impl FnMut(Self::PoolId, Self::Balance), - ) -> Vec<(Self::CurrencyId, Self::Balance)> { - if now % 2 == 0 { - let mut total_accumulated_rewards = 0; - let valid_pool_ids = vec![DOT_POOL, BTC_POOL]; - - for (pool, _) in Pools::::iter() { - if valid_pool_ids.contains(&pool) { - let rewards: Balance = 100; - callback(pool, rewards); - total_accumulated_rewards += rewards; - } - } - - vec![(1, total_accumulated_rewards)] - } else { - vec![] - } - } - fn payout(who: &AccountId, pool: Self::PoolId, amount: Self::Balance) { + fn payout(who: &AccountId, pool: &Self::PoolId, amount: Self::Balance) { RECEIVED_PAYOUT.with(|v| { let mut old_map = v.borrow().clone(); - if let Some(before) = old_map.get_mut(&(pool, *who)) { + if let Some(before) = old_map.get_mut(&(*pool, *who)) { *before += amount; } else { - old_map.insert((pool, *who), amount); + old_map.insert((*pool, *who), amount); }; *v.borrow_mut() = old_map; @@ -106,7 +80,6 @@ impl Config for Runtime { type Balance = Balance; type PoolId = PoolId; type Handler = Handler; - type WeightInfo = (); } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -118,8 +91,8 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - RewardsModule: rewards::{Module, Storage, Call}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + RewardsModule: rewards::{Pallet, Storage, Call}, } ); diff --git a/rewards/src/tests.rs b/rewards/src/tests.rs index b3d8b9d30..cc2380db8 100644 --- a/rewards/src/tests.rs +++ b/rewards/src/tests.rs @@ -18,7 +18,7 @@ fn add_share_should_work() { ); assert_eq!(RewardsModule::share_and_withdrawn_reward(DOT_POOL, ALICE), (0, 0)); - RewardsModule::add_share(&ALICE, DOT_POOL, 0); + RewardsModule::add_share(&ALICE, &DOT_POOL, 0); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -29,7 +29,7 @@ fn add_share_should_work() { ); assert_eq!(RewardsModule::share_and_withdrawn_reward(DOT_POOL, ALICE), (0, 0)); - RewardsModule::add_share(&ALICE, DOT_POOL, 100); + RewardsModule::add_share(&ALICE, &DOT_POOL, 100); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -54,7 +54,7 @@ fn add_share_should_work() { ); assert_eq!(RewardsModule::share_and_withdrawn_reward(DOT_POOL, BOB), (0, 0)); - RewardsModule::add_share(&BOB, DOT_POOL, 50); + RewardsModule::add_share(&BOB, &DOT_POOL, 50); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -65,7 +65,7 @@ fn add_share_should_work() { ); assert_eq!(RewardsModule::share_and_withdrawn_reward(DOT_POOL, BOB), (50, 2500)); - RewardsModule::add_share(&ALICE, DOT_POOL, 150); + RewardsModule::add_share(&ALICE, &DOT_POOL, 150); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -81,12 +81,12 @@ fn add_share_should_work() { #[test] fn claim_rewards_should_work() { ExtBuilder::default().build().execute_with(|| { - RewardsModule::add_share(&ALICE, DOT_POOL, 100); - RewardsModule::add_share(&BOB, DOT_POOL, 100); + RewardsModule::add_share(&ALICE, &DOT_POOL, 100); + RewardsModule::add_share(&BOB, &DOT_POOL, 100); Pools::::mutate(DOT_POOL, |pool_info| { pool_info.total_rewards += 5000; }); - RewardsModule::add_share(&CAROL, DOT_POOL, 200); + RewardsModule::add_share(&CAROL, &DOT_POOL, 200); assert_eq!( RewardsModule::pools(DOT_POOL), @@ -112,7 +112,7 @@ fn claim_rewards_should_work() { 0 ); - RewardsModule::claim_rewards(&ALICE, DOT_POOL); + RewardsModule::claim_rewards(&ALICE, &DOT_POOL); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -127,7 +127,7 @@ fn claim_rewards_should_work() { 2500 ); - RewardsModule::claim_rewards(&CAROL, DOT_POOL); + RewardsModule::claim_rewards(&CAROL, &DOT_POOL); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -142,7 +142,7 @@ fn claim_rewards_should_work() { 0 ); - RewardsModule::claim_rewards(&BOB, DOT_POOL); + RewardsModule::claim_rewards(&BOB, &DOT_POOL); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -162,8 +162,8 @@ fn claim_rewards_should_work() { #[test] fn remove_share_should_work() { ExtBuilder::default().build().execute_with(|| { - RewardsModule::add_share(&ALICE, DOT_POOL, 100); - RewardsModule::add_share(&BOB, DOT_POOL, 100); + RewardsModule::add_share(&ALICE, &DOT_POOL, 100); + RewardsModule::add_share(&BOB, &DOT_POOL, 100); Pools::::mutate(DOT_POOL, |pool_info| { pool_info.total_rewards += 10000; }); @@ -188,7 +188,7 @@ fn remove_share_should_work() { ); // remove amount is zero, do not claim interest - RewardsModule::remove_share(&ALICE, DOT_POOL, 0); + RewardsModule::remove_share(&ALICE, &DOT_POOL, 0); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -203,7 +203,7 @@ fn remove_share_should_work() { 0 ); - RewardsModule::remove_share(&BOB, DOT_POOL, 50); + RewardsModule::remove_share(&BOB, &DOT_POOL, 50); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -218,7 +218,7 @@ fn remove_share_should_work() { 5000 ); - RewardsModule::remove_share(&ALICE, DOT_POOL, 101); + RewardsModule::remove_share(&ALICE, &DOT_POOL, 101); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -248,7 +248,7 @@ fn set_share_should_work() { ); assert_eq!(RewardsModule::share_and_withdrawn_reward(DOT_POOL, ALICE), (0, 0)); - RewardsModule::set_share(&ALICE, DOT_POOL, 100); + RewardsModule::set_share(&ALICE, &DOT_POOL, 100); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -271,7 +271,7 @@ fn set_share_should_work() { } ); - RewardsModule::set_share(&ALICE, DOT_POOL, 500); + RewardsModule::set_share(&ALICE, &DOT_POOL, 500); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -286,7 +286,7 @@ fn set_share_should_work() { 0 ); - RewardsModule::set_share(&ALICE, DOT_POOL, 100); + RewardsModule::set_share(&ALICE, &DOT_POOL, 100); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -304,43 +304,18 @@ fn set_share_should_work() { } #[test] -fn on_initialize_should_work() { +fn accumulate_reward_should_work() { ExtBuilder::default().build().execute_with(|| { - Pools::::mutate(DOT_POOL, |pool_info| { - pool_info.total_rewards += 100; - }); - Pools::::mutate(BTC_POOL, |pool_info| { - pool_info.total_rewards += 200; - }); - Pools::::mutate(XBTC_POOL, |pool_info| { - pool_info.total_rewards += 300; - }); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { total_shares: 0, - total_rewards: 100, - total_withdrawn_rewards: 0, - } - ); - assert_eq!( - RewardsModule::pools(BTC_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 200, - total_withdrawn_rewards: 0, - } - ); - assert_eq!( - RewardsModule::pools(XBTC_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 300, + total_rewards: 0, total_withdrawn_rewards: 0, } ); - RewardsModule::on_initialize(1); + RewardsModule::accumulate_reward(&DOT_POOL, 100); assert_eq!( RewardsModule::pools(DOT_POOL), PoolInfo { @@ -349,47 +324,5 @@ fn on_initialize_should_work() { total_withdrawn_rewards: 0, } ); - assert_eq!( - RewardsModule::pools(BTC_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 200, - total_withdrawn_rewards: 0, - } - ); - assert_eq!( - RewardsModule::pools(XBTC_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 300, - total_withdrawn_rewards: 0, - } - ); - - RewardsModule::on_initialize(2); - assert_eq!( - RewardsModule::pools(DOT_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 200, - total_withdrawn_rewards: 0, - } - ); - assert_eq!( - RewardsModule::pools(BTC_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 300, - total_withdrawn_rewards: 0, - } - ); - assert_eq!( - RewardsModule::pools(XBTC_POOL), - PoolInfo { - total_shares: 0, - total_rewards: 300, - total_withdrawn_rewards: 0, - } - ); }); } diff --git a/tokens/Cargo.toml b/tokens/Cargo.toml index 641eb41b2..735420dd3 100644 --- a/tokens/Cargo.toml +++ b/tokens/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } @@ -35,3 +35,7 @@ std = [ "frame-system/std", "orml-traits/std", ] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] diff --git a/tokens/src/imbalances.rs b/tokens/src/imbalances.rs index 76faac858..084a3b9a5 100644 --- a/tokens/src/imbalances.rs +++ b/tokens/src/imbalances.rs @@ -1,7 +1,7 @@ // wrapping these imbalances in a private module is necessary to ensure absolute // privacy of the inner member. use crate::{Config, TotalIssuance}; -use frame_support::traits::{Get, Imbalance, TryDrop}; +use frame_support::traits::{Get, Imbalance, SameOrOther, TryDrop}; use sp_runtime::traits::{Saturating, Zero}; use sp_std::{marker, mem, result}; @@ -21,6 +21,12 @@ impl> PositiveImbalance> Default for PositiveImbalance { + fn default() -> Self { + Self::zero() + } +} + /// Opaque, move-only struct with private fields that serves as a token /// denoting that funds have been destroyed without any equal and opposite /// accounting. @@ -37,6 +43,12 @@ impl> NegativeImbalance> Default for NegativeImbalance { + fn default() -> Self { + Self::zero() + } +} + impl> TryDrop for PositiveImbalance { fn try_drop(self) -> result::Result<(), Self> { self.drop_zero() @@ -73,14 +85,18 @@ impl> Imbalance for Pos self.0 = self.0.saturating_add(other.0); mem::forget(other); } - fn offset(self, other: Self::Opposite) -> result::Result { + // allow to make the impl same with `pallet-balances` + #[allow(clippy::comparison_chain)] + fn offset(self, other: Self::Opposite) -> SameOrOther { let (a, b) = (self.0, other.0); mem::forget((self, other)); - if a >= b { - Ok(Self::new(a - b)) + if a > b { + SameOrOther::Same(Self::new(a - b)) + } else if b > a { + SameOrOther::Other(NegativeImbalance::new(b - a)) } else { - Err(NegativeImbalance::new(b - a)) + SameOrOther::None } } fn peek(&self) -> T::Balance { @@ -124,14 +140,18 @@ impl> Imbalance for Neg self.0 = self.0.saturating_add(other.0); mem::forget(other); } - fn offset(self, other: Self::Opposite) -> result::Result { + // allow to make the impl same with `pallet-balances` + #[allow(clippy::comparison_chain)] + fn offset(self, other: Self::Opposite) -> SameOrOther { let (a, b) = (self.0, other.0); mem::forget((self, other)); - if a >= b { - Ok(Self::new(a - b)) + if a > b { + SameOrOther::Same(Self::new(a - b)) + } else if b > a { + SameOrOther::Other(PositiveImbalance::new(b - a)) } else { - Err(PositiveImbalance::new(b - a)) + SameOrOther::None } } fn peek(&self) -> T::Balance { diff --git a/tokens/src/lib.rs b/tokens/src/lib.rs index 717698ff4..6c1202fca 100644 --- a/tokens/src/lib.rs +++ b/tokens/src/lib.rs @@ -47,7 +47,7 @@ use frame_support::{ LockableCurrency as PalletLockableCurrency, ReservableCurrency as PalletReservableCurrency, SignedImbalance, WithdrawReasons, }, - transactional, + transactional, PalletId, }; use frame_system::{ensure_signed, pallet_prelude::*}; use orml_traits::{ @@ -61,7 +61,7 @@ use sp_runtime::{ AccountIdConversion, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, Saturating, StaticLookup, Zero, }, - DispatchError, DispatchResult, ModuleId, RuntimeDebug, + DispatchError, DispatchResult, RuntimeDebug, }; use sp_std::{ convert::{Infallible, TryFrom, TryInto}, @@ -348,7 +348,7 @@ pub mod module { impl Pallet { /// Check whether account_id is a module account pub(crate) fn is_module_account_id(account_id: &T::AccountId) -> bool { - ModuleId::try_from_account(account_id).is_some() + PalletId::try_from_account(account_id).is_some() } pub(crate) fn try_mutate_account( @@ -381,10 +381,10 @@ impl Pallet { // If existed before, decrease account provider. // Ignore the result, because if it failed means that these’s remain consumers, // and the account storage in frame_system shouldn't be repeaded. - let _ = frame_system::Module::::dec_providers(who); + let _ = frame_system::Pallet::::dec_providers(who); } else if !existed && exists { // if new, increase account provider - frame_system::Module::::inc_providers(who); + frame_system::Pallet::::inc_providers(who); } if let Some(dust_amount) = handle_dust { @@ -446,13 +446,13 @@ impl Pallet { >::remove(who, currency_id); if existed { // decrease account ref count when destruct lock - frame_system::Module::::dec_consumers(who); + frame_system::Pallet::::dec_consumers(who); } } else { >::insert(who, currency_id, locks); if !existed { // increase account ref count when initialize lock - if frame_system::Module::::inc_consumers(who).is_err() { + if frame_system::Pallet::::inc_consumers(who).is_err() { // No providers for the locks. This is impossible under normal circumstances // since the funds that are under the lock will themselves be stored in the // account and therefore will need a reference. diff --git a/tokens/src/mock.rs b/tokens/src/mock.rs index a46a04667..c83901dae 100644 --- a/tokens/src/mock.rs +++ b/tokens/src/mock.rs @@ -54,6 +54,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } thread_local! { @@ -75,7 +76,7 @@ impl Contains for TenToFourteen { fn add(new: &AccountId) { TEN_TO_FOURTEEN.with(|v| { let mut members = v.borrow_mut(); - members.push(*new); + members.push(new.clone()); members.sort(); }) } @@ -95,12 +96,12 @@ parameter_types! { pub const ProposalBondMinimum: u64 = 1; pub const SpendPeriod: u64 = 2; pub const Burn: Permill = Permill::from_percent(50); - pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry"); + pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); pub const GetTokenId: CurrencyId = DOT; } impl pallet_treasury::Config for Runtime { - type ModuleId = TreasuryModuleId; + type PalletId = TreasuryPalletId; type Currency = CurrencyAdapter; type ApproveOrigin = frame_system::EnsureRoot; type RejectOrigin = frame_system::EnsureRoot; @@ -164,7 +165,7 @@ impl ChangeMembers for TestChangeMembers { } parameter_types! { - pub const ElectionsPhragmenModuleId: LockIdentifier = *b"phrelect"; + pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; pub const CandidacyBond: u64 = 3; pub const VotingBond: u64 = 2; pub const DesiredMembers: u32 = 2; @@ -175,7 +176,7 @@ parameter_types! { } impl pallet_elections_phragmen::Config for Runtime { - type ModuleId = ElectionsPhragmenModuleId; + type PalletId = ElectionsPhragmenPalletId; type Event = Event; type Currency = CurrencyAdapter; type CurrencyToVote = SaturatingCurrencyToVote; @@ -203,7 +204,7 @@ parameter_type_with_key! { } parameter_types! { - pub DustAccount: AccountId = ModuleId(*b"orml/dst").into_account(); + pub DustAccount: AccountId = PalletId(*b"orml/dst").into_account(); } impl Config for Runtime { @@ -226,10 +227,10 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - Tokens: tokens::{Module, Storage, Event, Config}, - Treasury: pallet_treasury::{Module, Call, Storage, Config, Event}, - ElectionsPhragmen: pallet_elections_phragmen::{Module, Call, Storage, Event}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Tokens: tokens::{Pallet, Storage, Event, Config}, + Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event}, + ElectionsPhragmen: pallet_elections_phragmen::{Pallet, Call, Storage, Event}, } ); diff --git a/traits/Cargo.toml b/traits/Cargo.toml index e99c0d48b..2c92c0f16 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -8,15 +8,16 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } num-traits = { version = "0.2.14", default-features = false } -impl-trait-for-tuples = "0.1.3" +impl-trait-for-tuples = "0.2.1" frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } orml-utilities = { path = "../utilities", version = "0.4.1-dev", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } funty = { version = "=1.1.0", default-features = false } # https://github.com/bitvecto-rs/bitvec/issues/105 @@ -31,4 +32,5 @@ std = [ "num-traits/std", "frame-support/std", "orml-utilities/std", + "xcm/std", ] diff --git a/traits/src/lib.rs b/traits/src/lib.rs index d8dfbab39..a8962dbcf 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -1,7 +1,8 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; -use sp_runtime::RuntimeDebug; +use impl_trait_for_tuples::impl_for_tuples; +use sp_runtime::{DispatchResult, RuntimeDebug}; use sp_std::{ cmp::{Eq, PartialEq}, prelude::Vec, @@ -27,6 +28,7 @@ pub mod auction; pub mod currency; pub mod data_provider; pub mod get_by_key; +pub mod location; pub mod nft; pub mod price; pub mod rewards; @@ -64,11 +66,23 @@ pub struct TimestampedValue { pub timestamp: Moment, } -#[impl_trait_for_tuples::impl_for_tuples(30)] +#[impl_for_tuples(30)] pub trait Happened { fn happened(t: &T); } +pub trait Handler { + fn handle(t: &T) -> DispatchResult; +} + +#[impl_for_tuples(30)] +impl Handler for Tuple { + fn handle(t: &T) -> DispatchResult { + for_tuples!( #( Tuple::handle(t); )* ); + Ok(()) + } +} + pub trait Contains { fn contains(t: &T) -> bool; } diff --git a/traits/src/location.rs b/traits/src/location.rs new file mode 100644 index 000000000..a0b9bdd0b --- /dev/null +++ b/traits/src/location.rs @@ -0,0 +1,123 @@ +use xcm::v0::{ + Junction::{self, *}, + MultiAsset, MultiLocation, +}; + +pub trait Parse { + /// Returns the "chain" location part. It could be parent, sibling + /// parachain, or child parachain. + fn chain_part(&self) -> Option; + /// Returns "non-chain" location part. + fn non_chain_part(&self) -> Option; +} + +fn is_chain_junction(junction: Option<&Junction>) -> bool { + matches!(junction, Some(Parent) | Some(Parachain { id: _ })) +} + +impl Parse for MultiLocation { + fn chain_part(&self) -> Option { + match (self.first(), self.at(1)) { + (Some(Parent), Some(Parachain { id })) => Some((Parent, Parachain { id: *id }).into()), + (Some(Parent), _) => Some(Parent.into()), + (Some(Parachain { id }), _) => Some(Parachain { id: *id }.into()), + _ => None, + } + } + + fn non_chain_part(&self) -> Option { + let mut location = self.clone(); + while is_chain_junction(location.first()) { + let _ = location.take_first(); + } + + if location != MultiLocation::Null { + Some(location) + } else { + None + } + } +} + +pub trait Reserve { + /// Returns assets reserve location. + fn reserve(&self) -> Option; +} + +impl Reserve for MultiAsset { + fn reserve(&self) -> Option { + if let MultiAsset::ConcreteFungible { id, .. } = self { + id.chain_part() + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const PARACHAIN: Junction = Parachain { id: 1 }; + const GENERAL_INDEX: Junction = GeneralIndex { id: 1 }; + + fn concrete_fungible(id: MultiLocation) -> MultiAsset { + MultiAsset::ConcreteFungible { id, amount: 1 } + } + + #[test] + fn parent_as_reserve_chain() { + assert_eq!( + concrete_fungible(MultiLocation::X2(Parent, GENERAL_INDEX)).reserve(), + Some(Parent.into()) + ); + } + + #[test] + fn sibling_parachain_as_reserve_chain() { + assert_eq!( + concrete_fungible(MultiLocation::X3(Parent, PARACHAIN, GENERAL_INDEX)).reserve(), + Some((Parent, PARACHAIN).into()) + ); + } + + #[test] + fn child_parachain_as_reserve_chain() { + assert_eq!( + concrete_fungible(MultiLocation::X2(PARACHAIN, GENERAL_INDEX)).reserve(), + Some(PARACHAIN.into()) + ); + } + + #[test] + fn no_reserve_chain() { + assert_eq!( + concrete_fungible(MultiLocation::X1(GeneralKey("DOT".into()))).reserve(), + None + ); + } + + #[test] + fn non_chain_part_works() { + assert_eq!(MultiLocation::X1(Parent).non_chain_part(), None); + assert_eq!(MultiLocation::X2(Parent, PARACHAIN).non_chain_part(), None); + assert_eq!(MultiLocation::X1(PARACHAIN).non_chain_part(), None); + + assert_eq!( + MultiLocation::X2(Parent, GENERAL_INDEX).non_chain_part(), + Some(GENERAL_INDEX.into()) + ); + assert_eq!( + MultiLocation::X3(Parent, GENERAL_INDEX, GENERAL_INDEX).non_chain_part(), + Some((GENERAL_INDEX, GENERAL_INDEX).into()) + ); + assert_eq!( + MultiLocation::X3(Parent, PARACHAIN, GENERAL_INDEX).non_chain_part(), + Some(GENERAL_INDEX.into()) + ); + assert_eq!( + MultiLocation::X2(PARACHAIN, GENERAL_INDEX).non_chain_part(), + Some(GENERAL_INDEX.into()) + ); + } +} diff --git a/traits/src/rewards.rs b/traits/src/rewards.rs index e53b905a6..ebf21f4b8 100644 --- a/traits/src/rewards.rs +++ b/traits/src/rewards.rs @@ -1,27 +1,15 @@ use codec::FullCodec; use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; -use sp_std::{fmt::Debug, vec::Vec}; +use sp_std::fmt::Debug; /// Hooks to manage reward pool -pub trait RewardHandler { - /// The share type of pool - type Share: AtLeast32BitUnsigned + Default + Copy + MaybeSerializeDeserialize + Debug; - +pub trait RewardHandler { /// The reward balance type type Balance: AtLeast32BitUnsigned + Default + Copy + MaybeSerializeDeserialize + Debug; /// The reward pool ID type - type PoolId: Copy + FullCodec; - - /// The currency type - type CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug; - - /// Accumulate rewards - fn accumulate_reward( - now: BlockNumber, - callback: impl FnMut(Self::PoolId, Self::Balance), - ) -> Vec<(Self::CurrencyId, Self::Balance)>; + type PoolId: FullCodec; /// Payout the reward to `who` - fn payout(who: &AccountId, pool: Self::PoolId, amount: Self::Balance); + fn payout(who: &AccountId, pool: &Self::PoolId, amount: Self::Balance); } diff --git a/unknown-tokens/Cargo.toml b/unknown-tokens/Cargo.toml new file mode 100644 index 000000000..3518ee05d --- /dev/null +++ b/unknown-tokens/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "orml-unknown-tokens" +description = "Unknown tokens module that implements `UnknownAsset` trait." +repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/unknown-tokens" +license = "Apache-2.0" +version = "0.4.1-dev" +authors = ["Acala Developers"] +edition = "2018" + +[dependencies] +serde = { version = "1.0.124", optional = true } +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } + +xcm = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } + +orml-xcm-support = { path = "../xcm-support", default-features = false } + +[dev-dependencies] +sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sp-std/std", + "frame-support/std", + "frame-system/std", + "xcm/std", + "orml-xcm-support/std", +] diff --git a/unknown-tokens/src/lib.rs b/unknown-tokens/src/lib.rs new file mode 100644 index 000000000..e048ba370 --- /dev/null +++ b/unknown-tokens/src/lib.rs @@ -0,0 +1,115 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unused_unit)] + +use frame_support::pallet_prelude::*; +use sp_std::vec::Vec; +use xcm::v0::{MultiAsset, MultiLocation}; + +use orml_xcm_support::UnknownAsset; + +pub use module::*; + +mod mock; +mod tests; + +#[frame_support::pallet] +pub mod module { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From + IsType<::Event>; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// Deposit success. [asset, to] + Deposited(MultiAsset, MultiLocation), + /// Withdraw success. [asset, from] + Withdrawn(MultiAsset, MultiLocation), + } + + #[pallet::error] + pub enum Error { + /// The balance is too low. + BalanceTooLow, + /// The operation will cause balance to overflow. + BalanceOverflow, + /// Unhandled asset. + UnhandledAsset, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::hooks] + impl Hooks for Pallet {} + + /// Concrete fungible balances under a given location and a concrete + /// fungible id. + /// + /// double_map: who, asset_id => u128 + #[pallet::storage] + #[pallet::getter(fn concrete_fungible_balances)] + pub(crate) type ConcreteFungibleBalances = + StorageDoubleMap<_, Blake2_128Concat, MultiLocation, Blake2_128Concat, MultiLocation, u128, ValueQuery>; + + /// Abstract fungible balances under a given location and a abstract + /// fungible id. + /// + /// double_map: who, asset_id => u128 + #[pallet::storage] + #[pallet::getter(fn abstract_fungible_balances)] + pub(crate) type AbstractFungibleBalances = + StorageDoubleMap<_, Blake2_128Concat, MultiLocation, Blake2_128Concat, Vec, u128, ValueQuery>; + + #[pallet::call] + impl Pallet {} +} + +impl UnknownAsset for Pallet { + fn deposit(asset: &MultiAsset, to: &MultiLocation) -> DispatchResult { + match asset { + MultiAsset::ConcreteFungible { id, amount } => { + ConcreteFungibleBalances::::try_mutate(to, id, |b| -> DispatchResult { + *b = b.checked_add(*amount).ok_or(Error::::BalanceOverflow)?; + Ok(()) + }) + } + MultiAsset::AbstractFungible { id, amount } => { + AbstractFungibleBalances::::try_mutate(to, id, |b| -> DispatchResult { + *b = b.checked_add(*amount).ok_or(Error::::BalanceOverflow)?; + Ok(()) + }) + } + _ => Err(Error::::UnhandledAsset.into()), + }?; + + Self::deposit_event(Event::Deposited(asset.clone(), to.clone())); + + Ok(()) + } + + fn withdraw(asset: &MultiAsset, from: &MultiLocation) -> DispatchResult { + match asset { + MultiAsset::ConcreteFungible { id, amount } => { + ConcreteFungibleBalances::::try_mutate(from, id, |b| -> DispatchResult { + *b = b.checked_sub(*amount).ok_or(Error::::BalanceTooLow)?; + Ok(()) + }) + } + MultiAsset::AbstractFungible { id, amount } => { + AbstractFungibleBalances::::try_mutate(from, id, |b| -> DispatchResult { + *b = b.checked_sub(*amount).ok_or(Error::::BalanceTooLow)?; + Ok(()) + }) + } + _ => Err(Error::::UnhandledAsset.into()), + }?; + + Self::deposit_event(Event::Withdrawn(asset.clone(), from.clone())); + + Ok(()) + } +} diff --git a/unknown-tokens/src/mock.rs b/unknown-tokens/src/mock.rs new file mode 100644 index 000000000..b8e297a15 --- /dev/null +++ b/unknown-tokens/src/mock.rs @@ -0,0 +1,74 @@ +//! Mocks for the unknown pallet. + +#![cfg(test)] + +use super::*; +use crate as unknown_tokens; + +use frame_support::{construct_runtime, parameter_types}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; + +pub type AccountId = AccountId32; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +impl Config for Runtime { + type Event = Event; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + UnknownTokens: unknown_tokens::{Pallet, Storage, Event}, + } +); + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/unknown-tokens/src/tests.rs b/unknown-tokens/src/tests.rs new file mode 100644 index 000000000..cc5c60686 --- /dev/null +++ b/unknown-tokens/src/tests.rs @@ -0,0 +1,144 @@ +//! Unit tests for unknown tokens pallet. + +#![cfg(test)] + +use super::*; +use mock::{Event, *}; + +use frame_support::{assert_err, assert_ok}; +use xcm::v0::Junction; + +const MOCK_RECIPIENT: MultiLocation = MultiLocation::X1(Junction::Parent); +const MOCK_CONCRETE_FUNGIBLE_ID: MultiLocation = MultiLocation::X1(Junction::Parent); + +fn mock_abstract_fungible_id() -> Vec { + vec![1] +} + +fn concrete_fungible(amount: u128) -> MultiAsset { + MultiAsset::ConcreteFungible { + id: MOCK_CONCRETE_FUNGIBLE_ID, + amount, + } +} + +fn abstract_fungible(amount: u128) -> MultiAsset { + MultiAsset::AbstractFungible { + id: mock_abstract_fungible_id(), + amount, + } +} + +#[test] +fn deposit_concrete_fungible_asset_works() { + ExtBuilder.build().execute_with(|| { + let asset = concrete_fungible(3); + assert_ok!(UnknownTokens::deposit(&asset, &MOCK_RECIPIENT)); + assert_eq!( + UnknownTokens::concrete_fungible_balances(&MOCK_RECIPIENT, &MOCK_CONCRETE_FUNGIBLE_ID), + 3 + ); + + let deposited_event = Event::unknown_tokens(crate::Event::Deposited(asset, MOCK_RECIPIENT)); + assert!(System::events().iter().any(|record| record.event == deposited_event)); + + // overflow case + let max_asset = concrete_fungible(u128::max_value()); + assert_err!( + UnknownTokens::deposit(&max_asset, &MOCK_RECIPIENT), + Error::::BalanceOverflow + ); + }); +} + +#[test] +fn deposit_abstract_fungible_asset() { + ExtBuilder.build().execute_with(|| { + let asset = abstract_fungible(3); + assert_ok!(UnknownTokens::deposit(&asset, &MOCK_RECIPIENT)); + assert_eq!( + UnknownTokens::abstract_fungible_balances(&MOCK_RECIPIENT, &mock_abstract_fungible_id()), + 3 + ); + + let deposited_event = Event::unknown_tokens(crate::Event::Deposited(asset, MOCK_RECIPIENT)); + assert!(System::events().iter().any(|record| record.event == deposited_event)); + + // overflow case + let max_asset = abstract_fungible(u128::max_value()); + assert_err!( + UnknownTokens::deposit(&max_asset, &MOCK_RECIPIENT), + Error::::BalanceOverflow + ); + assert_eq!( + UnknownTokens::abstract_fungible_balances(&MOCK_RECIPIENT, &mock_abstract_fungible_id()), + 3 + ); + }); +} + +#[test] +fn deposit_unhandled_asset_should_fail() { + ExtBuilder.build().execute_with(|| { + assert_err!( + UnknownTokens::deposit(&MultiAsset::All, &MOCK_RECIPIENT), + Error::::UnhandledAsset + ); + }); +} + +#[test] +fn withdraw_concrete_fungible_asset_works() { + ExtBuilder.build().execute_with(|| { + ConcreteFungibleBalances::::insert(&MOCK_RECIPIENT, &MOCK_CONCRETE_FUNGIBLE_ID, 3); + + let asset = concrete_fungible(3); + assert_ok!(UnknownTokens::withdraw(&asset, &MOCK_RECIPIENT)); + assert_eq!( + UnknownTokens::concrete_fungible_balances(&MOCK_RECIPIENT, &MOCK_CONCRETE_FUNGIBLE_ID), + 0 + ); + + let withdrawn_event = Event::unknown_tokens(crate::Event::Withdrawn(asset.clone(), MOCK_RECIPIENT)); + assert!(System::events().iter().any(|record| record.event == withdrawn_event)); + + // balance too low case + assert_err!( + UnknownTokens::withdraw(&asset, &MOCK_RECIPIENT), + Error::::BalanceTooLow + ); + }); +} + +#[test] +fn withdraw_abstract_fungible_asset_works() { + ExtBuilder.build().execute_with(|| { + AbstractFungibleBalances::::insert(&MOCK_RECIPIENT, &mock_abstract_fungible_id(), 3); + + let asset = abstract_fungible(3); + assert_ok!(UnknownTokens::withdraw(&asset, &MOCK_RECIPIENT)); + assert_eq!( + UnknownTokens::abstract_fungible_balances(&MOCK_RECIPIENT, &mock_abstract_fungible_id()), + 0 + ); + + let withdrawn_event = Event::unknown_tokens(crate::Event::Withdrawn(asset.clone(), MOCK_RECIPIENT)); + assert!(System::events().iter().any(|record| record.event == withdrawn_event)); + + // balance too low case + assert_err!( + UnknownTokens::withdraw(&asset, &MOCK_RECIPIENT), + Error::::BalanceTooLow + ); + }); +} + +#[test] +fn withdraw_unhandled_asset_should_fail() { + ExtBuilder.build().execute_with(|| { + assert_err!( + UnknownTokens::withdraw(&MultiAsset::All, &MOCK_RECIPIENT), + Error::::UnhandledAsset + ); + }); +} diff --git a/utilities/Cargo.toml b/utilities/Cargo.toml index 955ebc0ec..4b505a7a6 100644 --- a/utilities/Cargo.toml +++ b/utilities/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } @@ -18,7 +18,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", funty = { version = "=1.1.0", default-features = false } # https://github.com/bitvecto-rs/bitvec/issues/105 [dev-dependencies] -serde_json = "1.0.53" +serde_json = "1.0.64" frame-system = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } [features] diff --git a/vesting/Cargo.toml b/vesting/Cargo.toml index bb4cdc717..064519e91 100644 --- a/vesting/Cargo.toml +++ b/vesting/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Laminar Developers "] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } @@ -34,3 +34,7 @@ std = [ "frame-support/std", "frame-system/std", ] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] diff --git a/vesting/src/lib.rs b/vesting/src/lib.rs index b314dfb0b..b913d0516 100644 --- a/vesting/src/lib.rs +++ b/vesting/src/lib.rs @@ -277,7 +277,7 @@ impl Pallet { /// Returns locked balance based on current block number. fn locked_balance(who: &T::AccountId) -> BalanceOf { - let now = >::block_number(); + let now = >::block_number(); >::mutate_exists(who, |maybe_schedules| { let total = if let Some(schedules) = maybe_schedules.as_mut() { let mut total: BalanceOf = Zero::zero(); diff --git a/vesting/src/mock.rs b/vesting/src/mock.rs index 7274ef9c7..e3b250191 100644 --- a/vesting/src/mock.rs +++ b/vesting/src/mock.rs @@ -38,6 +38,7 @@ impl frame_system::Config for Runtime { type BaseCallFilter = (); type SystemWeightInfo = (); type SS58Prefix = (); + type OnSetCode = (); } type Balance = u64; @@ -52,7 +53,7 @@ impl pallet_balances::Config for Runtime { type DustRemoval = (); type Event = Event; type ExistentialDeposit = ExistentialDeposit; - type AccountStore = frame_system::Module; + type AccountStore = frame_system::Pallet; type MaxLocks = (); type WeightInfo = (); } @@ -92,9 +93,9 @@ construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Module, Call, Storage, Config, Event}, - Vesting: vesting::{Module, Storage, Call, Event, Config}, - PalletBalances: pallet_balances::{Module, Call, Storage, Config, Event}, + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Vesting: vesting::{Pallet, Storage, Call, Event, Config}, + PalletBalances: pallet_balances::{Pallet, Call, Storage, Config, Event}, } ); diff --git a/xcm-support/Cargo.toml b/xcm-support/Cargo.toml index 045d9898c..d6b1ae783 100644 --- a/xcm-support/Cargo.toml +++ b/xcm-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orml-xcm-support" -description = "Crosschain token transfer" +description = "Supporting module for XCM integration." repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/xcm-support" license = "Apache-2.0" version = "0.4.1-dev" diff --git a/xcm-support/README.md b/xcm-support/README.md new file mode 100644 index 000000000..11fa712a6 --- /dev/null +++ b/xcm-support/README.md @@ -0,0 +1,6 @@ +# XCM Support Module. + +## Overview + +The XCM support module provides supporting traits, types and implementations, +to support cross-chain message(XCM) integration with ORML modules. diff --git a/xcm-support/src/currency_adapter.rs b/xcm-support/src/currency_adapter.rs new file mode 100644 index 000000000..8a9b820bf --- /dev/null +++ b/xcm-support/src/currency_adapter.rs @@ -0,0 +1,108 @@ +use codec::FullCodec; +use sp_runtime::traits::{Convert, MaybeSerializeDeserialize, SaturatedConversion}; +use sp_std::{ + cmp::{Eq, PartialEq}, + fmt::Debug, + marker::PhantomData, + prelude::*, + result, +}; + +use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result}; +use xcm_executor::traits::{LocationConversion, MatchesFungible, TransactAsset}; + +use crate::UnknownAsset as UnknownAssetT; + +/// Asset transaction errors. +enum Error { + /// Failed to match fungible. + FailedToMatchFungible, + /// `MultiLocation` to `AccountId` Conversion failed. + AccountIdConversionFailed, + /// `CurrencyId` conversion failed. + CurrencyIdConversionFailed, +} + +impl From for XcmError { + fn from(e: Error) -> Self { + match e { + Error::FailedToMatchFungible => XcmError::FailedToTransactAsset("FailedToMatchFungible"), + Error::AccountIdConversionFailed => XcmError::FailedToTransactAsset("AccountIdConversionFailed"), + Error::CurrencyIdConversionFailed => XcmError::FailedToTransactAsset("CurrencyIdConversionFailed"), + } + } +} + +/// The `TransactAsset` implementation, to handle `MultiAsset` deposit/withdraw. +/// +/// If the asset is known, deposit/withdraw will be handled by `MultiCurrency`, +/// else by `UnknownAsset` if unknown. +pub struct MultiCurrencyAdapter< + MultiCurrency, + UnknownAsset, + Matcher, + AccountId, + AccountIdConvert, + CurrencyId, + CurrencyIdConvert, +>( + PhantomData<( + MultiCurrency, + UnknownAsset, + Matcher, + AccountId, + AccountIdConvert, + CurrencyId, + CurrencyIdConvert, + )>, +); + +impl< + MultiCurrency: orml_traits::MultiCurrency, + UnknownAsset: UnknownAssetT, + Matcher: MatchesFungible, + AccountId: sp_std::fmt::Debug, + AccountIdConvert: LocationConversion, + CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug, + CurrencyIdConvert: Convert>, + > TransactAsset + for MultiCurrencyAdapter< + MultiCurrency, + UnknownAsset, + Matcher, + AccountId, + AccountIdConvert, + CurrencyId, + CurrencyIdConvert, + > +{ + fn deposit_asset(asset: &MultiAsset, location: &MultiLocation) -> Result { + match ( + AccountIdConvert::from_location(location), + CurrencyIdConvert::convert(asset.clone()), + Matcher::matches_fungible(&asset), + ) { + // known asset + (Some(who), Some(currency_id), Some(amount)) => { + MultiCurrency::deposit(currency_id, &who, amount).map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + // unknown asset + _ => UnknownAsset::deposit(asset, location).map_err(|e| XcmError::FailedToTransactAsset(e.into())), + } + } + + fn withdraw_asset(asset: &MultiAsset, location: &MultiLocation) -> result::Result { + UnknownAsset::withdraw(asset, location).or_else(|_| { + let who = AccountIdConvert::from_location(location) + .ok_or_else(|| XcmError::from(Error::AccountIdConversionFailed))?; + let currency_id = CurrencyIdConvert::convert(asset.clone()) + .ok_or_else(|| XcmError::from(Error::CurrencyIdConversionFailed))?; + let amount: MultiCurrency::Balance = Matcher::matches_fungible(&asset) + .ok_or_else(|| XcmError::from(Error::FailedToMatchFungible))? + .saturated_into(); + MultiCurrency::withdraw(currency_id, &who, amount).map_err(|e| XcmError::FailedToTransactAsset(e.into())) + })?; + + Ok(asset.clone()) + } +} diff --git a/xcm-support/src/lib.rs b/xcm-support/src/lib.rs index a5d55ed10..039af0295 100644 --- a/xcm-support/src/lib.rs +++ b/xcm-support/src/lib.rs @@ -1,142 +1,82 @@ +//! # XCM Support Module. +//! +//! ## Overview +//! +//! The XCM support module provides supporting traits, types and +//! implementations, to support cross-chain message(XCM) integration with ORML +//! modules. + #![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unused_unit)] -use codec::FullCodec; -use sp_runtime::traits::{CheckedConversion, Convert, MaybeSerializeDeserialize, SaturatedConversion}; -use sp_std::{ - cmp::{Eq, PartialEq}, - collections::btree_set::BTreeSet, - convert::{TryFrom, TryInto}, - fmt::Debug, - marker::PhantomData, - prelude::*, - result, -}; +use frame_support::dispatch::{DispatchError, DispatchResult}; +use sp_runtime::traits::{CheckedConversion, Convert}; +use sp_std::{convert::TryFrom, marker::PhantomData, prelude::*}; -use xcm::v0::{Error, Junction, MultiAsset, MultiLocation, Result}; -use xcm_executor::traits::{FilterAssetLocation, LocationConversion, MatchesFungible, NativeAsset, TransactAsset}; +use xcm::v0::{MultiAsset, MultiLocation, Xcm}; +use xcm_executor::traits::{FilterAssetLocation, MatchesFungible}; -use frame_support::{log, traits::Get}; +use orml_traits::location::Reserve; -pub trait CurrencyIdConversion { - fn from_asset(asset: &MultiAsset) -> Option; -} +pub use currency_adapter::MultiCurrencyAdapter; -pub struct MultiCurrencyAdapter( - PhantomData<( - MultiCurrency, - Matcher, - AccountIdConverter, - AccountId, - CurrencyIdConverter, - CurrencyId, - )>, -); +mod currency_adapter; -impl< - MultiCurrency: orml_traits::MultiCurrency, - Matcher: MatchesFungible, - AccountIdConverter: LocationConversion, - AccountId: sp_std::fmt::Debug, - CurrencyIdConverter: CurrencyIdConversion, - CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug, - > TransactAsset - for MultiCurrencyAdapter -{ - fn deposit_asset(asset: &MultiAsset, location: &MultiLocation) -> Result { - log::info!("------------------------------------------------"); - log::info!(">>> trying deposit. asset: {:?}, location: {:?}", asset, location); - let who = AccountIdConverter::from_location(location).ok_or(())?; - log::info!("who: {:?}", who); - let currency_id = CurrencyIdConverter::from_asset(asset).ok_or(())?; - log::info!("currency_id: {:?}", currency_id); - let amount: MultiCurrency::Balance = Matcher::matches_fungible(&asset).ok_or(())?.saturated_into(); - log::info!("amount: {:?}", amount); - MultiCurrency::deposit(currency_id, &who, amount).map_err(|_| ())?; - log::info!(">>> success deposit."); - log::info!("------------------------------------------------"); - Ok(()) - } +mod tests; - fn withdraw_asset(asset: &MultiAsset, location: &MultiLocation) -> result::Result { - log::info!("------------------------------------------------"); - log::info!(">>> trying withdraw. asset: {:?}, location: {:?}", asset, location); - let who = AccountIdConverter::from_location(location).ok_or(())?; - log::info!("who: {:?}", who); - let currency_id = CurrencyIdConverter::from_asset(asset).ok_or(())?; - log::info!("currency_id: {:?}", currency_id); - let amount: MultiCurrency::Balance = Matcher::matches_fungible(&asset).ok_or(())?.saturated_into(); - log::info!("amount: {:?}", amount); - MultiCurrency::withdraw(currency_id, &who, amount).map_err(|_| ())?; - log::info!(">>> success withdraw."); - log::info!("------------------------------------------------"); - Ok(asset.clone()) - } +/// The XCM handler to execute XCM locally. +pub trait XcmHandler { + fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult; } -pub struct IsConcreteWithGeneralKey( - PhantomData<(CurrencyId, FromRelayChainBalance)>, -); -impl MatchesFungible - for IsConcreteWithGeneralKey +/// A `MatchesFungible` implementation. It matches concrete fungible assets +/// whose `id` could be converted into `CurrencyId`. +pub struct IsNativeConcrete(PhantomData<(CurrencyId, CurrencyIdConvert)>); +impl MatchesFungible for IsNativeConcrete where - CurrencyId: TryFrom>, - B: TryFrom, - FromRelayChainBalance: Convert, + CurrencyIdConvert: Convert>, + Amount: TryFrom, { - fn matches_fungible(a: &MultiAsset) -> Option { + fn matches_fungible(a: &MultiAsset) -> Option { if let MultiAsset::ConcreteFungible { id, amount } = a { - if id == &MultiLocation::X1(Junction::Parent) { - // Convert relay chain decimals to local chain - let local_amount = FromRelayChainBalance::convert(*amount); - return CheckedConversion::checked_from(local_amount); - } - if let Some(Junction::GeneralKey(key)) = id.last() { - if TryInto::::try_into(key.clone()).is_ok() { - return CheckedConversion::checked_from(*amount); - } + if CurrencyIdConvert::convert(id.clone()).is_some() { + return CheckedConversion::checked_from(*amount); } } None } } -pub struct NativePalletAssetOr(PhantomData); -impl, MultiLocation)>>> FilterAssetLocation for NativePalletAssetOr { +/// A `FilterAssetLocation` implementation. Filters multi native assets whose +/// reserve is same with `origin`. +pub struct MultiNativeAsset; +impl FilterAssetLocation for MultiNativeAsset { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { - if NativeAsset::filter_asset_location(asset, origin) { - return true; - } - - // native orml-tokens with a general key - if let MultiAsset::ConcreteFungible { ref id, .. } = asset { - if let Some(Junction::GeneralKey(key)) = id.last() { - return Pairs::get().contains(&(key.clone(), origin.clone())); + if let Some(ref reserve) = asset.reserve() { + if reserve == origin { + return true; } } - false } } -pub struct CurrencyIdConverter( - PhantomData, - PhantomData, -); -impl CurrencyIdConversion - for CurrencyIdConverter -where - CurrencyId: TryFrom>, - RelayChainCurrencyId: Get, -{ - fn from_asset(asset: &MultiAsset) -> Option { - if let MultiAsset::ConcreteFungible { id: location, .. } = asset { - if location == &MultiLocation::X1(Junction::Parent) { - return Some(RelayChainCurrencyId::get()); - } - if let Some(Junction::GeneralKey(key)) = location.last() { - return CurrencyId::try_from(key.clone()).ok(); - } - } - None +/// Handlers unknown asset deposit and withdraw. +pub trait UnknownAsset { + /// Deposit unknown asset. + fn deposit(asset: &MultiAsset, to: &MultiLocation) -> DispatchResult; + + /// Withdraw unknown asset. + fn withdraw(asset: &MultiAsset, from: &MultiLocation) -> DispatchResult; +} + +const NO_UNKNOWN_ASSET_IMPL: &str = "NoUnknownAssetImpl"; + +impl UnknownAsset for () { + fn deposit(_asset: &MultiAsset, _to: &MultiLocation) -> DispatchResult { + Err(DispatchError::Other(NO_UNKNOWN_ASSET_IMPL)) + } + fn withdraw(_asset: &MultiAsset, _from: &MultiLocation) -> DispatchResult { + Err(DispatchError::Other(NO_UNKNOWN_ASSET_IMPL)) } } diff --git a/xcm-support/src/tests.rs b/xcm-support/src/tests.rs new file mode 100644 index 000000000..c83b5021b --- /dev/null +++ b/xcm-support/src/tests.rs @@ -0,0 +1,109 @@ +//! Unit tests for xcm-support implementations. + +#![cfg(test)] + +use super::*; + +use xcm::v0::{Junction::*, MultiAsset::ConcreteFungible, MultiLocation::*}; + +#[derive(Debug, PartialEq, Eq)] +pub enum TestCurrencyId { + TokenA, + TokenB, + RelayChainToken, +} + +pub struct CurrencyIdConvert; +impl Convert> for CurrencyIdConvert { + fn convert(l: MultiLocation) -> Option { + use TestCurrencyId::*; + let token_a: Vec = "TokenA".into(); + let token_b: Vec = "TokenB".into(); + match l { + X1(Parent) => Some(RelayChainToken), + X3(Parent, Parachain { id: 1 }, GeneralKey(k)) if k == token_a => Some(TokenA), + X3(Parent, Parachain { id: 2 }, GeneralKey(k)) if k == token_b => Some(TokenB), + _ => None, + } + } +} + +type MatchesCurrencyId = IsNativeConcrete; + +#[test] +fn is_native_concrete_matches_native_currencies() { + assert_eq!( + MatchesCurrencyId::matches_fungible(&ConcreteFungible { + id: X1(Parent), + amount: 100 + }), + Some(100), + ); + assert_eq!( + MatchesCurrencyId::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())), + amount: 100 + }), + Some(100), + ); + assert_eq!( + MatchesCurrencyId::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 2 }, GeneralKey("TokenB".into())), + amount: 100 + }), + Some(100), + ); +} + +#[test] +fn is_native_concrete_does_not_matches_non_native_currencies() { + assert!( + >::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 2 }, GeneralKey("TokenC".into())), + amount: 100 + }) + .is_none() + ); + assert!( + >::matches_fungible(&ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenB".into())), + amount: 100 + }) + .is_none() + ); + assert!( + >::matches_fungible(&ConcreteFungible { + id: X1(GeneralKey("TokenB".into())), + amount: 100 + }) + .is_none() + ); +} + +#[test] +fn multi_native_asset() { + assert!(MultiNativeAsset::filter_asset_location( + &ConcreteFungible { + id: Parent.into(), + amount: 10, + }, + &Parent.into() + )); + assert!(MultiNativeAsset::filter_asset_location( + &ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())), + amount: 10, + }, + &X2(Parent, Parachain { id: 1 }), + )); + assert_eq!( + MultiNativeAsset::filter_asset_location( + &ConcreteFungible { + id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())), + amount: 10, + }, + &X1(Parent), + ), + false + ); +} diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml index 5124431b1..6f45a4462 100644 --- a/xtokens/Cargo.toml +++ b/xtokens/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "orml-xtokens" -description = "Crosschain token transfer" -repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/tokens" +description = "Cross-chain token transfer." +repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/xtokens" license = "Apache-2.0" version = "0.4.1-dev" authors = ["Acala Developers"] edition = "2018" [dependencies] -serde = { version = "1.0.111", optional = true } +serde = { version = "1.0.124", optional = true } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } @@ -20,12 +20,23 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "roco cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "rococo-v1", default-features = false } xcm = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } -xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1", default-features = false } + +orml-xcm-support = { path = "../xcm-support", default-features = false } +orml-traits = { path = "../traits", default-features = false} [dev-dependencies] sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1" } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1" } +xcm-simulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" } +cumulus-pallet-xcm-handler = { git = "https://github.com/paritytech/cumulus", branch = "rococo-v1" } +parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "rococo-v1" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "rococo-v1" } + orml-tokens = { path = "../tokens", version = "0.4.1-dev" } +orml-traits = { path = "../traits", version = "0.4.1-dev" } [features] default = ["std"] @@ -39,4 +50,6 @@ std = [ "frame-system/std", "cumulus-primitives-core/std", "xcm/std", + "orml-xcm-support/std", + "orml-traits/std", ] diff --git a/xtokens/README.md b/xtokens/README.md new file mode 100644 index 000000000..4e12469b1 --- /dev/null +++ b/xtokens/README.md @@ -0,0 +1,31 @@ +# Xtokens Module + +## Overview + +The xtokens module provides cross-chain token transfer functionality, by cross-consensus +messages(XCM). + +The xtokens module provides functions for +- Token transfer from parachains to relay chain. +- Token transfer between parachains, including relay chain tokens like DOT, + KSM, and parachain tokens like ACA, aUSD. + +## Notes + +#### Integration tests + +Integration tests could be done manually after integrating xtokens into runtime. To cover the full features, set up at least 4 relay chain validators and 3 collators of different parachains, and use dispatchable calls to include all these scenarios: + +- Transfer relay chain tokens to relay chain. +- Transfer tokens issued by parachain A, from parachain A to parachain B. + - Sending the tx from parachain A. + - Set the destination as Parachain B. + - Set the currency ID as parachain A token. +- Transfer tokens issued by parachain B, from parachain A to parachain B. + - Sending the tx from parachain A. + - Set the destination as Parachain B. + - Set the currency ID as parachain B token. +- Transfer tokens issued by parachain C, from parachain A to parachain B. + - Sending the tx from parachain A. + - Set the destination as Parachain B. + - Set the currency ID as parachain C token. diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index e045c3529..7df979629 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -1,60 +1,59 @@ +//! # Xtokens Module +//! +//! ## Overview +//! +//! The xtokens module provides cross-chain token transfer functionality, by +//! cross-consensus messages(XCM). +//! +//! The xtokens module provides functions for +//! - Token transfer from parachains to relay chain. +//! - Token transfer between parachains, including relay chain tokens like DOT, +//! KSM, and parachain tokens like ACA, aUSD. +//! +//! ## Interface +//! +//! ### Dispatchable functions +//! +//! - `transfer`: Transfer local assets with given `CurrencyId` and `Amount`. +//! - `transfer_multiasset`: Transfer `MultiAsset` assets. + #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::from_over_into)] #![allow(clippy::unused_unit)] +#![allow(clippy::large_enum_variant)] -pub use module::*; +use frame_support::{pallet_prelude::*, traits::Get, transactional, Parameter}; +use frame_system::{ensure_signed, pallet_prelude::*}; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Convert, MaybeSerializeDeserialize, Member, Zero}, + DispatchError, +}; +use sp_std::prelude::*; -#[frame_support::pallet] -pub mod module { - use codec::{Decode, Encode}; - use frame_support::{pallet_prelude::*, traits::Get, transactional, Parameter}; - use frame_system::{ensure_signed, pallet_prelude::*}; - use sp_runtime::{ - traits::{AtLeast32BitUnsigned, Convert, MaybeSerializeDeserialize, Member}, - RuntimeDebug, - }; - use sp_std::prelude::*; - - use cumulus_primitives_core::{relay_chain::Balance as RelayChainBalance, ParaId}; - use xcm::v0::{Error as XcmError, ExecuteXcm, Junction, MultiAsset, MultiLocation, NetworkId, Order, Xcm}; - use xcm_executor::traits::LocationConversion; - - #[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, RuntimeDebug)] - /// Identity of chain. - pub enum ChainId { - /// The relay chain. - RelayChain, - /// A parachain. - ParaChain(ParaId), - } +use xcm::v0::{ + Junction::*, + MultiAsset, MultiLocation, Order, + Order::*, + Xcm::{self, *}, +}; - #[derive(Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug)] - /// Identity of cross chain currency. - pub struct XCurrencyId { - /// The reserve chain of the currency. For instance, the reserve chain - /// of DOT is Polkadot. - pub chain_id: ChainId, - /// The identity of the currency. - pub currency_id: Vec, - } +use orml_traits::location::{Parse, Reserve}; +use orml_xcm_support::XcmHandler; - #[cfg(test)] - impl XCurrencyId { - pub fn new(chain_id: ChainId, currency_id: Vec) -> Self { - XCurrencyId { chain_id, currency_id } - } - } +mod mock; +mod tests; - impl Into for XCurrencyId { - fn into(self) -> MultiLocation { - MultiLocation::X1(Junction::GeneralKey(self.currency_id)) - } - } +pub use module::*; + +#[frame_support::pallet] +pub mod module { + use super::*; #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; + /// The balance type. type Balance: Parameter + Member + AtLeast32BitUnsigned @@ -63,51 +62,43 @@ pub mod module { + MaybeSerializeDeserialize + Into; - /// Convertor `Balance` to `RelayChainBalance`. - type ToRelayChainBalance: Convert; + /// Currency Id. + type CurrencyId: Parameter + Member + Clone; - type AccountId32Convert: Convert; + /// Convert `T::CurrencyIn` to `MultiLocation`. + type CurrencyIdConvert: Convert>; - type RelayChainNetworkId: Get; - - /// Parachain ID. - type ParaId: Get; + /// Convert `Self::Account` to `AccountId32` + type AccountId32Convert: Convert; - type AccountIdConverter: LocationConversion; + /// Self chain location. + #[pallet::constant] + type SelfLocation: Get; - type XcmExecutor: ExecuteXcm; + /// Xcm handler to execute XCM. + type XcmHandler: XcmHandler; } #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] + #[pallet::metadata(T::AccountId = "AccountId", T::CurrencyId = "CurrencyId", T::Balance = "Balance")] pub enum Event { - /// Transferred to relay chain. \[src, dest, amount\] - TransferredToRelayChain(T::AccountId, T::AccountId, T::Balance), - - /// Transfer to relay chain failed. \[src, dest, amount, error\] - TransferToRelayChainFailed(T::AccountId, T::AccountId, T::Balance, XcmError), - - /// Transferred to parachain. \[x_currency_id, src, para_id, dest, - /// dest_network, amount\] - TransferredToParachain(XCurrencyId, T::AccountId, ParaId, T::AccountId, NetworkId, T::Balance), - - /// Transfer to parachain failed. \[x_currency_id, src, para_id, dest, - /// dest_network, amount, error\] - TransferToParachainFailed( - XCurrencyId, - T::AccountId, - ParaId, - T::AccountId, - NetworkId, - T::Balance, - XcmError, - ), + /// Transferred. \[sender, currency_id, amount, dest\] + Transferred(T::AccountId, T::CurrencyId, T::Balance, MultiLocation), + /// Transferred `MultiAsset`. \[sender, asset, dest\] + TransferredMultiAsset(T::AccountId, MultiAsset, MultiLocation), } #[pallet::error] pub enum Error { - /// Bad location. - BadLocation, + /// Asset has no reserve location. + AssetHasNoReserve, + /// Not cross-chain transfer. + NotCrossChainTransfer, + /// Invalid transfer destination. + InvalidDest, + /// Currency is not cross-chain transferable. + NotCrossChainTransferableCurrency, } #[pallet::hooks] @@ -118,220 +109,158 @@ pub mod module { #[pallet::call] impl Pallet { - /// Transfer relay chain tokens to relay chain. - #[pallet::weight(10)] + /// Transfer native currencies. #[transactional] - pub fn transfer_to_relay_chain( + #[pallet::weight(1000)] + pub fn transfer( origin: OriginFor, - dest: T::AccountId, + currency_id: T::CurrencyId, amount: T::Balance, + dest: MultiLocation, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let xcm = Xcm::WithdrawAsset { - assets: vec![MultiAsset::ConcreteFungible { - id: MultiLocation::X1(Junction::Parent), - amount: T::ToRelayChainBalance::convert(amount), - }], - effects: vec![Order::InitiateReserveWithdraw { - assets: vec![MultiAsset::All], - reserve: MultiLocation::X1(Junction::Parent), - effects: vec![Order::DepositAsset { - assets: vec![MultiAsset::All], - dest: MultiLocation::X1(Junction::AccountId32 { - network: T::RelayChainNetworkId::get(), - id: T::AccountId32Convert::convert(dest.clone()), - }), - }], - }], - }; - - let xcm_origin = - T::AccountIdConverter::try_into_location(who.clone()).map_err(|_| Error::::BadLocation)?; - // TODO: revert state on xcm execution failure. - match T::XcmExecutor::execute_xcm(xcm_origin, xcm) { - Ok(_) => Self::deposit_event(Event::::TransferredToRelayChain(who, dest, amount)), - Err(err) => Self::deposit_event(Event::::TransferToRelayChainFailed(who, dest, amount, err)), + if amount == Zero::zero() { + return Ok(().into()); } + let id: MultiLocation = T::CurrencyIdConvert::convert(currency_id.clone()) + .ok_or(Error::::NotCrossChainTransferableCurrency)?; + let asset = MultiAsset::ConcreteFungible { + id, + amount: amount.into(), + }; + Self::do_transfer_multiasset(who.clone(), asset, dest.clone())?; + Self::deposit_event(Event::::Transferred(who, currency_id, amount, dest)); Ok(().into()) } - /// Transfer tokens to parachain. - #[pallet::weight(10)] + /// Transfer `MultiAsset`. #[transactional] - pub fn transfer_to_parachain( + #[pallet::weight(1000)] + pub fn transfer_multiasset( origin: OriginFor, - x_currency_id: XCurrencyId, - para_id: ParaId, - dest: T::AccountId, - dest_network: NetworkId, - amount: T::Balance, + asset: MultiAsset, + dest: MultiLocation, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - if para_id == T::ParaId::get() { + if Self::is_zero_amount(&asset) { return Ok(().into()); } - let xcm = match x_currency_id.chain_id { - ChainId::RelayChain => { - Self::transfer_relay_chain_tokens_to_parachain(para_id, &dest, dest_network.clone(), amount) - } - ChainId::ParaChain(reserve_chain) => { - if T::ParaId::get() == reserve_chain { - Self::transfer_owned_tokens_to_parachain( - x_currency_id.clone(), - para_id, - &dest, - dest_network.clone(), - amount, - ) - } else { - Self::transfer_non_owned_tokens_to_parachain( - reserve_chain, - x_currency_id.clone(), - para_id, - &dest, - dest_network.clone(), - amount, - ) - } - } + Self::do_transfer_multiasset(who.clone(), asset.clone(), dest.clone())?; + Self::deposit_event(Event::::TransferredMultiAsset(who, asset, dest)); + Ok(().into()) + } + } + + impl Pallet { + /// Transfer `MultiAsset` without depositing event. + fn do_transfer_multiasset( + who: T::AccountId, + asset: MultiAsset, + dest: MultiLocation, + ) -> DispatchResultWithPostInfo { + let (dest, recipient) = Self::ensure_valid_dest(dest)?; + + let self_location = T::SelfLocation::get(); + ensure!(dest != self_location, Error::::NotCrossChainTransfer); + + let reserve = asset.reserve().ok_or(Error::::AssetHasNoReserve)?; + let xcm = if reserve == self_location { + Self::transfer_self_reserve_asset(asset, dest, recipient) + } else if reserve == dest { + Self::transfer_to_reserve(asset, dest, recipient) + } else { + Self::transfer_to_non_reserve(asset, reserve, dest, recipient) }; - let xcm_origin = - T::AccountIdConverter::try_into_location(who.clone()).map_err(|_| Error::::BadLocation)?; - // TODO: revert state on xcm execution failure. - match T::XcmExecutor::execute_xcm(xcm_origin, xcm) { - Ok(_) => Self::deposit_event(Event::::TransferredToParachain( - x_currency_id, - who, - para_id, - dest, - dest_network, - amount, - )), - Err(err) => Self::deposit_event(Event::::TransferToParachainFailed( - x_currency_id, - who, - para_id, - dest, - dest_network, - amount, - err, - )), - } + T::XcmHandler::execute_xcm(who, xcm)?; Ok(().into()) } - } - impl Pallet { - fn transfer_relay_chain_tokens_to_parachain( - para_id: ParaId, - dest: &T::AccountId, - dest_network: NetworkId, - amount: T::Balance, - ) -> Xcm { - Xcm::WithdrawAsset { - assets: vec![MultiAsset::ConcreteFungible { - id: MultiLocation::X1(Junction::Parent), - amount: T::ToRelayChainBalance::convert(amount), + fn transfer_self_reserve_asset(asset: MultiAsset, dest: MultiLocation, recipient: MultiLocation) -> Xcm { + WithdrawAsset { + assets: vec![asset], + effects: vec![DepositReserveAsset { + assets: vec![MultiAsset::All], + dest, + effects: Self::deposit_asset(recipient), }], - effects: vec![Order::InitiateReserveWithdraw { + } + } + + fn transfer_to_reserve(asset: MultiAsset, reserve: MultiLocation, recipient: MultiLocation) -> Xcm { + WithdrawAsset { + assets: vec![asset], + effects: vec![InitiateReserveWithdraw { assets: vec![MultiAsset::All], - reserve: MultiLocation::X1(Junction::Parent), - effects: vec![Order::DepositReserveAsset { - assets: vec![MultiAsset::All], - // `dest` is children parachain(of parent). - dest: MultiLocation::X1(Junction::Parachain { id: para_id.into() }), - effects: vec![Order::DepositAsset { - assets: vec![MultiAsset::All], - dest: MultiLocation::X1(Junction::AccountId32 { - network: dest_network, - id: T::AccountId32Convert::convert(dest.clone()), - }), - }], - }], + reserve, + effects: Self::deposit_asset(recipient), }], } } - /// Transfer parachain tokens "owned" by self parachain to another - /// parachain. - /// - /// NOTE - `para_id` must not be self parachain. - fn transfer_owned_tokens_to_parachain( - x_currency_id: XCurrencyId, - para_id: ParaId, - dest: &T::AccountId, - dest_network: NetworkId, - amount: T::Balance, + fn transfer_to_non_reserve( + asset: MultiAsset, + reserve: MultiLocation, + dest: MultiLocation, + recipient: MultiLocation, ) -> Xcm { - Xcm::WithdrawAsset { - assets: vec![MultiAsset::ConcreteFungible { - id: x_currency_id.into(), - amount: amount.into(), - }], - effects: vec![Order::DepositReserveAsset { + let mut reanchored_dest = dest.clone(); + if reserve == Parent.into() { + if let MultiLocation::X2(Parent, Parachain { id }) = dest { + reanchored_dest = Parachain { id }.into(); + } + } + + WithdrawAsset { + assets: vec![asset], + effects: vec![InitiateReserveWithdraw { assets: vec![MultiAsset::All], - dest: MultiLocation::X2(Junction::Parent, Junction::Parachain { id: para_id.into() }), - effects: vec![Order::DepositAsset { + reserve, + effects: vec![DepositReserveAsset { assets: vec![MultiAsset::All], - dest: MultiLocation::X1(Junction::AccountId32 { - network: dest_network, - id: T::AccountId32Convert::convert(dest.clone()), - }), + dest: reanchored_dest, + effects: Self::deposit_asset(recipient), }], }], } } - /// Transfer parachain tokens not "owned" by self chain to another - /// parachain. - fn transfer_non_owned_tokens_to_parachain( - reserve_chain: ParaId, - x_currency_id: XCurrencyId, - para_id: ParaId, - dest: &T::AccountId, - dest_network: NetworkId, - amount: T::Balance, - ) -> Xcm { - let deposit_to_dest = Order::DepositAsset { + fn deposit_asset(recipient: MultiLocation) -> Vec { + vec![DepositAsset { assets: vec![MultiAsset::All], - dest: MultiLocation::X1(Junction::AccountId32 { - network: dest_network, - id: T::AccountId32Convert::convert(dest.clone()), - }), - }; - // If transfer to reserve chain, deposit to `dest` on reserve chain, - // else deposit reserve asset. - let reserve_chain_order = if para_id == reserve_chain { - deposit_to_dest - } else { - Order::DepositReserveAsset { - assets: vec![MultiAsset::All], - dest: MultiLocation::X2(Junction::Parent, Junction::Parachain { id: para_id.into() }), - effects: vec![deposit_to_dest], + dest: recipient, + }] + } + + fn is_zero_amount(asset: &MultiAsset) -> bool { + if let MultiAsset::ConcreteFungible { id: _, amount } = asset { + if *amount == Zero::zero() { + return true; } - }; + } - Xcm::WithdrawAsset { - assets: vec![MultiAsset::ConcreteFungible { - id: x_currency_id.into(), - amount: amount.into(), - }], - effects: vec![Order::InitiateReserveWithdraw { - assets: vec![MultiAsset::All], - reserve: MultiLocation::X2( - Junction::Parent, - Junction::Parachain { - id: reserve_chain.into(), - }, - ), - effects: vec![reserve_chain_order], - }], + if let MultiAsset::AbstractFungible { id: _, amount } = asset { + if *amount == Zero::zero() { + return true; + } + } + + false + } + + /// Ensure has the `dest` has chain part and recipient part. + fn ensure_valid_dest( + dest: MultiLocation, + ) -> sp_std::result::Result<(MultiLocation, MultiLocation), DispatchError> { + if let (Some(dest), Some(recipient)) = (dest.chain_part(), dest.non_chain_part()) { + Ok((dest, recipient)) + } else { + Err(Error::::InvalidDest.into()) } } } diff --git a/xtokens/src/mock.rs b/xtokens/src/mock.rs new file mode 100644 index 000000000..463aa3a03 --- /dev/null +++ b/xtokens/src/mock.rs @@ -0,0 +1,470 @@ +#![cfg(test)] + +use super::*; +use crate as orml_xtokens; + +use frame_support::parameter_types; +use orml_traits::parameter_type_with_key; +use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset, XcmHandler as XcmHandlerT}; +use polkadot_parachain::primitives::Sibling; +use serde::{Deserialize, Serialize}; +use sp_io::TestExternalities; +use sp_runtime::AccountId32; +use xcm::v0::{Junction, MultiLocation::*, NetworkId}; +use xcm_builder::{ + AccountId32Aliases, LocationInverter, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SovereignSignedViaLocation, +}; +use xcm_executor::Config as XcmConfigT; +use xcm_simulator::{decl_test_network, decl_test_parachain, prelude::*}; + +pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); +pub const BOB: AccountId32 = AccountId32::new([1u8; 32]); + +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum CurrencyId { + /// Relay chain token. + R, + /// Parachain A token. + A, + /// Parachain B token. + B, +} + +pub struct CurrencyIdConvert; +impl Convert> for CurrencyIdConvert { + fn convert(id: CurrencyId) -> Option { + match id { + CurrencyId::R => Some(Junction::Parent.into()), + CurrencyId::A => Some( + ( + Junction::Parent, + Junction::Parachain { id: 1 }, + Junction::GeneralKey("A".into()), + ) + .into(), + ), + CurrencyId::B => Some( + ( + Junction::Parent, + Junction::Parachain { id: 2 }, + Junction::GeneralKey("B".into()), + ) + .into(), + ), + } + } +} +impl Convert> for CurrencyIdConvert { + fn convert(l: MultiLocation) -> Option { + let a: Vec = "A".into(); + let b: Vec = "B".into(); + match l { + X1(Parent) => Some(CurrencyId::R), + X3(Junction::Parent, Junction::Parachain { id: 1 }, Junction::GeneralKey(k)) if k == a => { + Some(CurrencyId::A) + } + X3(Junction::Parent, Junction::Parachain { id: 2 }, Junction::GeneralKey(k)) if k == b => { + Some(CurrencyId::B) + } + _ => None, + } + } +} +impl Convert> for CurrencyIdConvert { + fn convert(a: MultiAsset) -> Option { + if let MultiAsset::ConcreteFungible { id, amount: _ } = a { + Self::convert(id) + } else { + None + } + } +} + +pub type Balance = u128; +pub type Amount = i128; + +decl_test_parachain! { + pub struct ParaA { + new_ext = parachain_ext::(1), + para_id = 1, + } + pub mod para_a { + test_network = super::TestNetwork, + xcm_config = { + use super::*; + + parameter_types! { + pub ParaANetwork: NetworkId = NetworkId::Any; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm_handler::Origin::Relay.into(); + pub Ancestry: MultiLocation = MultiLocation::X1(Junction::Parachain { + id: ParachainInfo::get().into(), + }); + pub const RelayChainCurrencyId: CurrencyId = CurrencyId::R; + } + + pub type LocationConverter = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, + ); + + pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + (), + IsNativeConcrete, + AccountId, + LocationConverter, + CurrencyId, + CurrencyIdConvert, + >; + + pub type LocalOriginConverter = ( + SovereignSignedViaLocation, + RelayChainAsNative, + SiblingParachainAsNative, + SignedAccountId32AsNative, + ); + + pub struct XcmConfig; + impl XcmConfigT for XcmConfig { + type Call = Call; + type XcmSender = XcmHandler; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = MultiNativeAsset; + type IsTeleporter = (); + type LocationInverter = LocationInverter; + } + }, + extra_config = { + parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: super::CurrencyId| -> Balance { + Default::default() + }; + } + + impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = super::CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = (); + } + + pub struct HandleXcm; + impl XcmHandlerT for HandleXcm { + fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult { + XcmHandler::execute_xcm(origin, xcm) + } + } + + pub struct AccountId32Convert; + impl Convert for AccountId32Convert { + fn convert(account_id: AccountId) -> [u8; 32] { + account_id.into() + } + } + + parameter_types! { + pub SelfLocation: MultiLocation = (Junction::Parent, Junction::Parachain { id: ParachainInfo::get().into() }).into(); + } + + impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountId32Convert = AccountId32Convert; + type SelfLocation = SelfLocation; + type XcmHandler = HandleXcm; + } + }, + extra_modules = { + Tokens: orml_tokens::{Pallet, Storage, Event, Config}, + XTokens: orml_xtokens::{Pallet, Storage, Call, Event}, + }, + } +} + +decl_test_parachain! { + pub struct ParaB { + new_ext = parachain_ext::(2), + para_id = 2, + } + pub mod para_c { + test_network = super::TestNetwork, + xcm_config = { + use super::*; + + parameter_types! { + pub ParaANetwork: NetworkId = NetworkId::Any; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm_handler::Origin::Relay.into(); + pub Ancestry: MultiLocation = MultiLocation::X1(Junction::Parachain { + id: ParachainInfo::get().into(), + }); + pub const RelayChainCurrencyId: CurrencyId = CurrencyId::R; + } + + pub type LocationConverter = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, + ); + + pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + (), + IsNativeConcrete, + AccountId, + LocationConverter, + CurrencyId, + CurrencyIdConvert, + >; + + pub type LocalOriginConverter = ( + SovereignSignedViaLocation, + RelayChainAsNative, + SiblingParachainAsNative, + SignedAccountId32AsNative, + ); + + pub struct XcmConfig; + impl XcmConfigT for XcmConfig { + type Call = Call; + type XcmSender = XcmHandler; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = MultiNativeAsset; + type IsTeleporter = (); + type LocationInverter = LocationInverter; + } + }, + extra_config = { + parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: super::CurrencyId| -> Balance { + Default::default() + }; + } + + impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = super::CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = (); + } + + pub struct HandleXcm; + impl XcmHandlerT for HandleXcm { + fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult { + XcmHandler::execute_xcm(origin, xcm) + } + } + + pub struct AccountId32Convert; + impl Convert for AccountId32Convert { + fn convert(account_id: AccountId) -> [u8; 32] { + account_id.into() + } + } + + parameter_types! { + pub SelfLocation: MultiLocation = (Junction::Parent, Junction::Parachain { id: ParachainInfo::get().into() }).into(); + } + + impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountId32Convert = AccountId32Convert; + type SelfLocation = SelfLocation; + type XcmHandler = HandleXcm; + } + }, + extra_modules = { + Tokens: orml_tokens::{Pallet, Storage, Event, Config}, + XTokens: orml_xtokens::{Pallet, Storage, Call, Event}, + }, + } +} + +decl_test_parachain! { + pub struct ParaC { + new_ext = parachain_ext::(3), + para_id = 3, + } + pub mod para_b { + test_network = super::TestNetwork, + xcm_config = { + use super::*; + + parameter_types! { + pub ParaANetwork: NetworkId = NetworkId::Any; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm_handler::Origin::Relay.into(); + pub Ancestry: MultiLocation = MultiLocation::X1(Junction::Parachain { + id: ParachainInfo::get().into(), + }); + pub const RelayChainCurrencyId: CurrencyId = CurrencyId::R; + } + + pub type LocationConverter = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, + ); + + pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + (), + IsNativeConcrete, + AccountId, + LocationConverter, + CurrencyId, + CurrencyIdConvert, + >; + + pub type LocalOriginConverter = ( + SovereignSignedViaLocation, + RelayChainAsNative, + SiblingParachainAsNative, + SignedAccountId32AsNative, + ); + + pub struct XcmConfig; + impl XcmConfigT for XcmConfig { + type Call = Call; + type XcmSender = XcmHandler; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = MultiNativeAsset; + type IsTeleporter = (); + type LocationInverter = LocationInverter; + } + }, + extra_config = { + parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: super::CurrencyId| -> Balance { + Default::default() + }; + } + + impl orml_tokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = super::CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = (); + } + + pub struct HandleXcm; + impl XcmHandlerT for HandleXcm { + fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult { + XcmHandler::execute_xcm(origin, xcm) + } + } + + pub struct AccountId32Convert; + impl Convert for AccountId32Convert { + fn convert(account_id: AccountId) -> [u8; 32] { + account_id.into() + } + } + + parameter_types! { + pub SelfLocation: MultiLocation = (Junction::Parent, Junction::Parachain { id: ParachainInfo::get().into() }).into(); + } + + impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountId32Convert = AccountId32Convert; + type SelfLocation = SelfLocation; + type XcmHandler = HandleXcm; + } + }, + extra_modules = { + Tokens: orml_tokens::{Pallet, Storage, Event, Config}, + XTokens: orml_xtokens::{Pallet, Storage, Call, Event}, + }, + } +} + +decl_test_network! { + pub struct TestNetwork { + relay_chain = default, + parachains = vec![ + (1, ParaA), + (2, ParaB), + (3, ParaC), + ], + } +} + +pub type ParaAXtokens = orml_xtokens::Pallet; +pub type ParaATokens = orml_tokens::Pallet; +pub type ParaBTokens = orml_tokens::Pallet; +pub type ParaCTokens = orml_tokens::Pallet; + +pub type RelayBalances = pallet_balances::Pallet; + +pub struct ParaExtBuilder; + +impl Default for ParaExtBuilder { + fn default() -> Self { + ParaExtBuilder + } +} + +impl ParaExtBuilder { + pub fn build< + Runtime: frame_system::Config + orml_tokens::Config, + >( + self, + para_id: u32, + ) -> TestExternalities + where + ::BlockNumber: From, + { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + parachain_info::GenesisConfig { + parachain_id: para_id.into(), + } + .assimilate_storage(&mut t) + .unwrap(); + + orml_tokens::GenesisConfig:: { + endowed_accounts: vec![(ALICE, CurrencyId::R, 100)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| frame_system::Pallet::::set_block_number(1.into())); + ext + } +} + +pub fn parachain_ext< + Runtime: frame_system::Config + orml_tokens::Config, +>( + para_id: u32, +) -> TestExternalities +where + ::BlockNumber: From, +{ + ParaExtBuilder::default().build::(para_id) +} diff --git a/xtokens/src/tests.rs b/xtokens/src/tests.rs new file mode 100644 index 000000000..a90ca0b32 --- /dev/null +++ b/xtokens/src/tests.rs @@ -0,0 +1,290 @@ +#![cfg(test)] + +use super::*; +use cumulus_primitives_core::ParaId; +use frame_support::{assert_noop, assert_ok, traits::Currency}; +use mock::*; +use orml_traits::MultiCurrency; +use polkadot_parachain::primitives::{AccountIdConversion, Sibling}; +use sp_runtime::AccountId32; +use xcm::v0::{Junction, NetworkId}; +use xcm_simulator::TestExt; + +fn para_a_account() -> AccountId32 { + ParaId::from(1).into_account() +} + +fn para_b_account() -> AccountId32 { + ParaId::from(2).into_account() +} + +fn sibling_a_account() -> AccountId32 { + use sp_runtime::traits::AccountIdConversion; + Sibling::from(1).into_account() +} + +fn sibling_b_account() -> AccountId32 { + use sp_runtime::traits::AccountIdConversion; + Sibling::from(2).into_account() +} + +fn sibling_c_account() -> AccountId32 { + use sp_runtime::traits::AccountIdConversion; + Sibling::from(3).into_account() +} + +#[test] +fn send_relay_chain_asset_to_relay_chain() { + TestNetwork::reset(); + + MockRelay::execute_with(|| { + let _ = RelayBalances::deposit_creating(¶_a_account(), 100); + }); + + ParaA::execute_with(|| { + assert_ok!(ParaAXtokens::transfer( + Some(ALICE).into(), + CurrencyId::R, + 30, + ( + Parent, + Junction::AccountId32 { + network: NetworkId::Polkadot, + id: BOB.into(), + }, + ) + .into(), + )); + assert_eq!(ParaATokens::free_balance(CurrencyId::R, &ALICE), 70); + }); + + MockRelay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 70); + assert_eq!(RelayBalances::free_balance(&BOB), 30); + }); +} + +#[test] +fn send_relay_chain_asset_to_sibling() { + TestNetwork::reset(); + + MockRelay::execute_with(|| { + let _ = RelayBalances::deposit_creating(¶_a_account(), 100); + }); + + ParaA::execute_with(|| { + assert_ok!(ParaAXtokens::transfer( + Some(ALICE).into(), + CurrencyId::R, + 30, + ( + Parent, + Parachain { id: 2 }, + Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into(), + }, + ) + .into(), + )); + assert_eq!(ParaATokens::free_balance(CurrencyId::R, &ALICE), 70); + }); + + MockRelay::execute_with(|| { + assert_eq!(RelayBalances::free_balance(¶_a_account()), 70); + assert_eq!(RelayBalances::free_balance(¶_b_account()), 30); + }); + + ParaB::execute_with(|| { + assert_eq!(ParaBTokens::free_balance(CurrencyId::R, &BOB), 30); + }); +} + +#[test] +fn send_sibling_asset_to_reserve_sibling() { + TestNetwork::reset(); + + ParaA::execute_with(|| { + assert_ok!(ParaATokens::deposit(CurrencyId::B, &ALICE, 100)); + }); + + ParaB::execute_with(|| { + assert_ok!(ParaBTokens::deposit(CurrencyId::B, &sibling_a_account(), 100)); + }); + + ParaA::execute_with(|| { + assert_ok!(ParaAXtokens::transfer( + Some(ALICE).into(), + CurrencyId::B, + 30, + ( + Parent, + Parachain { id: 2 }, + Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into(), + }, + ) + .into(), + )); + + assert_eq!(ParaATokens::free_balance(CurrencyId::B, &ALICE), 70); + }); + + ParaB::execute_with(|| { + assert_eq!(ParaBTokens::free_balance(CurrencyId::B, &sibling_a_account()), 70); + assert_eq!(ParaBTokens::free_balance(CurrencyId::B, &BOB), 30); + }); +} + +#[test] +fn send_sibling_asset_to_non_reserve_sibling() { + TestNetwork::reset(); + + ParaA::execute_with(|| { + assert_ok!(ParaATokens::deposit(CurrencyId::B, &ALICE, 100)); + }); + + ParaB::execute_with(|| { + assert_ok!(ParaBTokens::deposit(CurrencyId::B, &sibling_a_account(), 100)); + }); + + ParaA::execute_with(|| { + assert_ok!(ParaAXtokens::transfer( + Some(ALICE).into(), + CurrencyId::B, + 30, + ( + Parent, + Parachain { id: 3 }, + Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into(), + }, + ) + .into(), + )); + assert_eq!(ParaATokens::free_balance(CurrencyId::B, &ALICE), 70); + }); + + // check reserve accounts + ParaB::execute_with(|| { + assert_eq!(ParaBTokens::free_balance(CurrencyId::B, &sibling_a_account()), 70); + assert_eq!(ParaBTokens::free_balance(CurrencyId::B, &sibling_c_account()), 30); + }); + + ParaC::execute_with(|| { + assert_eq!(ParaCTokens::free_balance(CurrencyId::B, &BOB), 30); + }); +} + +#[test] +fn send_self_parachain_asset_to_sibling() { + TestNetwork::reset(); + + ParaA::execute_with(|| { + assert_ok!(ParaATokens::deposit(CurrencyId::A, &ALICE, 100)); + + assert_ok!(ParaAXtokens::transfer( + Some(ALICE).into(), + CurrencyId::A, + 30, + ( + Parent, + Parachain { id: 2 }, + Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into(), + }, + ) + .into(), + )); + + assert_eq!(ParaATokens::free_balance(CurrencyId::A, &ALICE), 70); + assert_eq!(ParaATokens::free_balance(CurrencyId::A, &sibling_b_account()), 30); + }); + + ParaB::execute_with(|| { + para_b::System::events().iter().for_each(|r| { + println!(">>> {:?}", r.event); + }); + assert_eq!(ParaBTokens::free_balance(CurrencyId::A, &BOB), 30); + }); +} + +#[test] +fn transfer_no_reserve_assets_fails() { + TestNetwork::reset(); + + ParaA::execute_with(|| { + assert_noop!( + ParaAXtokens::transfer_multiasset( + Some(ALICE).into(), + MultiAsset::ConcreteFungible { + id: GeneralKey("B".into()).into(), + amount: 1 + }, + ( + Parent, + Parachain { id: 2 }, + Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into() + } + ) + .into() + ), + Error::::AssetHasNoReserve + ); + }); +} + +#[test] +fn transfer_to_self_chain_fails() { + TestNetwork::reset(); + + ParaA::execute_with(|| { + assert_noop!( + ParaAXtokens::transfer_multiasset( + Some(ALICE).into(), + MultiAsset::ConcreteFungible { + id: (Parent, Parachain { id: 1 }, GeneralKey("A".into())).into(), + amount: 1 + }, + ( + Parent, + Parachain { id: 1 }, + Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into() + } + ) + .into() + ), + Error::::NotCrossChainTransfer + ); + }); +} + +#[test] +fn transfer_to_invalid_dest_fails() { + TestNetwork::reset(); + + ParaA::execute_with(|| { + assert_noop!( + ParaAXtokens::transfer_multiasset( + Some(ALICE).into(), + MultiAsset::ConcreteFungible { + id: (Parent, Parachain { id: 1 }, GeneralKey("A".into())).into(), + amount: 1 + }, + (Junction::AccountId32 { + network: NetworkId::Any, + id: BOB.into() + }) + .into() + ), + Error::::InvalidDest + ); + }); +} From 3da54420722b1777605578272639cfc0cbb68a12 Mon Sep 17 00:00:00 2001 From: brettkolodny Date: Thu, 15 Apr 2021 14:21:52 -0400 Subject: [PATCH 24/29] Orml bencher (#452) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use MultiLocation as xtokens transfer dest type. (#396) * Use MultiLocation as xtokens transfer dest type. * Make clippy happy. * Use xcm-handler to execute XCM locally (#401) * Use cumulus xcm-handler to execute XCM locally. * Add docstring for xtokens config. * Replace XcmError::Undefined usage. (#402) * Replace XcmError::Undefined usage. * make clippy happy * Bump and unify serde versions. (#403) * xtokens and xcm-support documentations (#404) * Add xtokens module docstring. * Add xtokens readme. * Add documentations for xcm-support. * Add xtokens and xcm-support entries in main readme. * Add unit tests for xcm-support. (#405) * Added Minterest to the list of users. (#406) * update step guide link * Handle unknown assets in TransactAsset impl (#409) * Handle unknown assets in TransactAsset impl. * More documentations. * Clean code. * Renaming. * Should try to deposit known asset first. * Return error if no UnknownAsset impl. * Make clippy happy. * Fix description and repo link. (#410) * Unknown tokens pallet (#411) * Impl unknown tokens pallet. * Fix workspace pallet path. * Make clippy happy. * Clippy, be happy. * Unit tests. * Remove nonces from oracle pallet. (#413) * refactor rewards (#412) * Bump rococo v1 dependencies (#418) * Fix mocks. * Replace deprecated. * Update orml-unknown-tokens unit tests. (#421) * add build-script-utils from Substrate (#422) * Update README.md (#420) * Update README.md * Update README.md * Bump impl-trait-for-tuples to 0.2.1 (#424) * update Cargo.toml (#429) * bencher init commit * split into files * clean deps * add docs * use frame_benchmarking apis * add macro bencher_use to export stuff for bench_runner * * generate weights file * refactor * improvements * CLI Options (#446) * Added command line options for output, headers, and templates * Fixed options to conform to standard cli * Added weight-gen cli * fixed dependencies * Replaced unwraps with expects (#449) * Orml bencher (#451) * add Handler (#431) * remove disable-tokens-by-owner (#434) * Cross-chain transfer rework (#432) * Reserve chain trait. * Rework cross-chain transfer. * Remove relay chain balance convert. * Add 'Parse' trait. * Change transfer_multiasset fn signature. * Add transfer dispatchable call. * Update doc. * Use xcm-simulator to mock network. * Send relay chain asset to sibling unit test. * Move location traits into orml-traits. * Add MultiNativeAsset filter for is reserve check. * More unit tests. * Failing edge case unit tests. * Handle zero amount asset case. * Fix mocks. * Renaming. * Update currency adapter to work with new xtokens impl (#436) * Xcm support implementations rework. * Update xtokens mock. * Use CurrencyId convert. (#437) * Use CurrencyId convert. * Apply review suggestions. * Update xtokens docs. (#438) * Update xtokens docs. * Fix typo. * Update imbalances impl. * Don't deposit failure event in orml-unknown-tokens. (#440) * Don't deposit failure event in orml-unknown-tokens. * Patch substrate/polkadot/cumulus. * Fix patch. * Update README.md (#441) Include Zeitgeist into "Projects using ORML" section * Add PoV size in benchmarking. (#442) * Bump cumulus ref in cargo patch. (#443) * fix missing features (#444) * fix missing features * test with benchmarks * update auction weight (#445) * Bump dependencies. (#448) * Replaced unwraps with expects Co-authored-by: Xiliang Chen Co-authored-by: Shaun Wang Co-authored-by: Harald Heckmann Co-authored-by: wangjj9219 <183318287@qq.com> Co-authored-by: Shaun Wang Co-authored-by: dzianis.ramanouski Co-authored-by: Bette <42193328+bette7@users.noreply.github.com> Co-authored-by: wangjj9219 <183318287@qq.com> Co-authored-by: Xiliang Chen Co-authored-by: transxask <68648225+transxask@users.noreply.github.com> Co-authored-by: Aaro Perämaa Co-authored-by: Ermal Kaleci Co-authored-by: Harald Heckmann --- Cargo.dev.toml | 2 +- bencher/Cargo.toml | 44 +++++++++ bencher/src/bench_runner.rs | 39 ++++++++ bencher/src/handler.rs | 54 +++++++++++ bencher/src/lib.rs | 28 ++++++ bencher/src/macros.rs | 160 +++++++++++++++++++++++++++++++ bencher/src/template.hbs | 23 +++++ weight-gen/Cargo.toml | 20 ++++ weight-gen/src/main.rs | 185 ++++++++++++++++++++++++++++++++++++ weight-gen/src/template.hbs | 23 +++++ 10 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 bencher/Cargo.toml create mode 100644 bencher/src/bench_runner.rs create mode 100644 bencher/src/handler.rs create mode 100644 bencher/src/lib.rs create mode 100644 bencher/src/macros.rs create mode 100644 bencher/src/template.hbs create mode 100644 weight-gen/Cargo.toml create mode 100644 weight-gen/src/main.rs create mode 100644 weight-gen/src/template.hbs diff --git a/Cargo.dev.toml b/Cargo.dev.toml index 86f3665e4..3c16f9bd8 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -20,7 +20,7 @@ members = [ "xcm-support", "unknown-tokens", "build-script-utils", - "weight-meter", + "weight-meter", ] resolver = "2" diff --git a/bencher/Cargo.toml b/bencher/Cargo.toml new file mode 100644 index 000000000..cbe74919c --- /dev/null +++ b/bencher/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "orml-bencher" +description = "Provide macro to benchmark pallets." +repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/bencher" +license = "Apache-2.0" +version = "0.1.0" +authors = ["Laminar Developers "] +edition = "2018" + +[[bin]] +path = "src/bin.rs" +name = "codegen" + +[dependencies] +linregress = { version = "0.4.0", optional = true } +handlebars = {version = "3.5.2", optional = true } +serde = { features = ['derive'], optional = true, version = "1.0.119" } +serde_json = "1.0" +codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"], default-features = false } +sp-core = { version = "3.0.0", default-features = false } +sp-std = { version = "3.0.0", default-features = false } +sp-io = { version = "3.0.0", default-features = false } +sp-runtime-interface = { version = "3.0.0", default-features = false } +sp-state-machine = { version = "0.9.0", default-features = false, optional = true } +sc-executor = { version = "0.9.0", default-features = false, optional = true } +sc-client-db = { version = "0.9.0", default-features = false, features = ["with-kvdb-rocksdb"], optional = true } +frame-benchmarking = { version = "3.1.0", default-features = false } + +[features] +default = ["std"] +std = [ + "linregress", + "handlebars", + "serde/std", + "codec/std", + "sp-core/std", + "sp-std/std", + "sp-io/std", + "sp-runtime-interface/std", + "sp-state-machine/std", + "sc-executor/std", + "sc-client-db", + "frame-benchmarking/std", +] diff --git a/bencher/src/bench_runner.rs b/bencher/src/bench_runner.rs new file mode 100644 index 000000000..6f0d49075 --- /dev/null +++ b/bencher/src/bench_runner.rs @@ -0,0 +1,39 @@ +use frame_benchmarking::{ + benchmarking, + frame_support::sp_runtime::traits::{Block, NumberFor}, +}; +use sc_client_db::BenchmarkingState; +use sc_executor::{sp_wasm_interface::HostFunctions, WasmExecutionMethod, WasmExecutor}; +use sp_core::traits::{CallInWasm, MissingHostFunctions}; +use sp_io::SubstrateHostFunctions; +use sp_state_machine::{Ext, OverlayedChanges, StorageTransactionCache}; + +/// Run benches +pub fn run(wasm_code: Vec) -> Vec { + let mut overlay = OverlayedChanges::default(); + let mut cache = StorageTransactionCache::default(); + let state = BenchmarkingState::::new(Default::default(), Default::default()).unwrap(); + let mut ext = Ext::<_, NumberFor, _>::new(&mut overlay, &mut cache, &state, None, None); + + let mut host_functions = benchmarking::HostFunctions::host_functions(); + host_functions.append(&mut SubstrateHostFunctions::host_functions()); + + let executor = WasmExecutor::new( + WasmExecutionMethod::Compiled, + Default::default(), + host_functions, + 1, + None, + ); + + executor + .call_in_wasm( + &wasm_code[..], + None, + "run_benches", + &[], + &mut ext, + MissingHostFunctions::Disallow, + ) + .unwrap() +} diff --git a/bencher/src/handler.rs b/bencher/src/handler.rs new file mode 100644 index 000000000..0d174c597 --- /dev/null +++ b/bencher/src/handler.rs @@ -0,0 +1,54 @@ +use crate::BenchResult; +use serde::{Serialize, Deserialize}; +use codec::Decode; +use linregress::{FormulaRegressionBuilder, RegressionDataBuilder}; +use std::io::Write; + +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +struct BenchData { + pub name: String, + pub base_weight: u64, + pub base_reads: u32, + pub base_writes: u32, +} + +/// Handle bench results +pub fn handle(output: Vec) { + let results = as Decode>::decode(&mut &output[..]).unwrap(); + let data: Vec = results + .into_iter() + .map(|result| { + let name = String::from_utf8_lossy(&result.method).to_string(); + + eprintln!("{:#?}", result); + + let y: Vec = result.elapses.into_iter().map(|x| x as f64).collect(); + let x: Vec = (0..y.len()).into_iter().map(|x| x as f64).collect(); + let data = vec![("Y", y), ("X", x)]; + let data = RegressionDataBuilder::new().build_from(data).unwrap(); + let formula = "Y ~ X"; + + let model = FormulaRegressionBuilder::new() + .data(&data) + .formula(formula) + .fit() + .unwrap(); + + BenchData { + name, + base_weight: model.parameters.intercept_value as u64 * 1_000, + base_reads: result.reads, + base_writes: result.writes, + } + }) + .collect(); + + if let Ok(json) = serde_json::to_string(&data) { + let stdout = ::std::io::stdout(); + let mut handle = stdout.lock(); + + handle.write_all(&json.as_bytes()).unwrap(); + } else { + eprintln!("Could not write benchdata to JSON"); + } +} diff --git a/bencher/src/lib.rs b/bencher/src/lib.rs new file mode 100644 index 000000000..2ef25f776 --- /dev/null +++ b/bencher/src/lib.rs @@ -0,0 +1,28 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[doc(hidden)] +pub extern crate frame_benchmarking; +#[doc(hidden)] +pub extern crate sp_core; +#[doc(hidden)] +pub extern crate sp_std; + +use codec::{Decode, Encode}; +use sp_std::prelude::Vec; + +#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)] +pub struct BenchResult { + pub method: Vec, + pub elapses: Vec, + pub reads: u32, + pub repeat_reads: u32, + pub writes: u32, + pub repeat_writes: u32, +} + +mod macros; + +#[cfg(feature = "std")] +pub mod bench_runner; +#[cfg(feature = "std")] +pub mod handler; diff --git a/bencher/src/macros.rs b/bencher/src/macros.rs new file mode 100644 index 000000000..0175ef40c --- /dev/null +++ b/bencher/src/macros.rs @@ -0,0 +1,160 @@ +/// Run benches in WASM environment. +/// +/// Configure your module to build the mock runtime into wasm code. +/// Create a `build.rs` like you do with your runtime. +/// ```.ignore +/// use substrate_wasm_builder::WasmBuilder; +/// fn main() { +/// WasmBuilder::new() +/// .with_current_project() +/// .export_heap_base() +/// .import_memory() +/// .build() +/// } +/// ``` +/// +/// Update mock runtime to be build into wasm code. +/// ```.ignore +/// #![cfg_attr(not(feature = "std"), no_std)] +/// +/// #[cfg(feature = "std")] +/// include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +/// +/// #[cfg(feature = "std")] +/// pub fn wasm_binary_unwrap() -> &'static [u8] { WASM_BINARY.unwrap() } +/// .. +/// ``` +/// +/// Create a file `bench_runner.rs` with following code: +/// ```.ignore +/// orml_bencher::run_benches!(my_module::benches); +/// ``` +/// +/// Update Cargo.toml by adding: +/// ```toml +/// .. +/// [package] +/// name = "my-module" +/// .. +/// build = 'build.rs' +/// +/// [build-dependencies] +/// substrate-wasm-builder = '4.0.0' +/// +/// [[bench]] +/// name = 'benches' +/// harness = false +/// path = 'bench_runner.rs' +/// required-features = ['bench'] +/// +/// [features] +/// bench = [] +/// .. +/// ``` +/// +/// Run bench with features bench: `cargo bench --features=bench` +#[cfg(feature = "std")] +#[macro_export] +macro_rules! run_benches { + ($benches:path) => { + use $benches::{wasm_binary_unwrap, Block}; + pub fn main() { + let output = $crate::bench_runner::run::(wasm_binary_unwrap().to_vec()); + $crate::handler::handle(output); + } + }; +} + +/// Define benches +/// +/// Create a file `src/benches.rs`: +/// ```.ignore +/// #![cfg_attr(not(feature = "std"), no_std)] +/// #![allow(dead_code)] +/// +/// #[cfg(feature = "std")] // Re-export for bench_runner +/// pub use crate::mock::{Block, wasm_binary_unwrap}; +/// +/// use crate::mock::YourModule; +/// +/// fn foo(b: &mut Bencher) { +/// b.bench("foo", || { +/// YourModule::foo(); +/// }); +/// } +/// +/// fn bar(b: &mut Bencher) { +/// b.bench("bar", || { +/// YourModule::bar(); +/// }); +/// } +/// +/// orml_bencher::bench!(foo, bar); +/// ``` +/// Update `src/lib.rs`: +/// ```.ignore +/// #[cfg(any(feature = "bench", test))] +/// pub mod mock; /* mock runtime needs to be compiled into wasm */ +/// #[cfg(feature = "bench")] +/// pub mod benches; +/// ``` +#[macro_export] +macro_rules! bench { + ( + $($method:path),+ + ) => { + use $crate::BenchResult; + use $crate::sp_std::{cmp::max, prelude::Vec}; + use $crate::frame_benchmarking::{benchmarking, BenchmarkResults}; + + #[derive(Default, Clone, PartialEq, Debug)] + struct Bencher { + pub results: Vec, + } + + impl Bencher { + pub fn bench(&mut self, name: &str, block: fn() -> ()) { + // Warm up the DB + benchmarking::commit_db(); + benchmarking::wipe_db(); + + let mut result = BenchResult { + method: name.as_bytes().to_vec(), + ..Default::default() + }; + + for _ in 0..50 { + benchmarking::commit_db(); + benchmarking::reset_read_write_count(); + + let start_time = benchmarking::current_time(); + block(); + let end_time = benchmarking::current_time(); + let elasped = end_time - start_time; + result.elapses.push(elasped); + + benchmarking::commit_db(); + let (reads, repeat_reads, writes, repeat_writes) = benchmarking::read_write_count(); + + result.reads = max(result.reads, reads); + result.repeat_reads = max(result.repeat_reads, repeat_reads); + result.writes = max(result.writes, writes); + result.repeat_writes = max(result.repeat_writes, repeat_writes); + + benchmarking::wipe_db(); + } + self.results.push(result); + } + } + + $crate::sp_core::wasm_export_functions! { + fn run_benches() -> Vec { + let mut bencher = Bencher::default(); + $( + $method(&mut bencher); + )+ + bencher.results + } + } + } +} diff --git a/bencher/src/template.hbs b/bencher/src/template.hbs new file mode 100644 index 000000000..470b00c25 --- /dev/null +++ b/bencher/src/template.hbs @@ -0,0 +1,23 @@ +{{header}} + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(dead_code)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct ModuleWeights(PhantomData); +impl ModuleWeights { + {{~#each benchmarks as |benchmark|}} + pub fn {{benchmark.name~}} () -> Weight { + ({{underscore benchmark.base_weight}} as Weight) + {{~#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight)) + {{~/if}} + {{~#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight)) + {{~/if}} + } + {{~/each}} +} diff --git a/weight-gen/Cargo.toml b/weight-gen/Cargo.toml new file mode 100644 index 000000000..81f68bd1a --- /dev/null +++ b/weight-gen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "weight-gen" +description = "CLI for generating weight from bencher output" +license = "Apache-2.0" +version = "0.1.0" +authors = ["Laminar Developers "] +edition = "2018" + +[dependencies] +serde = { features = ['derive'], optional = true, version = "1.0.119" } +serde_json = "1.0" +clap = "3.0.0-beta.2" +handlebars = {version = "3.5.2", optional = true } + +[features] +default = ["std"] +std = [ + "handlebars", + "serde/std", +] \ No newline at end of file diff --git a/weight-gen/src/main.rs b/weight-gen/src/main.rs new file mode 100644 index 000000000..19d86aee5 --- /dev/null +++ b/weight-gen/src/main.rs @@ -0,0 +1,185 @@ +use serde::{Serialize, Deserialize}; +use clap::{AppSettings, Clap}; +use std::io::Read; + +#[derive(Clap)] +#[clap(version = "01.0", author = "Laminar Developers ")] +#[clap(setting = AppSettings::ColoredHelp)] +struct Opts { + input: Option, + #[clap(short, long)] + template: Option, + #[clap(short, long)] + header: Option, + #[clap(short, long)] + out: Option, +} + +#[cfg(feature = "std")] +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct BenchData { + pub name: String, + pub base_weight: u64, + pub base_reads: u32, + pub base_writes: u32, +} + +#[derive(Serialize, Default, Debug, Clone)] +struct TemplateData { + pub header: String, + pub benchmarks: Vec, +} + +// A Handlebars helper to add an underscore after every 3rd character, +// i.e. a separator for large numbers. +#[derive(Clone, Copy)] +struct UnderscoreHelper; +impl handlebars::HelperDef for UnderscoreHelper { + fn call<'reg: 'rc, 'rc>( + &self, + h: &handlebars::Helper, + _: &handlebars::Handlebars, + _: &handlebars::Context, + _rc: &mut handlebars::RenderContext, + out: &mut dyn handlebars::Output, + ) -> handlebars::HelperResult { + use handlebars::JsonRender; + let param = h.param(0).expect("Unable to retrieve param from handlebars helper"); + let underscore_param = underscore(param.value().render()); + out.write(&underscore_param)?; + Ok(()) + } +} + +// Add an underscore after every 3rd character, i.e. a separator for large +// numbers. +fn underscore(i: Number) -> String +where + Number: std::string::ToString, +{ + let mut s = String::new(); + let i_str = i.to_string(); + let a = i_str.chars().rev().enumerate(); + for (idx, val) in a { + if idx != 0 && idx % 3 == 0 { + s.insert(0, '_'); + } + s.insert(0, val); + } + s +} + +// A helper to join a string of vectors. +#[derive(Clone, Copy)] +struct JoinHelper; +impl handlebars::HelperDef for JoinHelper { + fn call<'reg: 'rc, 'rc>( + &self, + h: &handlebars::Helper, + _: &handlebars::Handlebars, + _: &handlebars::Context, + _rc: &mut handlebars::RenderContext, + out: &mut dyn handlebars::Output, + ) -> handlebars::HelperResult { + use handlebars::JsonRender; + let param = h.param(0).expect("Unable to retrieve param from handlebars helper"); + let value = param.value(); + let joined = if value.is_array() { + value + .as_array() + .unwrap() + .iter() + .map(|v| v.render()) + .collect::>() + .join(" ") + } else { + value.render() + }; + out.write(&joined)?; + Ok(()) + } +} + +fn parse_stdio() -> Option> { + let mut buffer = String::new(); + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + + handle.read_to_string(&mut buffer).expect("Unable to read from stdin"); + + let lines: Vec<&str> = buffer.split("\n").collect(); + for line in lines { + let json = serde_json::from_str(line); + + if let Ok(data) = json { + return Some(data); + } + } + + None +} + +fn main() { + let opts: Opts = Opts::parse(); + + let benchmarks: Vec = { + if let Some(data) = opts.input { + serde_json::from_str(&data).expect("Could not parse JSON data") + } else { + parse_stdio().expect("Could not parse JSON data") + } + }; + + let mut handlebars = handlebars::Handlebars::new(); + handlebars.register_helper("underscore", Box::new(UnderscoreHelper)); + handlebars.register_helper("join", Box::new(JoinHelper)); + // Don't HTML escape any characters. + handlebars.register_escape_fn(|s| -> String { s.to_string() }); + + // Use empty header if a header path is not given. + let header = { + if let Some(path) = opts.header { + let header_string = ::std::fs::read_to_string(&path) + .expect(&format!("Header file not found at {}", &path)); + + header_string + } else { + String::from("") + } + }; + + let hbs_data = TemplateData { + header, + benchmarks, + }; + + const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs"); + + // Use default template if template path is not given. + let template = { + if let Some(path) = opts.template { + let template_string = ::std::fs::read_to_string(&path) + .expect(&format!("Template file not found at {}", &path)); + + template_string + } else { + String::from(DEFAULT_TEMPLATE) + } + }; + + // Write benchmark to file or print to terminal if output path is not given. + if let Some(path) = opts.out { + let mut output_file = ::std::fs::File::create(&path) + .expect(&format!("Could not create output file at {}", &path)); + + handlebars + .render_template_to_write(&template, &hbs_data, &mut output_file) + .expect("Unable to render template"); + } else { + let template_string = handlebars + .render_template(&template, &hbs_data) + .expect("Unable to render template"); + + println!("{}", template_string); + } +} \ No newline at end of file diff --git a/weight-gen/src/template.hbs b/weight-gen/src/template.hbs new file mode 100644 index 000000000..470b00c25 --- /dev/null +++ b/weight-gen/src/template.hbs @@ -0,0 +1,23 @@ +{{header}} + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(dead_code)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct ModuleWeights(PhantomData); +impl ModuleWeights { + {{~#each benchmarks as |benchmark|}} + pub fn {{benchmark.name~}} () -> Weight { + ({{underscore benchmark.base_weight}} as Weight) + {{~#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight)) + {{~/if}} + {{~#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight)) + {{~/if}} + } + {{~/each}} +} From f41455052c65882f46e0c238f35b542e045ad112 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 16 Apr 2021 20:10:09 +0200 Subject: [PATCH 25/29] fix bencher dependencies --- bencher/Cargo.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bencher/Cargo.toml b/bencher/Cargo.toml index cbe74919c..6cb4f5aae 100644 --- a/bencher/Cargo.toml +++ b/bencher/Cargo.toml @@ -17,14 +17,14 @@ handlebars = {version = "3.5.2", optional = true } serde = { features = ['derive'], optional = true, version = "1.0.119" } serde_json = "1.0" codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"], default-features = false } -sp-core = { version = "3.0.0", default-features = false } -sp-std = { version = "3.0.0", default-features = false } -sp-io = { version = "3.0.0", default-features = false } -sp-runtime-interface = { version = "3.0.0", default-features = false } -sp-state-machine = { version = "0.9.0", default-features = false, optional = true } -sc-executor = { version = "0.9.0", default-features = false, optional = true } -sc-client-db = { version = "0.9.0", default-features = false, features = ["with-kvdb-rocksdb"], optional = true } -frame-benchmarking = { version = "3.1.0", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-runtime-interface = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false, optional = true } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false, features = ["wasmtime"], optional = true } +sc-client-db = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false, features = ["with-kvdb-rocksdb"], optional = true } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } [features] default = ["std"] From 1ce44f7b49ceb3a014ee765992cc882b299fbe08 Mon Sep 17 00:00:00 2001 From: brettkolodny Date: Tue, 20 Apr 2021 10:37:44 -0400 Subject: [PATCH 26/29] Removed bin from Cargo.toml (#458) --- bencher/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bencher/Cargo.toml b/bencher/Cargo.toml index 6cb4f5aae..5ff55205b 100644 --- a/bencher/Cargo.toml +++ b/bencher/Cargo.toml @@ -7,10 +7,6 @@ version = "0.1.0" authors = ["Laminar Developers "] edition = "2018" -[[bin]] -path = "src/bin.rs" -name = "codegen" - [dependencies] linregress = { version = "0.4.0", optional = true } handlebars = {version = "3.5.2", optional = true } From 98685816508c361a965137c125e3178d67e13af5 Mon Sep 17 00:00:00 2001 From: brettkolodny Date: Wed, 21 Apr 2021 16:22:22 -0400 Subject: [PATCH 27/29] Weight meter (#459) * Added argument to BenchmarkingState --- Cargo.dev.toml | 2 ++ bencher/src/bench_runner.rs | 2 +- bencher/src/template.hbs | 23 ----------------------- 3 files changed, 3 insertions(+), 24 deletions(-) delete mode 100644 bencher/src/template.hbs diff --git a/Cargo.dev.toml b/Cargo.dev.toml index 3c16f9bd8..e10d3796f 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -4,6 +4,7 @@ cargo-features = ["resolver"] members = [ "auction", "authority", + "bencher", "benchmarking", "currencies", "gradually-update", @@ -20,6 +21,7 @@ members = [ "xcm-support", "unknown-tokens", "build-script-utils", + "weight-gen", "weight-meter", ] resolver = "2" diff --git a/bencher/src/bench_runner.rs b/bencher/src/bench_runner.rs index 6f0d49075..26fabc066 100644 --- a/bencher/src/bench_runner.rs +++ b/bencher/src/bench_runner.rs @@ -12,7 +12,7 @@ use sp_state_machine::{Ext, OverlayedChanges, StorageTransactionCache}; pub fn run(wasm_code: Vec) -> Vec { let mut overlay = OverlayedChanges::default(); let mut cache = StorageTransactionCache::default(); - let state = BenchmarkingState::::new(Default::default(), Default::default()).unwrap(); + let state = BenchmarkingState::::new(Default::default(), Default::default(), false).unwrap(); let mut ext = Ext::<_, NumberFor, _>::new(&mut overlay, &mut cache, &state, None, None); let mut host_functions = benchmarking::HostFunctions::host_functions(); diff --git a/bencher/src/template.hbs b/bencher/src/template.hbs deleted file mode 100644 index 470b00c25..000000000 --- a/bencher/src/template.hbs +++ /dev/null @@ -1,23 +0,0 @@ -{{header}} - -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(dead_code)] - -use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; - -pub struct ModuleWeights(PhantomData); -impl ModuleWeights { - {{~#each benchmarks as |benchmark|}} - pub fn {{benchmark.name~}} () -> Weight { - ({{underscore benchmark.base_weight}} as Weight) - {{~#if (ne benchmark.base_reads "0")}} - .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight)) - {{~/if}} - {{~#if (ne benchmark.base_writes "0")}} - .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight)) - {{~/if}} - } - {{~/each}} -} From 77c429093a4dc1c64df1d43396bc376c05b1d85a Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Thu, 22 Apr 2021 11:26:56 +1200 Subject: [PATCH 28/29] fix --- bencher/src/handler.rs | 2 +- bencher/src/macros.rs | 2 +- weight-gen/src/main.rs | 124 ++++++++++++++++++++--------------------- 3 files changed, 62 insertions(+), 66 deletions(-) diff --git a/bencher/src/handler.rs b/bencher/src/handler.rs index 0d174c597..8533d2db1 100644 --- a/bencher/src/handler.rs +++ b/bencher/src/handler.rs @@ -1,7 +1,7 @@ use crate::BenchResult; -use serde::{Serialize, Deserialize}; use codec::Decode; use linregress::{FormulaRegressionBuilder, RegressionDataBuilder}; +use serde::{Deserialize, Serialize}; use std::io::Write; #[derive(Serialize, Deserialize, Default, Debug, Clone)] diff --git a/bencher/src/macros.rs b/bencher/src/macros.rs index 0175ef40c..f6548136a 100644 --- a/bencher/src/macros.rs +++ b/bencher/src/macros.rs @@ -113,7 +113,7 @@ macro_rules! bench { } impl Bencher { - pub fn bench(&mut self, name: &str, block: fn() -> ()) { + pub fn bench ()>(&mut self, name: &str, block: F) { // Warm up the DB benchmarking::commit_db(); benchmarking::wipe_db(); diff --git a/weight-gen/src/main.rs b/weight-gen/src/main.rs index 19d86aee5..3cdf1052b 100644 --- a/weight-gen/src/main.rs +++ b/weight-gen/src/main.rs @@ -1,18 +1,18 @@ -use serde::{Serialize, Deserialize}; use clap::{AppSettings, Clap}; +use serde::{Deserialize, Serialize}; use std::io::Read; #[derive(Clap)] #[clap(version = "01.0", author = "Laminar Developers ")] #[clap(setting = AppSettings::ColoredHelp)] struct Opts { - input: Option, - #[clap(short, long)] - template: Option, - #[clap(short, long)] - header: Option, - #[clap(short, long)] - out: Option, + input: Option, + #[clap(short, long)] + template: Option, + #[clap(short, long)] + header: Option, + #[clap(short, long)] + out: Option, } #[cfg(feature = "std")] @@ -101,76 +101,72 @@ impl handlebars::HelperDef for JoinHelper { } fn parse_stdio() -> Option> { - let mut buffer = String::new(); - let stdin = std::io::stdin(); - let mut handle = stdin.lock(); + let mut buffer = String::new(); + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); - handle.read_to_string(&mut buffer).expect("Unable to read from stdin"); + handle.read_to_string(&mut buffer).expect("Unable to read from stdin"); - let lines: Vec<&str> = buffer.split("\n").collect(); - for line in lines { - let json = serde_json::from_str(line); + let lines: Vec<&str> = buffer.split("\n").collect(); + for line in lines { + let json = serde_json::from_str(line); - if let Ok(data) = json { - return Some(data); - } - } + if let Ok(data) = json { + return Some(data); + } + } - None + None } fn main() { - let opts: Opts = Opts::parse(); + let opts: Opts = Opts::parse(); - let benchmarks: Vec = { - if let Some(data) = opts.input { - serde_json::from_str(&data).expect("Could not parse JSON data") - } else { - parse_stdio().expect("Could not parse JSON data") - } - }; + let benchmarks: Vec = { + if let Some(data) = opts.input { + serde_json::from_str(&data).expect("Could not parse JSON data") + } else { + parse_stdio().expect("Could not parse JSON data") + } + }; - let mut handlebars = handlebars::Handlebars::new(); + let mut handlebars = handlebars::Handlebars::new(); handlebars.register_helper("underscore", Box::new(UnderscoreHelper)); handlebars.register_helper("join", Box::new(JoinHelper)); // Don't HTML escape any characters. handlebars.register_escape_fn(|s| -> String { s.to_string() }); - // Use empty header if a header path is not given. - let header = { - if let Some(path) = opts.header { - let header_string = ::std::fs::read_to_string(&path) - .expect(&format!("Header file not found at {}", &path)); + // Use empty header if a header path is not given. + let header = { + if let Some(path) = opts.header { + let header_string = ::std::fs::read_to_string(&path).expect(&format!("Header file not found at {}", &path)); header_string - } else { - String::from("") - } - }; - - let hbs_data = TemplateData { - header, - benchmarks, - }; - - const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs"); - - // Use default template if template path is not given. - let template = { - if let Some(path) = opts.template { - let template_string = ::std::fs::read_to_string(&path) - .expect(&format!("Template file not found at {}", &path)); - - template_string - } else { - String::from(DEFAULT_TEMPLATE) - } - }; - - // Write benchmark to file or print to terminal if output path is not given. - if let Some(path) = opts.out { - let mut output_file = ::std::fs::File::create(&path) - .expect(&format!("Could not create output file at {}", &path)); + } else { + String::from("") + } + }; + + let hbs_data = TemplateData { header, benchmarks }; + + const DEFAULT_TEMPLATE: &str = include_str!("./template.hbs"); + + // Use default template if template path is not given. + let template = { + if let Some(path) = opts.template { + let template_string = + ::std::fs::read_to_string(&path).expect(&format!("Template file not found at {}", &path)); + + template_string + } else { + String::from(DEFAULT_TEMPLATE) + } + }; + + // Write benchmark to file or print to terminal if output path is not given. + if let Some(path) = opts.out { + let mut output_file = + ::std::fs::File::create(&path).expect(&format!("Could not create output file at {}", &path)); handlebars .render_template_to_write(&template, &hbs_data, &mut output_file) @@ -181,5 +177,5 @@ fn main() { .expect("Unable to render template"); println!("{}", template_string); - } -} \ No newline at end of file + } +} From 2d4fe5466990a4623c59eca721d2531e6941f541 Mon Sep 17 00:00:00 2001 From: brettkolodny Date: Thu, 22 Apr 2021 16:14:51 -0400 Subject: [PATCH 29/29] Updated frame-support version (#461) --- weight-meter/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weight-meter/Cargo.toml b/weight-meter/Cargo.toml index 2265bb15e..e9e527744 100644 --- a/weight-meter/Cargo.toml +++ b/weight-meter/Cargo.toml @@ -10,7 +10,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] spin = "0.7.1" -frame-support = { version = "3.0.0", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "rococo-v1", default-features = false } weight-meter-procedural = { path = "weight-meter-procedural", default-features = false } [features]