feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
[package]
|
||||
name = "pezsp-api"
|
||||
version = "26.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Bizinikiwi runtime api primitives"
|
||||
readme = "README.md"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
docify = { workspace = true }
|
||||
hash-db = { optional = true, workspace = true, default-features = true }
|
||||
log = { workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
pezsp-api-proc-macro = { workspace = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-externalities = { optional = true, workspace = true }
|
||||
pezsp-metadata-ir = { optional = true, workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
pezsp-runtime-interface = { workspace = true }
|
||||
pezsp-state-machine = { optional = true, workspace = true }
|
||||
pezsp-trie = { optional = true, workspace = true }
|
||||
pezsp-version = { workspace = true }
|
||||
thiserror = { optional = true, workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pezsp-test-primitives = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"hash-db",
|
||||
"log/std",
|
||||
"scale-info/std",
|
||||
"pezsp-api-proc-macro/std",
|
||||
"pezsp-core/std",
|
||||
"pezsp-externalities",
|
||||
"pezsp-externalities?/std",
|
||||
"pezsp-metadata-ir?/std",
|
||||
"pezsp-runtime-interface/std",
|
||||
"pezsp-runtime/std",
|
||||
"pezsp-state-machine/std",
|
||||
"pezsp-test-primitives/std",
|
||||
"pezsp-trie/std",
|
||||
"pezsp-version/std",
|
||||
"thiserror",
|
||||
]
|
||||
# Special feature to disable logging completely.
|
||||
#
|
||||
# By default `pezsp-api` initializes the `RuntimeLogger` for each runtime api function. However,
|
||||
# logging functionality increases the code size. It is recommended to enable this feature when
|
||||
# building a runtime for registering it on chain.
|
||||
#
|
||||
# This sets the max logging level to `off` for `log`.
|
||||
disable-logging = ["log/max_level_off"]
|
||||
# Do not report the documentation in the metadata.
|
||||
no-metadata-docs = ["pezsp-api-proc-macro/no-metadata-docs"]
|
||||
frame-metadata = ["pezsp-metadata-ir"]
|
||||
runtime-benchmarks = [
|
||||
"pezsp-runtime-interface/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"pezsp-state-machine?/runtime-benchmarks",
|
||||
"pezsp-test-primitives/runtime-benchmarks",
|
||||
"pezsp-trie?/runtime-benchmarks",
|
||||
"pezsp-version/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
Bizinikiwi runtime api
|
||||
|
||||
The Bizinikiwi runtime api is the crucial interface between the node and the runtime.
|
||||
Every call that goes into the runtime is done with a runtime api. The runtime apis are not fixed.
|
||||
Every Bizinikiwi user can define its own apis with
|
||||
[`decl_runtime_apis`](https://docs.rs/pezsp-api/latest/sp_api/macro.decl_runtime_apis.html) and implement them in
|
||||
the runtime with [`impl_runtime_apis`](https://docs.rs/pezsp-api/latest/sp_api/macro.impl_runtime_apis.html).
|
||||
|
||||
Every Bizinikiwi runtime needs to implement the [`Core`] runtime api. This api provides the basic
|
||||
functionality that every runtime needs to export.
|
||||
|
||||
Besides the macros and the [`Core`] runtime api, this crates provides the [`Metadata`] runtime
|
||||
api, the [`ApiExt`] trait, the [`CallApiAt`] trait and the [`ConstructRuntimeApi`] trait.
|
||||
|
||||
On a meta level this implies, the client calls the generated API from the client perspective.
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -0,0 +1,43 @@
|
||||
[package]
|
||||
name = "pezsp-api-proc-macro"
|
||||
version = "15.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Macros for declaring and implementing runtime apis."
|
||||
documentation = "https://docs.rs/pezsp-api-proc-macro"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
Inflector = { workspace = true }
|
||||
blake2 = { workspace = true }
|
||||
expander = { workspace = true }
|
||||
proc-macro-crate = { workspace = true }
|
||||
proc-macro2 = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
syn = { features = [
|
||||
"extra-traits",
|
||||
"fold",
|
||||
"full",
|
||||
"visit",
|
||||
"visit-mut",
|
||||
], workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = { workspace = true }
|
||||
|
||||
[features]
|
||||
# Required for the doc tests
|
||||
default = ["std"]
|
||||
std = ["blake2/std"]
|
||||
no-metadata-docs = []
|
||||
@@ -0,0 +1,39 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The ident used for the block generic parameter.
|
||||
pub const BLOCK_GENERIC_IDENT: &str = "Block";
|
||||
|
||||
/// The `core_trait` attribute.
|
||||
pub const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
|
||||
/// The `api_version` attribute.
|
||||
///
|
||||
/// Is used to set the current version of the trait.
|
||||
pub const API_VERSION_ATTRIBUTE: &str = "api_version";
|
||||
/// The `changed_in` attribute.
|
||||
///
|
||||
/// Is used when the function signature changed between different versions of a trait.
|
||||
/// This attribute should be placed on the old signature of the function.
|
||||
pub const CHANGED_IN_ATTRIBUTE: &str = "changed_in";
|
||||
/// The `renamed` attribute.
|
||||
///
|
||||
/// Is used when a trait method was renamed.
|
||||
pub const RENAMED_ATTRIBUTE: &str = "renamed";
|
||||
|
||||
/// All attributes that we support in the declaration of a runtime api trait.
|
||||
pub const SUPPORTED_ATTRIBUTE_NAMES: &[&str] =
|
||||
&[CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE, CHANGED_IN_ATTRIBUTE, RENAMED_ATTRIBUTE];
|
||||
@@ -0,0 +1,751 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
|
||||
RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
|
||||
},
|
||||
utils::{
|
||||
extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side,
|
||||
generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
|
||||
prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
|
||||
versioned_trait_name, AllowSelfRefInParameters,
|
||||
},
|
||||
};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use syn::{
|
||||
fold::{self, Fold},
|
||||
parse::{Error, Parse, ParseStream, Result},
|
||||
parse_macro_input, parse_quote,
|
||||
spanned::Spanned,
|
||||
token::Comma,
|
||||
visit::{self, Visit},
|
||||
Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, LitInt, LitStr, TraitBound,
|
||||
TraitItem, TraitItemFn,
|
||||
};
|
||||
|
||||
/// The structure used for parsing the runtime api declarations.
|
||||
struct RuntimeApiDecls {
|
||||
decls: Vec<ItemTrait>,
|
||||
}
|
||||
|
||||
impl Parse for RuntimeApiDecls {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut decls = Vec::new();
|
||||
|
||||
while !input.is_empty() {
|
||||
decls.push(ItemTrait::parse(input)?);
|
||||
}
|
||||
|
||||
Ok(Self { decls })
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the given generics with `Block: BlockT` as first generic parameter.
|
||||
fn extend_generics_with_block(generics: &mut Generics) {
|
||||
let c = generate_crate_access();
|
||||
|
||||
generics.lt_token = Some(Default::default());
|
||||
generics.params.insert(0, parse_quote!( Block: #c::BlockT ));
|
||||
generics.gt_token = Some(Default::default());
|
||||
}
|
||||
|
||||
/// Remove all attributes from the vector that are supported by us in the declaration of a runtime
|
||||
/// api trait. The returned hashmap contains all found attribute names as keys and the rest of the
|
||||
/// attribute body as `TokenStream`.
|
||||
fn remove_supported_attributes(attrs: &mut Vec<Attribute>) -> HashMap<&'static str, Attribute> {
|
||||
let mut result = HashMap::new();
|
||||
attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path().is_ident(a)) {
|
||||
Some(attribute) => {
|
||||
result.insert(*attribute, v.clone());
|
||||
false
|
||||
},
|
||||
None => true,
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Versioned API traits are used to catch missing methods when implementing a specific version of a
|
||||
/// versioned API. They contain all non-versioned methods (aka stable methods) from the main trait
|
||||
/// and all versioned methods for the specific version. This means that there is one trait for each
|
||||
/// version mentioned in the trait definition. For example:
|
||||
/// ```ignore
|
||||
/// // The trait version implicitly is 1
|
||||
/// decl_runtime_apis!(
|
||||
/// trait SomeApi {
|
||||
/// fn method1(); // this is a 'stable method'
|
||||
///
|
||||
/// #[api_version(2)]
|
||||
/// fn method2();
|
||||
///
|
||||
/// #[api_version(2)]
|
||||
/// fn method3();
|
||||
///
|
||||
/// #[api_version(3)]
|
||||
/// fn method4();
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
/// This trait has got three different versions. The function below will generate the following
|
||||
/// code:
|
||||
/// ```
|
||||
/// trait SomeApiV1 {
|
||||
/// // in V1 only the stable methods are required. The rest has got default implementations.
|
||||
/// fn method1();
|
||||
/// }
|
||||
///
|
||||
/// trait SomeApiV2 {
|
||||
/// // V2 contains all methods from V1 and V2. V3 not required so they are skipped.
|
||||
/// fn method1();
|
||||
/// fn method2();
|
||||
/// fn method3();
|
||||
/// }
|
||||
///
|
||||
/// trait SomeApiV3 {
|
||||
/// // And V3 contains all methods from the trait.
|
||||
/// fn method1();
|
||||
/// fn method2();
|
||||
/// fn method3();
|
||||
/// fn method4();
|
||||
/// }
|
||||
/// ```
|
||||
fn generate_versioned_api_traits(
|
||||
api: ItemTrait,
|
||||
methods: BTreeMap<u32, Vec<TraitItemFn>>,
|
||||
) -> Vec<ItemTrait> {
|
||||
let mut result = Vec::<ItemTrait>::new();
|
||||
for (version, _) in &methods {
|
||||
let mut versioned_trait = api.clone();
|
||||
versioned_trait.ident = versioned_trait_name(&versioned_trait.ident, *version);
|
||||
versioned_trait.items = Vec::new();
|
||||
// Add the methods from the current version and all previous one. Versions are sorted so
|
||||
// it's safe to stop early.
|
||||
for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) {
|
||||
versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Fn(m)));
|
||||
}
|
||||
|
||||
result.push(versioned_trait);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Try to parse the given `Attribute` as `renamed` attribute.
|
||||
fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> {
|
||||
let err = || {
|
||||
Error::new(
|
||||
renamed.span(),
|
||||
&format!(
|
||||
"Unexpected `{RENAMED_ATTRIBUTE}` attribute. \
|
||||
The supported format is `{RENAMED_ATTRIBUTE}(\"old_name\", version_it_was_renamed)`",
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
renamed
|
||||
.parse_args_with(|input: ParseStream| {
|
||||
let old_name: LitStr = input.parse()?;
|
||||
let _comma: Comma = input.parse()?;
|
||||
let version: LitInt = input.parse()?;
|
||||
|
||||
if !input.is_empty() {
|
||||
return Err(input.error("No more arguments expected"));
|
||||
}
|
||||
|
||||
Ok((old_name.value(), version.base10_parse()?))
|
||||
})
|
||||
.map_err(|_| err())
|
||||
}
|
||||
|
||||
/// Generate the declaration of the trait for the runtime.
|
||||
fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for decl in decls {
|
||||
let mut decl = decl.clone();
|
||||
let decl_span = decl.span();
|
||||
extend_generics_with_block(&mut decl.generics);
|
||||
let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
|
||||
|
||||
let found_attributes = remove_supported_attributes(&mut decl.attrs);
|
||||
let api_version = get_api_version(&found_attributes).map(generate_runtime_api_version)?;
|
||||
let id = generate_runtime_api_id(&decl.ident.to_string());
|
||||
|
||||
let trait_api_version = get_api_version(&found_attributes)?;
|
||||
|
||||
let mut methods_by_version: BTreeMap<u32, Vec<TraitItemFn>> = BTreeMap::new();
|
||||
|
||||
// Process the items in the declaration. The filter_map function below does a lot of stuff
|
||||
// because the method attributes are stripped at this point
|
||||
decl.items.iter_mut().for_each(|i| {
|
||||
match i {
|
||||
TraitItem::Fn(ref mut method) => {
|
||||
let method_attrs = remove_supported_attributes(&mut method.attrs);
|
||||
let mut method_version = trait_api_version;
|
||||
// validate the api version for the method (if any) and generate default
|
||||
// implementation for versioned methods
|
||||
if let Some(version_attribute) = method_attrs.get(API_VERSION_ATTRIBUTE) {
|
||||
method_version = match parse_runtime_api_version(version_attribute) {
|
||||
Ok(method_api_ver) if method_api_ver < trait_api_version => {
|
||||
let method_ver = method_api_ver.to_string();
|
||||
let trait_ver = trait_api_version.to_string();
|
||||
let mut err1 = Error::new(
|
||||
version_attribute.span(),
|
||||
format!(
|
||||
"Method version `{}` is older than (or equal to) trait version `{}`.\
|
||||
Methods can't define versions older than the trait version.",
|
||||
method_ver,
|
||||
trait_ver,
|
||||
),
|
||||
);
|
||||
|
||||
let err2 = match found_attributes.get(&API_VERSION_ATTRIBUTE) {
|
||||
Some(attr) =>
|
||||
Error::new(attr.span(), "Trait version is set here."),
|
||||
None => Error::new(
|
||||
decl_span,
|
||||
"Trait version is not set so it is implicitly equal to 1.",
|
||||
),
|
||||
};
|
||||
err1.combine(err2);
|
||||
result.push(err1.to_compile_error());
|
||||
|
||||
trait_api_version
|
||||
},
|
||||
Ok(method_api_ver) => method_api_ver,
|
||||
Err(e) => {
|
||||
result.push(e.to_compile_error());
|
||||
trait_api_version
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Any method with the `changed_in` attribute isn't required for the runtime
|
||||
// anymore.
|
||||
if !method_attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
|
||||
// Make sure we replace all the wild card parameter names.
|
||||
replace_wild_card_parameter_names(&mut method.sig);
|
||||
|
||||
// partition methods by api version
|
||||
methods_by_version.entry(method_version).or_default().push(method.clone());
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
let versioned_methods_iter = methods_by_version
|
||||
.iter()
|
||||
.flat_map(|(&version, methods)| methods.iter().map(move |method| (method, version)));
|
||||
let metadata =
|
||||
crate::runtime_metadata::generate_decl_runtime_metadata(&decl, versioned_methods_iter);
|
||||
|
||||
let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version);
|
||||
|
||||
let main_api_ident = decl.ident.clone();
|
||||
let versioned_ident = &versioned_api_traits
|
||||
.first()
|
||||
.expect("There should always be at least one version.")
|
||||
.ident;
|
||||
|
||||
result.push(quote!(
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
#[allow(deprecated)]
|
||||
pub mod #mod_name {
|
||||
pub use super::*;
|
||||
|
||||
#( #versioned_api_traits )*
|
||||
|
||||
pub use #versioned_ident as #main_api_ident;
|
||||
|
||||
#metadata
|
||||
|
||||
pub #api_version
|
||||
|
||||
pub #id
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
Ok(quote!( #( #result )* ))
|
||||
}
|
||||
|
||||
/// Modify the given runtime api declaration to be usable on the client side.
|
||||
struct ToClientSideDecl<'a> {
|
||||
block_hash: &'a TokenStream,
|
||||
crate_: &'a TokenStream,
|
||||
found_attributes: &'a mut HashMap<&'static str, Attribute>,
|
||||
/// Any error that we found while converting this declaration.
|
||||
errors: &'a mut Vec<TokenStream>,
|
||||
trait_: &'a Ident,
|
||||
}
|
||||
|
||||
impl<'a> ToClientSideDecl<'a> {
|
||||
/// Process the given [`ItemTrait`].
|
||||
fn process(mut self, decl: ItemTrait) -> ItemTrait {
|
||||
let mut decl = self.fold_item_trait(decl);
|
||||
|
||||
let block_hash = self.block_hash;
|
||||
let crate_ = self.crate_;
|
||||
|
||||
// Add the special method that will be implemented by the `impl_runtime_apis!` macro
|
||||
// to enable functions to call into the runtime.
|
||||
decl.items.push(parse_quote! {
|
||||
/// !!INTERNAL USE ONLY!!
|
||||
#[doc(hidden)]
|
||||
fn __runtime_api_internal_call_api_at(
|
||||
&self,
|
||||
at: #block_hash,
|
||||
params: std::vec::Vec<u8>,
|
||||
fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
|
||||
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError>;
|
||||
});
|
||||
|
||||
decl
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToClientSideDecl<'a> {
|
||||
fn fold_item_trait_items(
|
||||
&mut self,
|
||||
items: Vec<TraitItem>,
|
||||
trait_generics_num: usize,
|
||||
) -> Vec<TraitItem> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
items.into_iter().for_each(|i| match i {
|
||||
TraitItem::Fn(method) => {
|
||||
let fn_decl = self.create_method_decl(method, trait_generics_num);
|
||||
result.push(fn_decl.into());
|
||||
},
|
||||
r => result.push(r),
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Takes the method declared by the user and creates the declaration we require for the runtime
|
||||
/// api client side. This method will call by default the `method_runtime_api_impl` for doing
|
||||
/// the actual call into the runtime.
|
||||
fn create_method_decl(
|
||||
&mut self,
|
||||
mut method: TraitItemFn,
|
||||
trait_generics_num: usize,
|
||||
) -> TraitItemFn {
|
||||
let params = match extract_parameter_names_types_and_borrows(
|
||||
&method.sig,
|
||||
AllowSelfRefInParameters::No,
|
||||
) {
|
||||
Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
|
||||
Err(e) => {
|
||||
self.errors.push(e.to_compile_error());
|
||||
Vec::new()
|
||||
},
|
||||
};
|
||||
let ret_type = return_type_extract_type(&method.sig.output);
|
||||
|
||||
fold_fn_decl_for_client_side(&mut method.sig, self.block_hash, self.crate_);
|
||||
|
||||
let crate_ = self.crate_;
|
||||
|
||||
let found_attributes = remove_supported_attributes(&mut method.attrs);
|
||||
|
||||
// Parse the renamed attributes.
|
||||
let mut renames = Vec::new();
|
||||
for (_, a) in found_attributes.iter().filter(|a| a.0 == &RENAMED_ATTRIBUTE) {
|
||||
match parse_renamed_attribute(a) {
|
||||
Ok((old_name, version)) => {
|
||||
renames.push((version, prefix_function_with_trait(&self.trait_, &old_name)));
|
||||
},
|
||||
Err(e) => self.errors.push(e.to_compile_error()),
|
||||
}
|
||||
}
|
||||
|
||||
renames.sort_by(|l, r| r.cmp(l));
|
||||
let (versions, old_names) = renames.into_iter().fold(
|
||||
(Vec::new(), Vec::new()),
|
||||
|(mut versions, mut old_names), (version, old_name)| {
|
||||
versions.push(version);
|
||||
old_names.push(old_name);
|
||||
(versions, old_names)
|
||||
},
|
||||
);
|
||||
|
||||
// Generate the function name before we may rename it below to
|
||||
// `function_name_before_version_{}`.
|
||||
let function_name = prefix_function_with_trait(&self.trait_, &method.sig.ident);
|
||||
|
||||
// If the method has a `changed_in` attribute, we need to alter the method name to
|
||||
// `method_before_version_VERSION`.
|
||||
match get_changed_in(&found_attributes) {
|
||||
Ok(Some(version)) => {
|
||||
// Make sure that the `changed_in` version is at least the current `api_version`.
|
||||
if get_api_version(self.found_attributes).ok() < Some(version) {
|
||||
self.errors.push(
|
||||
Error::new(
|
||||
method.span(),
|
||||
"`changed_in` version can not be greater than the `api_version`",
|
||||
)
|
||||
.to_compile_error(),
|
||||
);
|
||||
}
|
||||
|
||||
let ident = Ident::new(
|
||||
&format!("{}_before_version_{}", method.sig.ident, version),
|
||||
method.sig.ident.span(),
|
||||
);
|
||||
method.sig.ident = ident;
|
||||
method.attrs.push(parse_quote!( #[deprecated] ));
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err(e) => {
|
||||
self.errors.push(e.to_compile_error());
|
||||
},
|
||||
};
|
||||
|
||||
// The module where the runtime relevant stuff is declared.
|
||||
let trait_name = &self.trait_;
|
||||
let runtime_mod = generate_runtime_mod_name_for_trait(trait_name);
|
||||
let underscores = (0..trait_generics_num).map(|_| quote!(_));
|
||||
|
||||
// Generate the default implementation that calls the `method_runtime_api_impl` method.
|
||||
method.default = Some(parse_quote! {
|
||||
{
|
||||
let __runtime_api_impl_params_encoded__ =
|
||||
#crate_::Encode::encode(&( #( &#params ),* ));
|
||||
|
||||
<Self as #trait_name<#( #underscores ),*>>::__runtime_api_internal_call_api_at(
|
||||
self,
|
||||
__runtime_api_at_param__,
|
||||
__runtime_api_impl_params_encoded__,
|
||||
&|_version| {
|
||||
#(
|
||||
// Check if we need to call the function by an old name.
|
||||
if _version.apis.iter().any(|(s, v)| {
|
||||
s == &#runtime_mod::ID && *v < #versions
|
||||
}) {
|
||||
return #old_names
|
||||
}
|
||||
)*
|
||||
|
||||
#function_name
|
||||
}
|
||||
)
|
||||
.and_then(|r|
|
||||
std::result::Result::map_err(
|
||||
<#ret_type as #crate_::Decode>::decode(&mut &r[..]),
|
||||
|err| #crate_::ApiError::FailedToDecodeReturnValue {
|
||||
function: #function_name,
|
||||
error: err,
|
||||
raw: r.clone(),
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
method
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Fold for ToClientSideDecl<'a> {
|
||||
fn fold_item_trait(&mut self, mut input: ItemTrait) -> ItemTrait {
|
||||
extend_generics_with_block(&mut input.generics);
|
||||
|
||||
*self.found_attributes = remove_supported_attributes(&mut input.attrs);
|
||||
// Check if this is the `Core` runtime api trait.
|
||||
let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
|
||||
let block_ident = Ident::new(BLOCK_GENERIC_IDENT, Span::call_site());
|
||||
|
||||
if is_core_trait {
|
||||
// Add all the supertraits we want to have for `Core`.
|
||||
input.supertraits = parse_quote!('static + Send);
|
||||
} else {
|
||||
// Add the `Core` runtime api as super trait.
|
||||
let crate_ = &self.crate_;
|
||||
input.supertraits.push(parse_quote!( #crate_::Core<#block_ident> ));
|
||||
}
|
||||
|
||||
input.items = self.fold_item_trait_items(input.items, input.generics.params.len());
|
||||
|
||||
fold::fold_item_trait(self, input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the identifier as const variable for the given `trait_name`
|
||||
/// by hashing the `trait_name`.
|
||||
fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
|
||||
use blake2::digest::{consts::U8, Digest};
|
||||
|
||||
let mut res = [0; 8];
|
||||
res.copy_from_slice(blake2::Blake2b::<U8>::digest(trait_name).as_slice());
|
||||
|
||||
quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
|
||||
}
|
||||
|
||||
/// Generates the const variable that holds the runtime api version.
|
||||
fn generate_runtime_api_version(version: u32) -> TokenStream {
|
||||
quote!( const VERSION: u32 = #version; )
|
||||
}
|
||||
|
||||
/// Generates the implementation of `RuntimeApiInfo` for the given trait.
|
||||
fn generate_runtime_info_impl(trait_: &ItemTrait, version: u32) -> TokenStream {
|
||||
let trait_name = &trait_.ident;
|
||||
let crate_ = generate_crate_access();
|
||||
let id = generate_runtime_api_id(&trait_name.to_string());
|
||||
let version = generate_runtime_api_version(version as u32);
|
||||
|
||||
let impl_generics = trait_.generics.type_params().map(|t| {
|
||||
let ident = &t.ident;
|
||||
let colon_token = &t.colon_token;
|
||||
let bounds = &t.bounds;
|
||||
|
||||
quote! { #ident #colon_token #bounds }
|
||||
});
|
||||
|
||||
let ty_generics = trait_.generics.type_params().map(|t| {
|
||||
let ident = &t.ident;
|
||||
quote! { #ident }
|
||||
});
|
||||
let maybe_allow_attrs = trait_.attrs.iter().filter(|attr| attr.path().is_ident("allow"));
|
||||
let maybe_allow_deprecated = trait_.attrs.iter().filter_map(|attr| {
|
||||
attr.path().is_ident("deprecated").then(|| quote! { #[allow(deprecated)] })
|
||||
});
|
||||
|
||||
quote!(
|
||||
#crate_::std_enabled! {
|
||||
#( #maybe_allow_attrs )*
|
||||
#( #maybe_allow_deprecated )*
|
||||
impl < #( #impl_generics, )* > #crate_::RuntimeApiInfo
|
||||
for dyn #trait_name < #( #ty_generics, )* >
|
||||
{
|
||||
#id
|
||||
#version
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Get changed in version from the user given attribute or `Ok(None)`, if no attribute was given.
|
||||
fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result<Option<u32>> {
|
||||
found_attributes
|
||||
.get(&CHANGED_IN_ATTRIBUTE)
|
||||
.map(|v| parse_runtime_api_version(v).map(Some))
|
||||
.unwrap_or(Ok(None))
|
||||
}
|
||||
|
||||
/// Get the api version from the user given attribute or `Ok(1)`, if no attribute was given.
|
||||
fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result<u32> {
|
||||
found_attributes
|
||||
.get(&API_VERSION_ATTRIBUTE)
|
||||
.map(parse_runtime_api_version)
|
||||
.unwrap_or(Ok(1))
|
||||
}
|
||||
|
||||
/// Generate the declaration of the trait for the client side.
|
||||
fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for decl in decls {
|
||||
let decl = decl.clone();
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
let block_hash = quote!( <Block as #crate_::BlockT>::Hash );
|
||||
let mut found_attributes = HashMap::new();
|
||||
let mut errors = Vec::new();
|
||||
let trait_ = decl.ident.clone();
|
||||
|
||||
let decl = ToClientSideDecl {
|
||||
crate_: &crate_,
|
||||
block_hash: &block_hash,
|
||||
found_attributes: &mut found_attributes,
|
||||
errors: &mut errors,
|
||||
trait_: &trait_,
|
||||
}
|
||||
.process(decl);
|
||||
|
||||
let api_version = get_api_version(&found_attributes);
|
||||
|
||||
let runtime_info = api_version.map(|v| generate_runtime_info_impl(&decl, v))?;
|
||||
|
||||
result.push(quote!(
|
||||
#crate_::std_enabled! {
|
||||
#[allow(deprecated)]
|
||||
#decl
|
||||
}
|
||||
#runtime_info
|
||||
#( #errors )*
|
||||
));
|
||||
}
|
||||
|
||||
Ok(quote!( #( #result )* ))
|
||||
}
|
||||
|
||||
/// Checks that a trait declaration is in the format we expect.
|
||||
struct CheckTraitDecl {
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
|
||||
impl CheckTraitDecl {
|
||||
/// Check the given trait.
|
||||
///
|
||||
/// All errors will be collected in `self.errors`.
|
||||
fn check(&mut self, trait_: &ItemTrait) {
|
||||
self.check_method_declarations(trait_.items.iter().filter_map(|i| match i {
|
||||
TraitItem::Fn(method) => Some(method),
|
||||
_ => None,
|
||||
}));
|
||||
|
||||
visit::visit_item_trait(self, trait_);
|
||||
}
|
||||
|
||||
/// Check that the given method declarations are correct.
|
||||
///
|
||||
/// Any error is stored in `self.errors`.
|
||||
fn check_method_declarations<'a>(&mut self, methods: impl Iterator<Item = &'a TraitItemFn>) {
|
||||
let mut method_to_signature_changed = HashMap::<Ident, Vec<Option<u32>>>::new();
|
||||
|
||||
methods.into_iter().for_each(|method| {
|
||||
let attributes = remove_supported_attributes(&mut method.attrs.clone());
|
||||
|
||||
let changed_in = match get_changed_in(&attributes) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
self.errors.push(e);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
method_to_signature_changed
|
||||
.entry(method.sig.ident.clone())
|
||||
.or_default()
|
||||
.push(changed_in);
|
||||
|
||||
if method.default.is_some() {
|
||||
self.errors.push(Error::new(
|
||||
method.default.span(),
|
||||
"A runtime API function cannot have a default implementation!",
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
method_to_signature_changed.into_iter().for_each(|(f, changed)| {
|
||||
// If `changed_in` is `None`, it means it is the current "default" method that calls
|
||||
// into the latest implementation.
|
||||
if changed.iter().filter(|c| c.is_none()).count() == 0 {
|
||||
self.errors.push(Error::new(
|
||||
f.span(),
|
||||
"There is no 'default' method with this name (without `changed_in` attribute).\n\
|
||||
The 'default' method is used to call into the latest implementation.",
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visit<'ast> for CheckTraitDecl {
|
||||
fn visit_fn_arg(&mut self, input: &'ast FnArg) {
|
||||
if let FnArg::Receiver(_) = input {
|
||||
self.errors.push(Error::new(input.span(), "`self` as argument not supported."))
|
||||
}
|
||||
|
||||
visit::visit_fn_arg(self, input);
|
||||
}
|
||||
|
||||
fn visit_generic_param(&mut self, input: &'ast GenericParam) {
|
||||
match input {
|
||||
GenericParam::Type(ty) if ty.ident == BLOCK_GENERIC_IDENT =>
|
||||
self.errors.push(Error::new(
|
||||
input.span(),
|
||||
"`Block: BlockT` generic parameter will be added automatically by the \
|
||||
`decl_runtime_apis!` macro!",
|
||||
)),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
visit::visit_generic_param(self, input);
|
||||
}
|
||||
|
||||
fn visit_trait_bound(&mut self, input: &'ast TraitBound) {
|
||||
if let Some(last_ident) = input.path.segments.last().map(|v| &v.ident) {
|
||||
if last_ident == "BlockT" || last_ident == BLOCK_GENERIC_IDENT {
|
||||
self.errors.push(Error::new(
|
||||
input.span(),
|
||||
"`Block: BlockT` generic parameter will be added automatically by the \
|
||||
`decl_runtime_apis!` macro! If you try to use a different trait than the \
|
||||
bizinikiwi `Block` trait, please rename it locally.",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
visit::visit_trait_bound(self, input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the trait declarations are in the format we expect.
|
||||
fn check_trait_decls(decls: &[ItemTrait]) -> Result<()> {
|
||||
let mut checker = CheckTraitDecl { errors: Vec::new() };
|
||||
decls.iter().for_each(|decl| checker.check(decl));
|
||||
|
||||
if let Some(err) = checker.errors.pop() {
|
||||
Err(checker.errors.into_iter().fold(err, |mut err, other| {
|
||||
err.combine(other);
|
||||
err
|
||||
}))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The implementation of the `decl_runtime_apis!` macro.
|
||||
pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
// Parse all trait declarations
|
||||
let RuntimeApiDecls { decls: api_decls } = parse_macro_input!(input as RuntimeApiDecls);
|
||||
|
||||
decl_runtime_apis_impl_inner(&api_decls)
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result<TokenStream> {
|
||||
check_trait_decls(api_decls)?;
|
||||
|
||||
let runtime_decls = generate_runtime_decls(api_decls)?;
|
||||
let client_side_decls = generate_client_side_decls(api_decls)?;
|
||||
|
||||
let decl = quote! {
|
||||
#runtime_decls
|
||||
|
||||
#client_side_decls
|
||||
};
|
||||
|
||||
let decl = expander::Expander::new("decl_runtime_apis")
|
||||
.dry(std::env::var("EXPAND_MACROS").is_err())
|
||||
.verbose(true)
|
||||
.write_to_out_dir(decl)
|
||||
.expect("Does not fail because of IO in OUT_DIR; qed");
|
||||
|
||||
Ok(decl)
|
||||
}
|
||||
@@ -0,0 +1,950 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::utils::{
|
||||
extract_api_version, extract_block_type_from_trait_path, extract_impl_trait,
|
||||
extract_parameter_names_types_and_borrows, generate_crate_access,
|
||||
generate_runtime_mod_name_for_trait, prefix_function_with_trait, versioned_trait_name,
|
||||
AllowSelfRefInParameters, ApiVersion, RequireQualifiedTraitPath,
|
||||
};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use syn::{
|
||||
fold::{self, Fold},
|
||||
parse::{Error, Parse, ParseStream, Result},
|
||||
parse_macro_input, parse_quote,
|
||||
spanned::Spanned,
|
||||
visit_mut::{self, VisitMut},
|
||||
Attribute, Ident, ImplItem, ItemImpl, Path, Signature, Type, TypePath,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The structure used for parsing the runtime api implementations.
|
||||
struct RuntimeApiImpls {
|
||||
impls: Vec<ItemImpl>,
|
||||
}
|
||||
|
||||
impl Parse for RuntimeApiImpls {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut impls = Vec::new();
|
||||
|
||||
while !input.is_empty() {
|
||||
impls.push(ItemImpl::parse(input)?);
|
||||
}
|
||||
|
||||
if impls.is_empty() {
|
||||
Err(Error::new(Span::call_site(), "No api implementation given!"))
|
||||
} else {
|
||||
Ok(Self { impls })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the call to the implementation of the requested function.
|
||||
/// The generated code includes decoding of the input arguments and encoding of the output.
|
||||
fn generate_impl_call(
|
||||
signature: &Signature,
|
||||
runtime: &Type,
|
||||
input: &Ident,
|
||||
impl_trait: &Path,
|
||||
api_version: &ApiVersion,
|
||||
) -> Result<TokenStream> {
|
||||
let params =
|
||||
extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?;
|
||||
|
||||
let c = generate_crate_access();
|
||||
let fn_name = &signature.ident;
|
||||
let fn_name_str = fn_name.to_string();
|
||||
let pnames = params.iter().map(|v| &v.0);
|
||||
let pnames2 = params.iter().map(|v| &v.0);
|
||||
let ptypes = params.iter().map(|v| &v.1);
|
||||
let pborrow = params.iter().map(|v| &v.2);
|
||||
|
||||
let decode_params = if params.is_empty() {
|
||||
quote!(
|
||||
if !#input.is_empty() {
|
||||
panic!(
|
||||
"Bad input data provided to {}: expected no parameters, but input buffer is not empty. Nothing bad happened: someone sent an invalid transaction to the node.",
|
||||
#fn_name_str
|
||||
);
|
||||
}
|
||||
)
|
||||
} else {
|
||||
let let_binding = if params.len() == 1 {
|
||||
quote! {
|
||||
let #( #pnames )* : #( #ptypes )*
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let ( #( #pnames ),* ) : ( #( #ptypes ),* )
|
||||
}
|
||||
};
|
||||
|
||||
quote!(
|
||||
#let_binding =
|
||||
match #c::Decode::decode(
|
||||
&mut #input,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(e) => panic!("Bad input data provided to {}: {}. Nothing bad happened: someone sent an invalid transaction to the node.", #fn_name_str, e),
|
||||
};
|
||||
)
|
||||
};
|
||||
|
||||
let fn_calls = if let Some(feature_gated) = &api_version.feature_gated {
|
||||
let pnames = pnames2;
|
||||
let pnames2 = pnames.clone();
|
||||
let pborrow2 = pborrow.clone();
|
||||
|
||||
let feature_name = &feature_gated.0;
|
||||
let impl_trait_fg = extend_with_api_version(impl_trait.clone(), Some(feature_gated.1));
|
||||
let impl_trait = extend_with_api_version(impl_trait.clone(), api_version.custom);
|
||||
|
||||
quote!(
|
||||
#[cfg(feature = #feature_name)]
|
||||
#[allow(deprecated)]
|
||||
let r = <#runtime as #impl_trait_fg>::#fn_name(#( #pborrow #pnames ),*);
|
||||
|
||||
#[cfg(not(feature = #feature_name))]
|
||||
#[allow(deprecated)]
|
||||
let r = <#runtime as #impl_trait>::#fn_name(#( #pborrow2 #pnames2 ),*);
|
||||
|
||||
r
|
||||
)
|
||||
} else {
|
||||
let pnames = pnames2;
|
||||
let impl_trait = extend_with_api_version(impl_trait.clone(), api_version.custom);
|
||||
|
||||
quote!(
|
||||
#[allow(deprecated)]
|
||||
<#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames ),*)
|
||||
)
|
||||
};
|
||||
|
||||
Ok(quote!(
|
||||
#decode_params
|
||||
|
||||
#fn_calls
|
||||
))
|
||||
}
|
||||
|
||||
/// Generate all the implementation calls for the given functions.
|
||||
fn generate_impl_calls(
|
||||
impls: &[ItemImpl],
|
||||
input: &Ident,
|
||||
) -> Result<Vec<(Ident, Ident, TokenStream, Vec<Attribute>)>> {
|
||||
let mut impl_calls = Vec::new();
|
||||
|
||||
for impl_ in impls {
|
||||
let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
|
||||
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
|
||||
let impl_trait = extend_with_runtime_decl_path(impl_trait_path.clone());
|
||||
let impl_trait_ident = &impl_trait_path
|
||||
.segments
|
||||
.last()
|
||||
.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
|
||||
.ident;
|
||||
|
||||
for item in &impl_.items {
|
||||
if let ImplItem::Fn(method) = item {
|
||||
let impl_call = generate_impl_call(
|
||||
&method.sig,
|
||||
&impl_.self_ty,
|
||||
input,
|
||||
&impl_trait,
|
||||
&trait_api_ver,
|
||||
)?;
|
||||
let mut attrs = filter_cfg_and_allow_attrs(&impl_.attrs);
|
||||
|
||||
// Add any `#[cfg(feature = X)]` attributes of the method to result
|
||||
attrs.extend(filter_cfg_and_allow_attrs(&method.attrs));
|
||||
|
||||
impl_calls.push((
|
||||
impl_trait_ident.clone(),
|
||||
method.sig.ident.clone(),
|
||||
impl_call,
|
||||
attrs,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(impl_calls)
|
||||
}
|
||||
|
||||
/// Generate the dispatch function that is used in native to call into the runtime.
|
||||
fn generate_dispatch_function(impls: &[ItemImpl]) -> Result<TokenStream> {
|
||||
let data = Ident::new("_sp_api_input_data_", Span::call_site());
|
||||
let c = generate_crate_access();
|
||||
let impl_calls =
|
||||
generate_impl_calls(impls, &data)?
|
||||
.into_iter()
|
||||
.map(|(trait_, fn_name, impl_, attrs)| {
|
||||
let name = prefix_function_with_trait(&trait_, &fn_name);
|
||||
quote!(
|
||||
#( #attrs )*
|
||||
#name => Some(#c::Encode::encode(&{ #impl_ })),
|
||||
)
|
||||
});
|
||||
|
||||
Ok(quote!(
|
||||
#c::std_enabled! {
|
||||
pub fn dispatch(method: &str, mut #data: &[u8]) -> Option<Vec<u8>> {
|
||||
match method {
|
||||
#( #impl_calls )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
/// Generate the interface functions that are used to call into the runtime in wasm.
|
||||
fn generate_wasm_interface(impls: &[ItemImpl]) -> Result<TokenStream> {
|
||||
let input = Ident::new("input", Span::call_site());
|
||||
let c = generate_crate_access();
|
||||
|
||||
let impl_calls =
|
||||
generate_impl_calls(impls, &input)?
|
||||
.into_iter()
|
||||
.map(|(trait_, fn_name, impl_, attrs)| {
|
||||
let fn_name =
|
||||
Ident::new(&prefix_function_with_trait(&trait_, &fn_name), Span::call_site());
|
||||
|
||||
quote!(
|
||||
#c::std_disabled! {
|
||||
#( #attrs )*
|
||||
#[no_mangle]
|
||||
#[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #c::__private::polkavm_export(abi = #c::__private::polkavm_abi))]
|
||||
pub unsafe extern fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 {
|
||||
let mut #input = if input_len == 0 {
|
||||
&[0u8; 0]
|
||||
} else {
|
||||
unsafe {
|
||||
::core::slice::from_raw_parts(input_data, input_len)
|
||||
}
|
||||
};
|
||||
|
||||
#c::init_runtime_logger();
|
||||
|
||||
let output = (move || { #impl_ })();
|
||||
#c::to_bizinikiwi_wasm_fn_return_value(&output)
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
Ok(quote!( #( #impl_calls )* ))
|
||||
}
|
||||
|
||||
fn generate_runtime_api_base_structures() -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
Ok(quote!(
|
||||
pub struct RuntimeApi {}
|
||||
#crate_::std_enabled! {
|
||||
/// Implements all runtime apis for the client side.
|
||||
pub struct RuntimeApiImpl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block> + 'static> {
|
||||
call: &'static C,
|
||||
transaction_depth: std::cell::RefCell<u16>,
|
||||
changes: std::cell::RefCell<#crate_::OverlayedChanges<#crate_::HashingFor<Block>>>,
|
||||
recorder: std::option::Option<#crate_::ProofRecorder<Block>>,
|
||||
call_context: #crate_::CallContext,
|
||||
extensions: std::cell::RefCell<#crate_::Extensions>,
|
||||
extensions_generated_for: std::cell::RefCell<std::option::Option<Block::Hash>>,
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> #crate_::ApiExt<Block> for
|
||||
RuntimeApiImpl<Block, C>
|
||||
{
|
||||
fn execute_in_transaction<F: FnOnce(&Self) -> #crate_::TransactionOutcome<R>, R>(
|
||||
&self,
|
||||
call: F,
|
||||
) -> R where Self: Sized {
|
||||
self.start_transaction();
|
||||
|
||||
*std::cell::RefCell::borrow_mut(&self.transaction_depth) += 1;
|
||||
let res = call(self);
|
||||
std::cell::RefCell::borrow_mut(&self.transaction_depth)
|
||||
.checked_sub(1)
|
||||
.expect("Transactions are opened and closed together; qed");
|
||||
|
||||
self.commit_or_rollback_transaction(
|
||||
std::matches!(res, #crate_::TransactionOutcome::Commit(_))
|
||||
);
|
||||
|
||||
res.into_inner()
|
||||
}
|
||||
|
||||
fn has_api<A: #crate_::RuntimeApiInfo + ?Sized>(
|
||||
&self,
|
||||
at: <Block as #crate_::BlockT>::Hash,
|
||||
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
|
||||
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
|
||||
.map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, |v| v == A::VERSION))
|
||||
}
|
||||
|
||||
fn has_api_with<A: #crate_::RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
|
||||
&self,
|
||||
at: <Block as #crate_::BlockT>::Hash,
|
||||
pred: P,
|
||||
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
|
||||
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
|
||||
.map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, pred))
|
||||
}
|
||||
|
||||
fn api_version<A: #crate_::RuntimeApiInfo + ?Sized>(
|
||||
&self,
|
||||
at: <Block as #crate_::BlockT>::Hash,
|
||||
) -> std::result::Result<Option<u32>, #crate_::ApiError> where Self: Sized {
|
||||
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
|
||||
.map(|v| #crate_::RuntimeVersion::api_version(&v, &A::ID))
|
||||
}
|
||||
|
||||
fn record_proof(&mut self) {
|
||||
self.recorder = std::option::Option::Some(std::default::Default::default());
|
||||
}
|
||||
|
||||
fn record_proof_with_recorder(&mut self, recorder: #crate_::ProofRecorder<Block>) {
|
||||
self.recorder = std::option::Option::Some(recorder);
|
||||
}
|
||||
|
||||
fn proof_recorder(&self) -> std::option::Option<#crate_::ProofRecorder<Block>> {
|
||||
std::clone::Clone::clone(&self.recorder)
|
||||
}
|
||||
|
||||
fn extract_proof(
|
||||
&mut self,
|
||||
) -> std::option::Option<#crate_::StorageProof> {
|
||||
let recorder = std::option::Option::take(&mut self.recorder);
|
||||
std::option::Option::map(recorder, |recorder| {
|
||||
#crate_::ProofRecorder::<Block>::drain_storage_proof(recorder)
|
||||
})
|
||||
}
|
||||
|
||||
fn into_storage_changes<B: #crate_::StateBackend<#crate_::HashingFor<Block>>>(
|
||||
&self,
|
||||
backend: &B,
|
||||
parent_hash: Block::Hash,
|
||||
) -> ::core::result::Result<
|
||||
#crate_::StorageChanges<Block>,
|
||||
String
|
||||
> where Self: Sized {
|
||||
let state_version = #crate_::CallApiAt::<Block>::runtime_version_at(self.call, std::clone::Clone::clone(&parent_hash))
|
||||
.map(|v| #crate_::RuntimeVersion::state_version(&v))
|
||||
.map_err(|e| format!("Failed to get state version: {}", e))?;
|
||||
|
||||
#crate_::OverlayedChanges::drain_storage_changes(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.changes),
|
||||
backend,
|
||||
state_version,
|
||||
)
|
||||
}
|
||||
|
||||
fn set_call_context(&mut self, call_context: #crate_::CallContext) {
|
||||
self.call_context = call_context;
|
||||
}
|
||||
|
||||
fn register_extension<E: #crate_::Extension>(&mut self, extension: E) {
|
||||
std::cell::RefCell::borrow_mut(&self.extensions).register(extension);
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl<Block: #crate_::BlockT, C> #crate_::ConstructRuntimeApi<Block, C>
|
||||
for RuntimeApi
|
||||
where
|
||||
C: #crate_::CallApiAt<Block> + 'static,
|
||||
{
|
||||
type RuntimeApi = RuntimeApiImpl<Block, C>;
|
||||
|
||||
fn construct_runtime_api<'a>(
|
||||
call: &'a C,
|
||||
) -> #crate_::ApiRef<'a, Self::RuntimeApi> {
|
||||
RuntimeApiImpl {
|
||||
call: unsafe { std::mem::transmute(call) },
|
||||
transaction_depth: 0.into(),
|
||||
changes: std::default::Default::default(),
|
||||
recorder: std::default::Default::default(),
|
||||
call_context: #crate_::CallContext::Offchain,
|
||||
extensions: std::default::Default::default(),
|
||||
extensions_generated_for: std::default::Default::default(),
|
||||
}.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> RuntimeApiImpl<Block, C> {
|
||||
fn commit_or_rollback_transaction(&self, commit: bool) {
|
||||
let proof = "\
|
||||
We only close a transaction when we opened one ourself.
|
||||
Other parts of the runtime that make use of transactions (state-machine)
|
||||
also balance their transactions. The runtime cannot close client initiated
|
||||
transactions; qed";
|
||||
|
||||
let res = if commit {
|
||||
let res = if let Some(recorder) = &self.recorder {
|
||||
#crate_::ProofRecorder::<Block>::commit_transaction(&recorder)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let res2 = #crate_::OverlayedChanges::commit_transaction(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.changes)
|
||||
);
|
||||
|
||||
#crate_::Extensions::commit_transaction(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.extensions),
|
||||
#crate_::TransactionType::Host,
|
||||
);
|
||||
|
||||
// Will panic on an `Err` below, however we should call commit
|
||||
// on the recorder and the changes together.
|
||||
std::result::Result::and(res, std::result::Result::map_err(res2, drop))
|
||||
} else {
|
||||
let res = if let Some(recorder) = &self.recorder {
|
||||
#crate_::ProofRecorder::<Block>::rollback_transaction(&recorder)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let res2 = #crate_::OverlayedChanges::rollback_transaction(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.changes)
|
||||
);
|
||||
|
||||
#crate_::Extensions::rollback_transaction(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.extensions),
|
||||
#crate_::TransactionType::Host,
|
||||
);
|
||||
|
||||
// Will panic on an `Err` below, however we should call commit
|
||||
// on the recorder and the changes together.
|
||||
std::result::Result::and(res, std::result::Result::map_err(res2, drop))
|
||||
};
|
||||
|
||||
std::result::Result::expect(res, proof);
|
||||
}
|
||||
|
||||
fn start_transaction(&self) {
|
||||
#crate_::OverlayedChanges::start_transaction(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.changes)
|
||||
);
|
||||
if let Some(recorder) = &self.recorder {
|
||||
#crate_::ProofRecorder::<Block>::start_transaction(&recorder);
|
||||
}
|
||||
|
||||
#crate_::Extensions::start_transaction(
|
||||
&mut std::cell::RefCell::borrow_mut(&self.extensions),
|
||||
#crate_::TransactionType::Host,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
/// Extend the given trait path with module that contains the declaration of the trait for the
|
||||
/// runtime.
|
||||
fn extend_with_runtime_decl_path(mut trait_: Path) -> Path {
|
||||
let runtime = {
|
||||
let trait_name = &trait_
|
||||
.segments
|
||||
.last()
|
||||
.as_ref()
|
||||
.expect("Trait path should always contain at least one item; qed")
|
||||
.ident;
|
||||
|
||||
generate_runtime_mod_name_for_trait(trait_name)
|
||||
};
|
||||
|
||||
let pos = trait_.segments.len() - 1;
|
||||
trait_.segments.insert(pos, runtime.into());
|
||||
trait_
|
||||
}
|
||||
|
||||
fn extend_with_api_version(mut trait_: Path, version: Option<u32>) -> Path {
|
||||
let version = if let Some(v) = version {
|
||||
v
|
||||
} else {
|
||||
// nothing to do
|
||||
return trait_;
|
||||
};
|
||||
|
||||
let trait_name = &mut trait_
|
||||
.segments
|
||||
.last_mut()
|
||||
.expect("Trait path should always contain at least one item; qed")
|
||||
.ident;
|
||||
*trait_name = versioned_trait_name(trait_name, version);
|
||||
|
||||
trait_
|
||||
}
|
||||
|
||||
/// Adds a feature guard to `attributes`.
|
||||
///
|
||||
/// Depending on `enable`, the feature guard either enables ('feature = "something"`) or disables
|
||||
/// (`not(feature = "something")`).
|
||||
fn add_feature_guard(attrs: &mut Vec<Attribute>, feature_name: &str, enable: bool) {
|
||||
let attr = match enable {
|
||||
true => parse_quote!(#[cfg(feature = #feature_name)]),
|
||||
false => parse_quote!(#[cfg(not(feature = #feature_name))]),
|
||||
};
|
||||
attrs.push(attr);
|
||||
}
|
||||
|
||||
/// Generates the implementations of the apis for the runtime.
|
||||
fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
|
||||
let mut impls_prepared = Vec::new();
|
||||
|
||||
// We put `runtime` before each trait to get the trait that is intended for the runtime and
|
||||
// we put the `RuntimeBlock` as first argument for the trait generics.
|
||||
for impl_ in impls.iter() {
|
||||
let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
|
||||
|
||||
let mut impl_ = impl_.clone();
|
||||
impl_.attrs = filter_cfg_and_allow_attrs(&impl_.attrs);
|
||||
|
||||
let trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
|
||||
let trait_ = extend_with_runtime_decl_path(trait_);
|
||||
// If the trait api contains feature gated version - there are staging methods in it. Handle
|
||||
// them explicitly here by adding staging implementation with `#cfg(feature = ...)` and
|
||||
// stable implementation with `#[cfg(not(feature = ...))]`.
|
||||
if let Some(feature_gated) = trait_api_ver.feature_gated {
|
||||
let mut feature_gated_impl = impl_.clone();
|
||||
add_feature_guard(&mut feature_gated_impl.attrs, &feature_gated.0, true);
|
||||
feature_gated_impl.trait_.as_mut().unwrap().1 =
|
||||
extend_with_api_version(trait_.clone(), Some(feature_gated.1));
|
||||
|
||||
impls_prepared.push(feature_gated_impl);
|
||||
|
||||
// Finally add `#[cfg(not(feature = ...))]` for the stable implementation (which is
|
||||
// generated outside this if).
|
||||
add_feature_guard(&mut impl_.attrs, &feature_gated.0, false);
|
||||
}
|
||||
|
||||
// Generate stable trait implementation.
|
||||
let trait_ = extend_with_api_version(trait_, trait_api_ver.custom);
|
||||
impl_.trait_.as_mut().unwrap().1 = trait_;
|
||||
impls_prepared.push(impl_);
|
||||
}
|
||||
|
||||
Ok(quote!( #( #impls_prepared )* ))
|
||||
}
|
||||
|
||||
/// Auxiliary data structure that is used to convert `impl Api for Runtime` to
|
||||
/// `impl Api for RuntimeApi`.
|
||||
/// This requires us to replace the runtime `Block` with the node `Block`,
|
||||
/// `impl Api for Runtime` with `impl Api for RuntimeApi` and replace the method implementations
|
||||
/// with code that calls into the runtime.
|
||||
struct ApiRuntimeImplToApiRuntimeApiImpl<'a> {
|
||||
runtime_block: &'a TypePath,
|
||||
}
|
||||
|
||||
impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
|
||||
/// Process the given item implementation.
|
||||
fn process(mut self, input: ItemImpl) -> ItemImpl {
|
||||
let mut input = self.fold_item_impl(input);
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
// Delete all functions, because all of them are default implemented by
|
||||
// `decl_runtime_apis!`. We only need to implement the `__runtime_api_internal_call_api_at`
|
||||
// function.
|
||||
input.items.clear();
|
||||
input.items.push(parse_quote! {
|
||||
fn __runtime_api_internal_call_api_at(
|
||||
&self,
|
||||
at: <__SrApiBlock__ as #crate_::BlockT>::Hash,
|
||||
params: std::vec::Vec<u8>,
|
||||
fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
|
||||
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
|
||||
// If we are not already in a transaction, we should create a new transaction
|
||||
// and then commit/roll it back at the end!
|
||||
let transaction_depth = *std::cell::RefCell::borrow(&self.transaction_depth);
|
||||
|
||||
if transaction_depth == 0 {
|
||||
self.start_transaction();
|
||||
}
|
||||
|
||||
let res = (|| {
|
||||
let version = #crate_::CallApiAt::<__SrApiBlock__>::runtime_version_at(
|
||||
self.call,
|
||||
at,
|
||||
)?;
|
||||
|
||||
match &mut *std::cell::RefCell::borrow_mut(&self.extensions_generated_for) {
|
||||
Some(generated_for) => {
|
||||
if *generated_for != at {
|
||||
return std::result::Result::Err(
|
||||
#crate_::ApiError::UsingSameInstanceForDifferentBlocks
|
||||
)
|
||||
}
|
||||
},
|
||||
generated_for @ None => {
|
||||
#crate_::CallApiAt::<__SrApiBlock__>::initialize_extensions(
|
||||
self.call,
|
||||
at,
|
||||
&mut std::cell::RefCell::borrow_mut(&self.extensions),
|
||||
)?;
|
||||
|
||||
*generated_for = Some(at);
|
||||
}
|
||||
}
|
||||
|
||||
let params = #crate_::CallApiAtParams {
|
||||
at,
|
||||
function: (*fn_name)(version),
|
||||
arguments: params,
|
||||
overlayed_changes: &self.changes,
|
||||
call_context: self.call_context,
|
||||
recorder: &self.recorder,
|
||||
extensions: &self.extensions,
|
||||
};
|
||||
|
||||
#crate_::CallApiAt::<__SrApiBlock__>::call_api_at(
|
||||
self.call,
|
||||
params,
|
||||
)
|
||||
})();
|
||||
|
||||
if transaction_depth == 0 {
|
||||
self.commit_or_rollback_transaction(std::result::Result::is_ok(&res));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
});
|
||||
|
||||
input
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
|
||||
fn fold_type_path(&mut self, input: TypePath) -> TypePath {
|
||||
let new_ty_path =
|
||||
if input == *self.runtime_block { parse_quote!(__SrApiBlock__) } else { input };
|
||||
|
||||
fold::fold_type_path(self, new_ty_path)
|
||||
}
|
||||
|
||||
fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
// Implement the trait for the `RuntimeApiImpl`
|
||||
input.self_ty =
|
||||
Box::new(parse_quote!( RuntimeApiImpl<__SrApiBlock__, RuntimeApiImplCall> ));
|
||||
|
||||
input.generics.params.push(parse_quote!(
|
||||
__SrApiBlock__: #crate_::BlockT
|
||||
));
|
||||
|
||||
input
|
||||
.generics
|
||||
.params
|
||||
.push(parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SrApiBlock__> + 'static ));
|
||||
|
||||
let where_clause = input.generics.make_where_clause();
|
||||
|
||||
where_clause.predicates.push(parse_quote! {
|
||||
RuntimeApiImplCall::StateBackend:
|
||||
#crate_::StateBackend<#crate_::HashingFor<__SrApiBlock__>>
|
||||
});
|
||||
|
||||
where_clause.predicates.push(parse_quote! { &'static RuntimeApiImplCall: Send });
|
||||
|
||||
input.attrs = filter_cfg_and_allow_attrs(&input.attrs);
|
||||
|
||||
fold::fold_item_impl(self, input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the implementations of the runtime apis for the `RuntimeApi` type.
|
||||
fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result<TokenStream> {
|
||||
let mut result = Vec::with_capacity(impls.len());
|
||||
|
||||
for impl_ in impls {
|
||||
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
|
||||
let runtime_block = extract_block_type_from_trait_path(impl_trait_path)?;
|
||||
let mut runtime_mod_path = extend_with_runtime_decl_path(impl_trait_path.clone());
|
||||
// remove the trait to get just the module path
|
||||
runtime_mod_path.segments.pop();
|
||||
|
||||
let mut processed_impl =
|
||||
ApiRuntimeImplToApiRuntimeApiImpl { runtime_block }.process(impl_.clone());
|
||||
|
||||
processed_impl.attrs.push(parse_quote!(#[automatically_derived]));
|
||||
|
||||
result.push(processed_impl);
|
||||
}
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
Ok(quote!( #crate_::std_enabled! { #( #result )* } ))
|
||||
}
|
||||
|
||||
fn populate_runtime_api_versions(
|
||||
result: &mut Vec<TokenStream>,
|
||||
sections: &mut Vec<TokenStream>,
|
||||
attrs: Vec<Attribute>,
|
||||
id: Path,
|
||||
version: TokenStream,
|
||||
crate_access: &TokenStream,
|
||||
) {
|
||||
result.push(quote!(
|
||||
#( #attrs )*
|
||||
(#id, #version)
|
||||
));
|
||||
|
||||
sections.push(quote!(
|
||||
#crate_access::std_disabled! {
|
||||
#( #attrs )*
|
||||
const _: () = {
|
||||
// All sections with the same name are going to be merged by concatenation.
|
||||
#[link_section = "runtime_apis"]
|
||||
static SECTION_CONTENTS: [u8; 12] = #crate_access::serialize_runtime_api_info(#id, #version);
|
||||
};
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
/// Generates `RUNTIME_API_VERSIONS` that holds all version information about the implemented
|
||||
/// runtime apis.
|
||||
fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
|
||||
let mut result = Vec::<TokenStream>::with_capacity(impls.len());
|
||||
let mut sections = Vec::<TokenStream>::with_capacity(impls.len());
|
||||
let mut processed_traits = HashMap::new();
|
||||
|
||||
let c = generate_crate_access();
|
||||
|
||||
for impl_ in impls {
|
||||
let versions = extract_api_version(&impl_.attrs, impl_.span())?;
|
||||
let api_ver = versions.custom.map(|a| a as u32);
|
||||
|
||||
let mut path = extend_with_runtime_decl_path(
|
||||
extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?.clone(),
|
||||
);
|
||||
// Remove the trait
|
||||
let trait_ = path
|
||||
.segments
|
||||
.pop()
|
||||
.expect("extract_impl_trait already checks that this is valid; qed")
|
||||
.into_value()
|
||||
.ident;
|
||||
|
||||
let span = trait_.span();
|
||||
if let Some(other_span) = processed_traits.insert(trait_, span) {
|
||||
let mut error = Error::new(
|
||||
span,
|
||||
"Two traits with the same name detected! \
|
||||
The trait name is used to generate its ID. \
|
||||
Please rename one trait at the declaration!",
|
||||
);
|
||||
|
||||
error.combine(Error::new(other_span, "First trait implementation."));
|
||||
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let id: Path = parse_quote!( #path ID );
|
||||
let mut attrs = filter_cfg_and_allow_attrs(&impl_.attrs);
|
||||
|
||||
// Handle API versioning
|
||||
// If feature gated version is set - handle it first
|
||||
if let Some(feature_gated) = versions.feature_gated {
|
||||
let feature_gated_version = feature_gated.1 as u32;
|
||||
// the attributes for the feature gated staging api
|
||||
let mut feature_gated_attrs = attrs.clone();
|
||||
add_feature_guard(&mut feature_gated_attrs, &feature_gated.0, true);
|
||||
populate_runtime_api_versions(
|
||||
&mut result,
|
||||
&mut sections,
|
||||
feature_gated_attrs,
|
||||
id.clone(),
|
||||
quote!( #feature_gated_version ),
|
||||
&c,
|
||||
);
|
||||
|
||||
// Add `#[cfg(not(feature ...))]` to the initial attributes. If the staging feature flag
|
||||
// is not set we want to set the stable api version
|
||||
add_feature_guard(&mut attrs, &feature_gated.0, false);
|
||||
}
|
||||
|
||||
// Now add the stable api version to the versions list. If the api has got staging functions
|
||||
// there might be a `#[cfg(not(feature ...))]` attribute attached to the stable version.
|
||||
let base_api_version = quote!( #path VERSION );
|
||||
let api_ver = api_ver.map(|a| quote!( #a )).unwrap_or_else(|| base_api_version);
|
||||
populate_runtime_api_versions(&mut result, &mut sections, attrs, id, api_ver, &c);
|
||||
}
|
||||
|
||||
Ok(quote!(
|
||||
pub const RUNTIME_API_VERSIONS: #c::ApisVec = #c::create_apis_vec!([ #( #result ),* ]);
|
||||
|
||||
#( #sections )*
|
||||
))
|
||||
}
|
||||
|
||||
/// replaces `Self` with explicit `ItemImpl.self_ty`.
|
||||
struct ReplaceSelfImpl {
|
||||
self_ty: Box<Type>,
|
||||
}
|
||||
|
||||
impl ReplaceSelfImpl {
|
||||
/// Replace `Self` with `ItemImpl.self_ty`
|
||||
fn replace(&mut self, trait_: &mut ItemImpl) {
|
||||
visit_mut::visit_item_impl_mut(self, trait_)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for ReplaceSelfImpl {
|
||||
fn visit_type_mut(&mut self, ty: &mut syn::Type) {
|
||||
match ty {
|
||||
Type::Path(p) if p.path.is_ident("Self") => {
|
||||
*ty = *self.self_ty.clone();
|
||||
},
|
||||
ty => syn::visit_mut::visit_type_mut(self, ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename `Self` to `ItemImpl.self_ty` in all items.
|
||||
fn rename_self_in_trait_impls(impls: &mut [ItemImpl]) {
|
||||
impls.iter_mut().for_each(|i| {
|
||||
let mut checker = ReplaceSelfImpl { self_ty: i.self_ty.clone() };
|
||||
checker.replace(i);
|
||||
});
|
||||
}
|
||||
|
||||
/// The implementation of the `impl_runtime_apis!` macro.
|
||||
pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
// Parse all impl blocks
|
||||
let RuntimeApiImpls { impls: mut api_impls } = parse_macro_input!(input as RuntimeApiImpls);
|
||||
|
||||
impl_runtime_apis_impl_inner(&mut api_impls)
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn impl_runtime_apis_impl_inner(api_impls: &mut [ItemImpl]) -> Result<TokenStream> {
|
||||
rename_self_in_trait_impls(api_impls);
|
||||
|
||||
let dispatch_impl = generate_dispatch_function(api_impls)?;
|
||||
let api_impls_for_runtime = generate_api_impl_for_runtime(api_impls)?;
|
||||
let base_runtime_api = generate_runtime_api_base_structures()?;
|
||||
let runtime_api_versions = generate_runtime_api_versions(api_impls)?;
|
||||
let wasm_interface = generate_wasm_interface(api_impls)?;
|
||||
let api_impls_for_runtime_api = generate_api_impl_for_runtime_api(api_impls)?;
|
||||
|
||||
let runtime_metadata = crate::runtime_metadata::generate_impl_runtime_metadata(api_impls)?;
|
||||
|
||||
let impl_ = quote!(
|
||||
#base_runtime_api
|
||||
|
||||
#api_impls_for_runtime
|
||||
|
||||
#api_impls_for_runtime_api
|
||||
|
||||
#runtime_api_versions
|
||||
|
||||
#runtime_metadata
|
||||
|
||||
pub mod api {
|
||||
use super::*;
|
||||
|
||||
#dispatch_impl
|
||||
|
||||
#wasm_interface
|
||||
}
|
||||
);
|
||||
|
||||
let impl_ = expander::Expander::new("impl_runtime_apis")
|
||||
.dry(std::env::var("EXPAND_MACROS").is_err())
|
||||
.verbose(true)
|
||||
.write_to_out_dir(impl_)
|
||||
.expect("Does not fail because of IO in OUT_DIR; qed");
|
||||
|
||||
Ok(impl_)
|
||||
}
|
||||
|
||||
// Filters all attributes except the cfg and allow ones.
|
||||
fn filter_cfg_and_allow_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|a| a.path().is_ident("cfg") || a.path().is_ident("allow"))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn filter_non_cfg_attributes() {
|
||||
let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]);
|
||||
let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]);
|
||||
let allow: Attribute = parse_quote!(#[allow(non_camel_case_types)]);
|
||||
|
||||
let attrs = vec![
|
||||
cfg_std.clone(),
|
||||
parse_quote!(#[derive(Debug)]),
|
||||
parse_quote!(#[test]),
|
||||
cfg_benchmarks.clone(),
|
||||
parse_quote!(#[allow(non_camel_case_types)]),
|
||||
];
|
||||
|
||||
let filtered = filter_cfg_and_allow_attrs(&attrs);
|
||||
assert_eq!(filtered.len(), 3);
|
||||
assert_eq!(cfg_std, filtered[0]);
|
||||
assert_eq!(cfg_benchmarks, filtered[1]);
|
||||
assert_eq!(allow, filtered[2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn impl_trait_rename_self_param() {
|
||||
let code = quote::quote! {
|
||||
impl client::Core<Block> for Runtime {
|
||||
fn initialize_block(header: &HeaderFor<Self>) -> Output<Self> {
|
||||
let _: HeaderFor<Self> = header.clone();
|
||||
example_fn::<Self>(header)
|
||||
}
|
||||
}
|
||||
};
|
||||
let expected = quote::quote! {
|
||||
impl client::Core<Block> for Runtime {
|
||||
fn initialize_block(header: &HeaderFor<Runtime>) -> Output<Runtime> {
|
||||
let _: HeaderFor<Runtime> = header.clone();
|
||||
example_fn::<Runtime>(header)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Parse the items
|
||||
let RuntimeApiImpls { impls: mut api_impls } =
|
||||
syn::parse2::<RuntimeApiImpls>(code).unwrap();
|
||||
|
||||
// Run the renamer which is being run first in the `impl_runtime_apis!` macro.
|
||||
rename_self_in_trait_impls(&mut api_impls);
|
||||
let result: TokenStream = quote::quote! { #(#api_impls)* };
|
||||
|
||||
assert_eq!(result.to_string(), expected.to_string());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Macros for declaring and implementing runtime apis.
|
||||
|
||||
#![recursion_limit = "512"]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod common;
|
||||
mod decl_runtime_apis;
|
||||
mod impl_runtime_apis;
|
||||
mod mock_impl_runtime_apis;
|
||||
mod runtime_metadata;
|
||||
mod utils;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_runtime_apis(input: TokenStream) -> TokenStream {
|
||||
impl_runtime_apis::impl_runtime_apis_impl(input)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn mock_impl_runtime_apis(input: TokenStream) -> TokenStream {
|
||||
mock_impl_runtime_apis::mock_impl_runtime_apis_impl(input)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn decl_runtime_apis(input: TokenStream) -> TokenStream {
|
||||
decl_runtime_apis::decl_runtime_apis_impl(input)
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::utils::{
|
||||
extract_block_type_from_trait_path, extract_impl_trait,
|
||||
extract_parameter_names_types_and_borrows, generate_crate_access, return_type_extract_type,
|
||||
AllowSelfRefInParameters, RequireQualifiedTraitPath,
|
||||
};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use quote::{quote, quote_spanned};
|
||||
|
||||
use syn::{
|
||||
fold::{self, Fold},
|
||||
parse::{Error, Parse, ParseStream, Result},
|
||||
parse_macro_input, parse_quote,
|
||||
spanned::Spanned,
|
||||
Attribute, ItemImpl, Pat, Type, TypePath,
|
||||
};
|
||||
|
||||
/// The `advanced` attribute.
|
||||
///
|
||||
/// If this attribute is given to a function, the function gets access to the `Hash` as first
|
||||
/// parameter and needs to return a `Result` with the appropriate error type.
|
||||
const ADVANCED_ATTRIBUTE: &str = "advanced";
|
||||
|
||||
/// The structure used for parsing the runtime api implementations.
|
||||
struct RuntimeApiImpls {
|
||||
impls: Vec<ItemImpl>,
|
||||
}
|
||||
|
||||
impl Parse for RuntimeApiImpls {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut impls = Vec::new();
|
||||
|
||||
while !input.is_empty() {
|
||||
impls.push(ItemImpl::parse(input)?);
|
||||
}
|
||||
|
||||
if impls.is_empty() {
|
||||
Err(Error::new(Span::call_site(), "No api implementation given!"))
|
||||
} else {
|
||||
Ok(Self { impls })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the `ApiExt` trait and the `Core` runtime api.
|
||||
fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result<TokenStream> {
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
Ok(quote!(
|
||||
impl #crate_::ApiExt<#block_type> for #self_ty {
|
||||
fn execute_in_transaction<F: FnOnce(&Self) -> #crate_::TransactionOutcome<R>, R>(
|
||||
&self,
|
||||
call: F,
|
||||
) -> R where Self: Sized {
|
||||
call(self).into_inner()
|
||||
}
|
||||
|
||||
fn has_api<A: #crate_::RuntimeApiInfo + ?Sized>(
|
||||
&self,
|
||||
_: <Block as #crate_::BlockT>::Hash,
|
||||
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn has_api_with<A: #crate_::RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
|
||||
&self,
|
||||
_: <Block as #crate_::BlockT>::Hash,
|
||||
pred: P,
|
||||
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
|
||||
Ok(pred(A::VERSION))
|
||||
}
|
||||
|
||||
fn api_version<A: #crate_::RuntimeApiInfo + ?Sized>(
|
||||
&self,
|
||||
_: <Block as #crate_::BlockT>::Hash,
|
||||
) -> std::result::Result<Option<u32>, #crate_::ApiError> where Self: Sized {
|
||||
Ok(Some(A::VERSION))
|
||||
}
|
||||
|
||||
fn record_proof(&mut self) {
|
||||
unimplemented!("`record_proof` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn record_proof_with_recorder(&mut self, _: #crate_::ProofRecorder<#block_type>) {
|
||||
unimplemented!("`record_proof_with_recorder` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn extract_proof(
|
||||
&mut self,
|
||||
) -> Option<#crate_::StorageProof> {
|
||||
unimplemented!("`extract_proof` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn proof_recorder(&self) -> Option<#crate_::ProofRecorder<#block_type>> {
|
||||
unimplemented!("`proof_recorder` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn into_storage_changes<B: #crate_::StateBackend<#crate_::HashingFor<#block_type>>>(
|
||||
&self,
|
||||
_: &B,
|
||||
_: <#block_type as #crate_::BlockT>::Hash,
|
||||
) -> std::result::Result<
|
||||
#crate_::StorageChanges<#block_type>,
|
||||
String
|
||||
> where Self: Sized {
|
||||
unimplemented!("`into_storage_changes` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn set_call_context(&mut self, _: #crate_::CallContext) {
|
||||
unimplemented!("`set_call_context` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn register_extension<E: #crate_::Extension>(&mut self, _: E) {
|
||||
unimplemented!("`register_extension` not implemented for runtime api mocks")
|
||||
}
|
||||
}
|
||||
|
||||
impl #crate_::Core<#block_type> for #self_ty {
|
||||
fn __runtime_api_internal_call_api_at(
|
||||
&self,
|
||||
_: <#block_type as #crate_::BlockT>::Hash,
|
||||
_: std::vec::Vec<u8>,
|
||||
_: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
|
||||
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
|
||||
unimplemented!("`__runtime_api_internal_call_api_at` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn version(
|
||||
&self,
|
||||
_: <#block_type as #crate_::BlockT>::Hash,
|
||||
) -> std::result::Result<#crate_::RuntimeVersion, #crate_::ApiError> {
|
||||
unimplemented!("`Core::version` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn execute_block(
|
||||
&self,
|
||||
_: <#block_type as #crate_::BlockT>::Hash,
|
||||
_: <#block_type as #crate_::BlockT>::LazyBlock,
|
||||
) -> std::result::Result<(), #crate_::ApiError> {
|
||||
unimplemented!("`Core::execute_block` not implemented for runtime api mocks")
|
||||
}
|
||||
|
||||
fn initialize_block(
|
||||
&self,
|
||||
_: <#block_type as #crate_::BlockT>::Hash,
|
||||
_: &<#block_type as #crate_::BlockT>::Header,
|
||||
) -> std::result::Result<#crate_::__private::ExtrinsicInclusionMode, #crate_::ApiError> {
|
||||
unimplemented!("`Core::initialize_block` not implemented for runtime api mocks")
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns if the advanced attribute is present in the given `attributes`.
|
||||
///
|
||||
/// If the attribute was found, it will be automatically removed from the vec.
|
||||
fn has_advanced_attribute(attributes: &mut Vec<Attribute>) -> bool {
|
||||
let mut found = false;
|
||||
attributes.retain(|attr| {
|
||||
if attr.path().is_ident(ADVANCED_ATTRIBUTE) {
|
||||
found = true;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
found
|
||||
}
|
||||
|
||||
/// Get the name and type of the `at` parameter that is passed to a runtime api function.
|
||||
///
|
||||
/// If `is_advanced` is `false`, the name is `_`.
|
||||
fn get_at_param_name(
|
||||
is_advanced: bool,
|
||||
param_names: &mut Vec<Pat>,
|
||||
param_types_and_borrows: &mut Vec<(TokenStream, bool)>,
|
||||
function_span: Span,
|
||||
default_hash_type: &TokenStream,
|
||||
) -> Result<(TokenStream, TokenStream)> {
|
||||
if is_advanced {
|
||||
if param_names.is_empty() {
|
||||
return Err(Error::new(
|
||||
function_span,
|
||||
format!(
|
||||
"If using the `{}` attribute, it is required that the function \
|
||||
takes at least one argument, the `Hash`.",
|
||||
ADVANCED_ATTRIBUTE,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// `param_names` and `param_types` have the same length, so if `param_names` is not empty
|
||||
// `param_types` can not be empty as well.
|
||||
let ptype_and_borrows = param_types_and_borrows.remove(0);
|
||||
let span = ptype_and_borrows.1.span();
|
||||
if ptype_and_borrows.1 {
|
||||
return Err(Error::new(span, "`Hash` needs to be taken by value and not by reference!"));
|
||||
}
|
||||
|
||||
let name = param_names.remove(0);
|
||||
Ok((quote!( #name ), ptype_and_borrows.0))
|
||||
} else {
|
||||
Ok((quote!(_), default_hash_type.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary structure to fold a runtime api trait implementation into the expected format.
|
||||
///
|
||||
/// This renames the methods, changes the method parameters and extracts the error type.
|
||||
struct FoldRuntimeApiImpl<'a> {
|
||||
/// The block type that is being used.
|
||||
block_type: &'a TypePath,
|
||||
}
|
||||
|
||||
impl<'a> FoldRuntimeApiImpl<'a> {
|
||||
/// Process the given [`syn::ItemImpl`].
|
||||
fn process(mut self, impl_item: syn::ItemImpl) -> syn::ItemImpl {
|
||||
let mut impl_item = self.fold_item_impl(impl_item);
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
let block_type = self.block_type;
|
||||
|
||||
impl_item.items.push(parse_quote! {
|
||||
fn __runtime_api_internal_call_api_at(
|
||||
&self,
|
||||
_: <#block_type as #crate_::BlockT>::Hash,
|
||||
_: std::vec::Vec<u8>,
|
||||
_: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
|
||||
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
|
||||
unimplemented!(
|
||||
"`__runtime_api_internal_call_api_at` not implemented for runtime api mocks. \
|
||||
Calling deprecated methods is not supported by mocked runtime api."
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
impl_item
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Fold for FoldRuntimeApiImpl<'a> {
|
||||
fn fold_impl_item_fn(&mut self, mut input: syn::ImplItemFn) -> syn::ImplItemFn {
|
||||
let block = {
|
||||
let crate_ = generate_crate_access();
|
||||
let is_advanced = has_advanced_attribute(&mut input.attrs);
|
||||
let mut errors = Vec::new();
|
||||
|
||||
let (mut param_names, mut param_types_and_borrows) =
|
||||
match extract_parameter_names_types_and_borrows(
|
||||
&input.sig,
|
||||
AllowSelfRefInParameters::YesButIgnore,
|
||||
) {
|
||||
Ok(res) => (
|
||||
res.iter().map(|v| v.0.clone()).collect::<Vec<_>>(),
|
||||
res.iter()
|
||||
.map(|v| {
|
||||
let ty = &v.1;
|
||||
let borrow = &v.2;
|
||||
(quote_spanned!(ty.span() => #borrow #ty ), v.2.is_some())
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
Err(e) => {
|
||||
errors.push(e.to_compile_error());
|
||||
|
||||
(Default::default(), Default::default())
|
||||
},
|
||||
};
|
||||
|
||||
let block_type = &self.block_type;
|
||||
let hash_type = quote!( <#block_type as #crate_::BlockT>::Hash );
|
||||
|
||||
let (at_param_name, hash_type) = match get_at_param_name(
|
||||
is_advanced,
|
||||
&mut param_names,
|
||||
&mut param_types_and_borrows,
|
||||
input.span(),
|
||||
&hash_type,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
errors.push(e.to_compile_error());
|
||||
(quote!(_), hash_type)
|
||||
},
|
||||
};
|
||||
|
||||
let param_types = param_types_and_borrows.iter().map(|v| &v.0);
|
||||
// Rewrite the input parameters.
|
||||
input.sig.inputs = parse_quote! {
|
||||
&self,
|
||||
#at_param_name: #hash_type,
|
||||
#( #param_names: #param_types ),*
|
||||
};
|
||||
|
||||
// When using advanced, the user needs to declare the correct return type on its own,
|
||||
// otherwise do it for the user.
|
||||
if !is_advanced {
|
||||
let ret_type = return_type_extract_type(&input.sig.output);
|
||||
|
||||
// Generate the correct return type.
|
||||
input.sig.output = parse_quote!(
|
||||
-> std::result::Result<#ret_type, #crate_::ApiError>
|
||||
);
|
||||
}
|
||||
|
||||
let orig_block = input.block.clone();
|
||||
|
||||
let construct_return_value = if is_advanced {
|
||||
quote!( (move || #orig_block)() )
|
||||
} else {
|
||||
quote! {
|
||||
let __fn_implementation__ = move || #orig_block;
|
||||
|
||||
Ok(__fn_implementation__())
|
||||
}
|
||||
};
|
||||
|
||||
// Generate the new method implementation that calls into the runtime.
|
||||
parse_quote!(
|
||||
{
|
||||
// Get the error to the user (if we have one).
|
||||
#( #errors )*
|
||||
|
||||
#construct_return_value
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
let mut input = fold::fold_impl_item_fn(self, input);
|
||||
// We need to set the block, after we modified the rest of the ast, otherwise we would
|
||||
// modify our generated block as well.
|
||||
input.block = block;
|
||||
input
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of [`generate_runtime_api_impls`].
|
||||
struct GeneratedRuntimeApiImpls {
|
||||
/// All the runtime api implementations.
|
||||
impls: TokenStream,
|
||||
/// The block type that is being used by the runtime apis.
|
||||
block_type: TypePath,
|
||||
/// The type the traits are implemented for.
|
||||
self_ty: Type,
|
||||
}
|
||||
|
||||
/// Generate the runtime api implementations from the given trait implementations.
|
||||
///
|
||||
/// This folds the method names, changes the method parameters, method return type,
|
||||
/// extracts the error type, self type and the block type.
|
||||
fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result<GeneratedRuntimeApiImpls> {
|
||||
let mut result = Vec::with_capacity(impls.len());
|
||||
let mut global_block_type: Option<TypePath> = None;
|
||||
let mut self_ty: Option<Box<Type>> = None;
|
||||
|
||||
for impl_ in impls {
|
||||
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::No)?;
|
||||
let block_type = extract_block_type_from_trait_path(impl_trait_path)?;
|
||||
|
||||
self_ty = match self_ty.take() {
|
||||
Some(self_ty) =>
|
||||
if self_ty == impl_.self_ty {
|
||||
Some(self_ty)
|
||||
} else {
|
||||
let mut error = Error::new(
|
||||
impl_.self_ty.span(),
|
||||
"Self type should not change between runtime apis",
|
||||
);
|
||||
|
||||
error.combine(Error::new(self_ty.span(), "First self type found here"));
|
||||
|
||||
return Err(error);
|
||||
},
|
||||
None => Some(impl_.self_ty.clone()),
|
||||
};
|
||||
|
||||
global_block_type = match global_block_type.take() {
|
||||
Some(global_block_type) =>
|
||||
if global_block_type == *block_type {
|
||||
Some(global_block_type)
|
||||
} else {
|
||||
let mut error = Error::new(
|
||||
block_type.span(),
|
||||
"Block type should be the same between all runtime apis.",
|
||||
);
|
||||
|
||||
error.combine(Error::new(
|
||||
global_block_type.span(),
|
||||
"First block type found here",
|
||||
));
|
||||
|
||||
return Err(error);
|
||||
},
|
||||
None => Some(block_type.clone()),
|
||||
};
|
||||
|
||||
result.push(FoldRuntimeApiImpl { block_type }.process(impl_.clone()));
|
||||
}
|
||||
|
||||
Ok(GeneratedRuntimeApiImpls {
|
||||
impls: quote!( #( #result )* ),
|
||||
block_type: global_block_type.expect("There is a least one runtime api; qed"),
|
||||
self_ty: *self_ty.expect("There is at least one runtime api; qed"),
|
||||
})
|
||||
}
|
||||
|
||||
/// The implementation of the `mock_impl_runtime_apis!` macro.
|
||||
pub fn mock_impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
// Parse all impl blocks
|
||||
let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls);
|
||||
|
||||
mock_impl_runtime_apis_impl_inner(&api_impls)
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
fn mock_impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result<TokenStream> {
|
||||
let GeneratedRuntimeApiImpls { impls, block_type, self_ty } =
|
||||
generate_runtime_api_impls(api_impls)?;
|
||||
let api_traits = implement_common_api_traits(block_type, self_ty)?;
|
||||
|
||||
Ok(quote!(
|
||||
#impls
|
||||
|
||||
#api_traits
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{parse_quote, spanned::Spanned, ItemImpl, ItemTrait, Result};
|
||||
|
||||
use crate::utils::{
|
||||
extract_api_version, extract_impl_trait, filter_cfg_attributes, generate_crate_access,
|
||||
generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath,
|
||||
};
|
||||
|
||||
/// Get the type parameter argument without lifetime or mutability
|
||||
/// of a runtime metadata function.
|
||||
///
|
||||
/// In the following example, both the `AccountId` and `Nonce` generic
|
||||
/// type parameters must implement `scale_info::TypeInfo` because they
|
||||
/// are added into the metadata using `scale_info::meta_type`.
|
||||
///
|
||||
/// ```ignore
|
||||
/// trait ExampleAccountNonceApi<AccountId, Nonce> {
|
||||
/// fn account_nonce<'a>(account: &'a AccountId) -> Nonce;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead of returning `&'a AccountId` for the first parameter, this function
|
||||
/// returns `AccountId` to place bounds around it.
|
||||
fn get_type_param(ty: &syn::Type) -> syn::Type {
|
||||
// Remove the lifetime and mutability of the type T to
|
||||
// place bounds around it.
|
||||
let ty_elem = match &ty {
|
||||
syn::Type::Reference(reference) => &reference.elem,
|
||||
syn::Type::Ptr(ptr) => &ptr.elem,
|
||||
syn::Type::Slice(slice) => &slice.elem,
|
||||
syn::Type::Array(arr) => &arr.elem,
|
||||
_ => ty,
|
||||
};
|
||||
|
||||
ty_elem.clone()
|
||||
}
|
||||
|
||||
/// Extract the documentation from the provided attributes.
|
||||
///
|
||||
/// It takes into account the `no-metadata-docs` feature.
|
||||
fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 {
|
||||
if cfg!(feature = "no-metadata-docs") {
|
||||
quote!(#crate_::vec![])
|
||||
} else {
|
||||
let docs = get_doc_literals(&attrs);
|
||||
quote!(#crate_::vec![ #( #docs, )* ])
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the runtime metadata of the provided trait.
|
||||
///
|
||||
/// The metadata is exposed as a generic function on the hidden module
|
||||
/// of the trait generated by the `decl_runtime_apis`.
|
||||
pub fn generate_decl_runtime_metadata<'a>(
|
||||
decl: &ItemTrait,
|
||||
versioned_methods_iter: impl Iterator<Item = (&'a syn::TraitItemFn, u32)>,
|
||||
) -> TokenStream2 {
|
||||
let crate_ = generate_crate_access();
|
||||
let mut methods = Vec::new();
|
||||
|
||||
// Ensure that any function parameter that relies on the `BlockT` bounds
|
||||
// also has `TypeInfo + 'static` bounds (required by `scale_info::meta_type`).
|
||||
//
|
||||
// For example, if a runtime API defines a method that has an input:
|
||||
// `fn func(input: <Block as BlockT>::Header)`
|
||||
// then the runtime metadata will imply `<Block as BlockT>::Header: TypeInfo + 'static`.
|
||||
//
|
||||
// This restricts the bounds at the metadata level, without needing to modify the `BlockT`
|
||||
// itself, since the concrete implementations are already satisfying `TypeInfo`.
|
||||
let mut where_clause = Vec::new();
|
||||
for (method, version) in versioned_methods_iter {
|
||||
let mut inputs = Vec::new();
|
||||
let signature = &method.sig;
|
||||
for input in &signature.inputs {
|
||||
// Exclude `self` from metadata collection.
|
||||
let syn::FnArg::Typed(typed) = input else { continue };
|
||||
|
||||
let pat = &typed.pat;
|
||||
let name = quote!(#pat).to_string();
|
||||
let ty = &typed.ty;
|
||||
|
||||
where_clause.push(get_type_param(ty));
|
||||
|
||||
inputs.push(quote!(
|
||||
#crate_::metadata_ir::RuntimeApiMethodParamMetadataIR {
|
||||
name: #name,
|
||||
ty: #crate_::scale_info::meta_type::<#ty>(),
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
let output = match &signature.output {
|
||||
syn::ReturnType::Default => quote!(#crate_::scale_info::meta_type::<()>()),
|
||||
syn::ReturnType::Type(_, ty) => {
|
||||
where_clause.push(get_type_param(ty));
|
||||
quote!(#crate_::scale_info::meta_type::<#ty>())
|
||||
},
|
||||
};
|
||||
|
||||
// String method name including quotes for constructing `v15::RuntimeApiMethodMetadata`.
|
||||
let method_name = signature.ident.to_string();
|
||||
let docs = collect_docs(&method.attrs, &crate_);
|
||||
|
||||
// Include the method metadata only if its `cfg` features are enabled.
|
||||
let attrs = filter_cfg_attributes(&method.attrs);
|
||||
let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
// Methods are filtered so that only those whose version is <= the `impl_version` passed to
|
||||
// `runtime_metadata` are kept in the metadata we hand back.
|
||||
methods.push(quote!(
|
||||
#( #attrs )*
|
||||
if #version <= impl_version {
|
||||
Some(#crate_::metadata_ir::RuntimeApiMethodMetadataIR {
|
||||
name: #method_name,
|
||||
inputs: #crate_::vec![ #( #inputs, )* ],
|
||||
output: #output,
|
||||
docs: #docs,
|
||||
deprecation_info: #deprecation,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
let trait_name_ident = &decl.ident;
|
||||
let trait_name = trait_name_ident.to_string();
|
||||
let docs = collect_docs(&decl.attrs, &crate_);
|
||||
let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
let attrs = filter_cfg_attributes(&decl.attrs);
|
||||
// The trait generics where already extended with `Block: BlockT`.
|
||||
let mut generics = decl.generics.clone();
|
||||
for generic_param in generics.params.iter_mut() {
|
||||
let syn::GenericParam::Type(ty) = generic_param else { continue };
|
||||
|
||||
// Default type parameters are not allowed in functions.
|
||||
ty.eq_token = None;
|
||||
ty.default = None;
|
||||
}
|
||||
|
||||
where_clause
|
||||
.into_iter()
|
||||
.map(|ty| parse_quote!(#ty: #crate_::scale_info::TypeInfo + 'static))
|
||||
.for_each(|w| generics.make_where_clause().predicates.push(w));
|
||||
|
||||
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
quote!(
|
||||
#crate_::frame_metadata_enabled! {
|
||||
#( #attrs )*
|
||||
#[inline(always)]
|
||||
pub fn runtime_metadata #impl_generics (impl_version: u32) -> #crate_::metadata_ir::RuntimeApiMetadataIR
|
||||
#where_clause
|
||||
{
|
||||
#crate_::metadata_ir::RuntimeApiMetadataIR {
|
||||
name: #trait_name,
|
||||
methods: [ #( #methods, )* ]
|
||||
.into_iter()
|
||||
.filter_map(|maybe_m| maybe_m)
|
||||
.collect(),
|
||||
docs: #docs,
|
||||
deprecation_info: #deprecation,
|
||||
version: impl_version.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Implement the `runtime_metadata` function on the runtime that
|
||||
/// generates the metadata for the given traits.
|
||||
///
|
||||
/// The metadata of each trait is extracted from the generic function
|
||||
/// exposed by `generate_decl_runtime_metadata`.
|
||||
pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result<TokenStream2> {
|
||||
if impls.is_empty() {
|
||||
return Ok(quote!());
|
||||
}
|
||||
|
||||
let crate_ = generate_crate_access();
|
||||
|
||||
// Get the name of the runtime for which the traits are implemented.
|
||||
let runtime_name = &impls
|
||||
.get(0)
|
||||
.expect("Traits should contain at least one implementation; qed")
|
||||
.self_ty;
|
||||
|
||||
let mut metadata = Vec::new();
|
||||
|
||||
for impl_ in impls {
|
||||
let mut trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
|
||||
|
||||
// Implementation traits are always references with a path `impl client::Core<generics> ...`
|
||||
// The trait name is the last segment of this path.
|
||||
let trait_name_ident = &trait_
|
||||
.segments
|
||||
.last()
|
||||
.as_ref()
|
||||
.expect("Trait path should always contain at least one item; qed")
|
||||
.ident;
|
||||
|
||||
// Extract the generics from the trait to pass to the `runtime_metadata`
|
||||
// function on the hidden module.
|
||||
let generics = trait_
|
||||
.segments
|
||||
.iter()
|
||||
.find_map(|segment| {
|
||||
if let syn::PathArguments::AngleBracketed(generics) = &segment.arguments {
|
||||
Some(generics.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.expect("Trait path should always contain at least one generic parameter; qed");
|
||||
|
||||
let mod_name = generate_runtime_mod_name_for_trait(&trait_name_ident);
|
||||
// Get absolute path to the `runtime_decl_for_` module by replacing the last segment.
|
||||
if let Some(segment) = trait_.segments.last_mut() {
|
||||
*segment = parse_quote!(#mod_name);
|
||||
}
|
||||
|
||||
// Build a call to request runtime metadata for the appropriate API version.
|
||||
let runtime_metadata_call = {
|
||||
let api_version = extract_api_version(&impl_.attrs, impl_.span())?;
|
||||
|
||||
// If we've annotated an api_version, that defines the methods we need to impl.
|
||||
// If we haven't, then by default we are implementing methods for whatever the
|
||||
// base version of the declared runtime API is.
|
||||
let base_version = if let Some(version) = api_version.custom {
|
||||
quote! { #version }
|
||||
} else {
|
||||
quote! { #trait_::VERSION }
|
||||
};
|
||||
|
||||
// Handle the case where eg `#[cfg_attr(feature = "foo", api_version(4))]` is
|
||||
// present by using that version only when the feature is enabled and falling
|
||||
// back to the above version if not.
|
||||
if let Some(cfg_version) = api_version.feature_gated {
|
||||
let cfg_feature = cfg_version.0;
|
||||
let cfg_version = cfg_version.1;
|
||||
quote! {{
|
||||
if cfg!(feature = #cfg_feature) {
|
||||
#trait_::runtime_metadata::#generics(#cfg_version)
|
||||
} else {
|
||||
#trait_::runtime_metadata::#generics(#base_version)
|
||||
}
|
||||
}}
|
||||
} else {
|
||||
quote! {
|
||||
#trait_::runtime_metadata::#generics(#base_version)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let attrs = filter_cfg_attributes(&impl_.attrs);
|
||||
metadata.push(quote!(
|
||||
#( #attrs )*
|
||||
#runtime_metadata_call
|
||||
));
|
||||
}
|
||||
|
||||
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
|
||||
// The function is implemented by calling `impl_runtime_apis!`.
|
||||
//
|
||||
// However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`.
|
||||
// Rely on the `Deref` trait to differentiate between a runtime that implements
|
||||
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro
|
||||
// construct_runtime!).
|
||||
//
|
||||
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()`
|
||||
// function. `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime
|
||||
// references (`& Runtime`), while `InternalImplRuntimeApis` is implemented by the
|
||||
// `impl_runtime_apis!` for Runtime (`Runtime`).
|
||||
//
|
||||
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
|
||||
// when both macros are called; and will resolve an empty `runtime_metadata` when only the
|
||||
// `construct_runtime!` is called.
|
||||
Ok(quote!(
|
||||
#crate_::frame_metadata_enabled! {
|
||||
#[doc(hidden)]
|
||||
impl #crate_::metadata_ir::InternalImplRuntimeApis for #runtime_name {
|
||||
fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> {
|
||||
#crate_::vec![ #( #metadata, )* ]
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::common::API_VERSION_ATTRIBUTE;
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parenthesized, parse_quote, punctuated::Punctuated, spanned::Spanned, token::And, Attribute,
|
||||
Error, Expr, ExprLit, FnArg, GenericArgument, Ident, ItemImpl, Lit, LitInt, LitStr, Meta,
|
||||
MetaNameValue, Pat, Path, PathArguments, Result, ReturnType, Signature, Token, Type, TypePath,
|
||||
};
|
||||
|
||||
/// Generates the access to the `pezsc_client` crate.
|
||||
pub fn generate_crate_access() -> TokenStream {
|
||||
match crate_name("sp-api") {
|
||||
Ok(FoundCrate::Itself) => quote!(pezsp_api::__private),
|
||||
Ok(FoundCrate::Name(renamed_name)) => {
|
||||
let renamed_name = Ident::new(&renamed_name, Span::call_site());
|
||||
quote!(#renamed_name::__private)
|
||||
},
|
||||
Err(e) => {
|
||||
if let Ok(FoundCrate::Name(name)) =
|
||||
crate_name(&"pezkuwi-sdk-frame").or_else(|_| crate_name(&"frame"))
|
||||
{
|
||||
let path = format!("{}::deps::pezsp_api::__private", name);
|
||||
let path = syn::parse_str::<syn::Path>(&path).expect("is a valid path; qed");
|
||||
quote!( #path )
|
||||
} else if let Ok(FoundCrate::Name(name)) = crate_name(&"pezkuwi-sdk") {
|
||||
let path = format!("{}::pezsp_api::__private", name);
|
||||
let path = syn::parse_str::<syn::Path>(&path).expect("is a valid path; qed");
|
||||
quote!( #path )
|
||||
} else {
|
||||
let err = Error::new(Span::call_site(), e).to_compile_error();
|
||||
quote!( #err )
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the name of the module that contains the trait declaration for the runtime.
|
||||
pub fn generate_runtime_mod_name_for_trait(trait_: &Ident) -> Ident {
|
||||
Ident::new(
|
||||
&format!("runtime_decl_for_{}", trait_.to_string().to_snake_case()),
|
||||
Span::call_site(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the type of a `syn::ReturnType`.
|
||||
pub fn return_type_extract_type(rt: &ReturnType) -> Type {
|
||||
match rt {
|
||||
ReturnType::Default => parse_quote!(()),
|
||||
ReturnType::Type(_, ref ty) => *ty.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace the `_` (wild card) parameter names in the given signature with unique identifiers.
|
||||
pub fn replace_wild_card_parameter_names(input: &mut Signature) {
|
||||
let mut generated_pattern_counter = 0;
|
||||
input.inputs.iter_mut().for_each(|arg| {
|
||||
if let FnArg::Typed(arg) = arg {
|
||||
arg.pat =
|
||||
Box::new(sanitize_pattern((*arg.pat).clone(), &mut generated_pattern_counter));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Fold the given `Signature` to make it usable on the client side.
|
||||
pub fn fold_fn_decl_for_client_side(
|
||||
input: &mut Signature,
|
||||
block_hash: &TokenStream,
|
||||
crate_: &TokenStream,
|
||||
) {
|
||||
replace_wild_card_parameter_names(input);
|
||||
|
||||
// Add `&self, at:& Block::Hash` as parameters to each function at the beginning.
|
||||
input.inputs.insert(0, parse_quote!( __runtime_api_at_param__: #block_hash ));
|
||||
input.inputs.insert(0, parse_quote!(&self));
|
||||
|
||||
// Wrap the output in a `Result`
|
||||
input.output = {
|
||||
let ty = return_type_extract_type(&input.output);
|
||||
parse_quote!( -> std::result::Result<#ty, #crate_::ApiError> )
|
||||
};
|
||||
}
|
||||
|
||||
/// Sanitize the given pattern.
|
||||
///
|
||||
/// - `_` patterns are changed to a variable based on `counter`.
|
||||
/// - `mut something` removes the `mut`.
|
||||
pub fn sanitize_pattern(pat: Pat, counter: &mut u32) -> Pat {
|
||||
match pat {
|
||||
Pat::Wild(_) => {
|
||||
let generated_name =
|
||||
Ident::new(&format!("__runtime_api_generated_name_{}__", counter), pat.span());
|
||||
*counter += 1;
|
||||
|
||||
parse_quote!( #generated_name )
|
||||
},
|
||||
Pat::Ident(mut pat) => {
|
||||
pat.mutability = None;
|
||||
pat.into()
|
||||
},
|
||||
_ => pat,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow `&self` in parameters of a method.
|
||||
pub enum AllowSelfRefInParameters {
|
||||
/// Allows `&self` in parameters, but doesn't return it as part of the parameters.
|
||||
YesButIgnore,
|
||||
No,
|
||||
}
|
||||
|
||||
/// Extracts the name, the type and `&` or ``(if it is a reference or not)
|
||||
/// for each parameter in the given function signature.
|
||||
pub fn extract_parameter_names_types_and_borrows(
|
||||
sig: &Signature,
|
||||
allow_self: AllowSelfRefInParameters,
|
||||
) -> Result<Vec<(Pat, Type, Option<And>)>> {
|
||||
let mut result = Vec::new();
|
||||
let mut generated_pattern_counter = 0;
|
||||
for input in sig.inputs.iter() {
|
||||
match input {
|
||||
FnArg::Typed(arg) => {
|
||||
let (ty, borrow) = match &*arg.ty {
|
||||
Type::Reference(t) => ((*t.elem).clone(), Some(t.and_token)),
|
||||
t => (t.clone(), None),
|
||||
};
|
||||
|
||||
let name = sanitize_pattern((*arg.pat).clone(), &mut generated_pattern_counter);
|
||||
result.push((name, ty, borrow));
|
||||
},
|
||||
FnArg::Receiver(_) if matches!(allow_self, AllowSelfRefInParameters::No) =>
|
||||
return Err(Error::new(input.span(), "`self` parameter not supported!")),
|
||||
FnArg::Receiver(recv) =>
|
||||
if recv.mutability.is_some() || recv.reference.is_none() {
|
||||
return Err(Error::new(recv.span(), "Only `&self` is supported!"));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Prefix the given function with the trait name.
|
||||
pub fn prefix_function_with_trait<F: ToString>(trait_: &Ident, function: &F) -> String {
|
||||
format!("{}_{}", trait_, function.to_string())
|
||||
}
|
||||
|
||||
/// Extracts the block type from a trait path.
|
||||
///
|
||||
/// It is expected that the block type is the first type in the generic arguments.
|
||||
pub fn extract_block_type_from_trait_path(trait_: &Path) -> Result<&TypePath> {
|
||||
let span = trait_.span();
|
||||
let generics = trait_
|
||||
.segments
|
||||
.last()
|
||||
.ok_or_else(|| Error::new(span, "Empty path not supported"))?;
|
||||
|
||||
match &generics.arguments {
|
||||
PathArguments::AngleBracketed(ref args) => args
|
||||
.args
|
||||
.first()
|
||||
.and_then(|v| match v {
|
||||
GenericArgument::Type(Type::Path(ref block)) => Some(block),
|
||||
_ => None,
|
||||
})
|
||||
.ok_or_else(|| Error::new(args.span(), "Missing `Block` generic parameter.")),
|
||||
PathArguments::None => {
|
||||
let span = trait_.segments.last().as_ref().unwrap().span();
|
||||
Err(Error::new(span, "Missing `Block` generic parameter."))
|
||||
},
|
||||
PathArguments::Parenthesized(_) =>
|
||||
Err(Error::new(generics.arguments.span(), "Unexpected parentheses in path!")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Should a qualified trait path be required?
|
||||
///
|
||||
/// e.g. `path::Trait` is qualified and `Trait` is not.
|
||||
pub enum RequireQualifiedTraitPath {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
/// Extract the trait that is implemented by the given `ItemImpl`.
|
||||
pub fn extract_impl_trait(impl_: &ItemImpl, require: RequireQualifiedTraitPath) -> Result<&Path> {
|
||||
impl_
|
||||
.trait_
|
||||
.as_ref()
|
||||
.map(|v| &v.1)
|
||||
.ok_or_else(|| Error::new(impl_.span(), "Only implementation of traits are supported!"))
|
||||
.and_then(|p| {
|
||||
if p.segments.len() > 1 || matches!(require, RequireQualifiedTraitPath::No) {
|
||||
Ok(p)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
p.span(),
|
||||
"The implemented trait has to be referenced with a path, \
|
||||
e.g. `impl client::Core for Runtime`.",
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse the given attribute as `API_VERSION_ATTRIBUTE`.
|
||||
pub fn parse_runtime_api_version(version: &Attribute) -> Result<u32> {
|
||||
let version = version.parse_args::<syn::LitInt>().map_err(|_| {
|
||||
Error::new(
|
||||
version.span(),
|
||||
&format!(
|
||||
"Unexpected `{api_version}` attribute. The supported format is `{api_version}(1)`",
|
||||
api_version = API_VERSION_ATTRIBUTE
|
||||
),
|
||||
)
|
||||
})?;
|
||||
|
||||
version.base10_parse()
|
||||
}
|
||||
|
||||
/// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. TeyrchainHostV2
|
||||
pub fn versioned_trait_name(trait_ident: &Ident, version: u32) -> Ident {
|
||||
format_ident!("{}V{}", trait_ident, version)
|
||||
}
|
||||
|
||||
/// Extract the documentation from the provided attributes.
|
||||
pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec<syn::Lit> {
|
||||
use quote::ToTokens;
|
||||
attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let syn::Meta::NameValue(meta) = &attr.meta else { return None };
|
||||
let Ok(lit) = syn::parse2::<syn::Lit>(meta.value.to_token_stream()) else {
|
||||
unreachable!("non-lit doc attribute values do not exist");
|
||||
};
|
||||
meta.path.get_ident().filter(|ident| *ident == "doc").map(|_| lit)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Filters all attributes except the cfg ones.
|
||||
pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
|
||||
attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect()
|
||||
}
|
||||
|
||||
fn deprecation_msg_formatter(msg: &str) -> String {
|
||||
format!(
|
||||
r#"{msg}
|
||||
help: the following are the possible correct uses
|
||||
|
|
||||
| #[deprecated = "reason"]
|
||||
|
|
||||
| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
||||
|
|
||||
| #[deprecated]
|
||||
|"#
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result<TokenStream> {
|
||||
match &attr.meta {
|
||||
Meta::List(meta_list) => {
|
||||
let parsed = meta_list
|
||||
.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)
|
||||
.map_err(|e| Error::new(attr.span(), e.to_string()))?;
|
||||
let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| {
|
||||
let value = match &item.value {
|
||||
Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit),
|
||||
_ => Err(Error::new(
|
||||
attr.span(),
|
||||
deprecation_msg_formatter(
|
||||
"Invalid deprecation attribute: expected string literal",
|
||||
),
|
||||
)),
|
||||
}?;
|
||||
if item.path.is_ident("note") {
|
||||
acc.0.replace(value);
|
||||
} else if item.path.is_ident("since") {
|
||||
acc.1.replace(value);
|
||||
}
|
||||
Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc)
|
||||
})?;
|
||||
note.map_or_else(
|
||||
|| Err(Error::new(attr.span(), deprecation_msg_formatter(
|
||||
"Invalid deprecation attribute: missing `note`"))),
|
||||
|note| {
|
||||
let since = if let Some(str) = since {
|
||||
quote! { Some(#str) }
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
let doc = quote! { #crate_::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #note, since: #since }};
|
||||
Ok(doc)
|
||||
},
|
||||
)
|
||||
},
|
||||
Meta::NameValue(MetaNameValue {
|
||||
value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }),
|
||||
..
|
||||
}) => {
|
||||
// #[deprecated = "lit"]
|
||||
let doc = quote! { #crate_::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #lit, since: None } };
|
||||
Ok(doc)
|
||||
},
|
||||
Meta::Path(_) => {
|
||||
// #[deprecated]
|
||||
Ok(quote! { #crate_::metadata_ir::ItemDeprecationInfoIR::DeprecatedWithoutNote })
|
||||
},
|
||||
_ => Err(Error::new(
|
||||
attr.span(),
|
||||
deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// collects deprecation attribute if its present.
|
||||
pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result<TokenStream> {
|
||||
attrs
|
||||
.iter()
|
||||
.find(|a| a.path().is_ident("deprecated"))
|
||||
.map(|a| parse_deprecated_meta(&crate_, a))
|
||||
.unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::ItemDeprecationInfoIR::NotDeprecated}))
|
||||
}
|
||||
|
||||
/// Represents an API version.
|
||||
pub struct ApiVersion {
|
||||
/// Corresponds to `#[api_version(X)]` attribute.
|
||||
pub custom: Option<u32>,
|
||||
/// Corresponds to `#[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]`
|
||||
/// attribute. `String` is the feature name, `u32` the staging api version.
|
||||
pub feature_gated: Option<(String, u32)>,
|
||||
}
|
||||
|
||||
/// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors.
|
||||
/// Returns:
|
||||
/// - Err if the version is malformed
|
||||
/// - `ApiVersion` on success. If a version is set or not is determined by the fields of
|
||||
/// `ApiVersion`
|
||||
pub fn extract_api_version(attrs: &[Attribute], span: Span) -> Result<ApiVersion> {
|
||||
// First fetch all `API_VERSION_ATTRIBUTE` values (should be only one)
|
||||
let api_ver = attrs
|
||||
.iter()
|
||||
.filter(|a| a.path().is_ident(API_VERSION_ATTRIBUTE))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if api_ver.len() > 1 {
|
||||
return Err(Error::new(
|
||||
span,
|
||||
format!(
|
||||
"Found multiple #[{}] attributes for an API implementation. \
|
||||
Each runtime API can have only one version.",
|
||||
API_VERSION_ATTRIBUTE
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Parse the runtime version if there exists one.
|
||||
Ok(ApiVersion {
|
||||
custom: api_ver.first().map(|v| parse_runtime_api_version(v)).transpose()?,
|
||||
feature_gated: extract_cfg_api_version(attrs, span)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse feature flagged api_version.
|
||||
/// E.g. `#[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]`
|
||||
fn extract_cfg_api_version(attrs: &[Attribute], span: Span) -> Result<Option<(String, u32)>> {
|
||||
let cfg_attrs = attrs.iter().filter(|a| a.path().is_ident("cfg_attr")).collect::<Vec<_>>();
|
||||
|
||||
let mut cfg_api_version_attr = Vec::new();
|
||||
for cfg_attr in cfg_attrs {
|
||||
let mut feature_name = None;
|
||||
let mut api_version = None;
|
||||
cfg_attr.parse_nested_meta(|m| {
|
||||
if m.path.is_ident("feature") {
|
||||
let a = m.value()?;
|
||||
let b: LitStr = a.parse()?;
|
||||
feature_name = Some(b.value());
|
||||
} else if m.path.is_ident(API_VERSION_ATTRIBUTE) {
|
||||
let content;
|
||||
parenthesized!(content in m.input);
|
||||
let ver: LitInt = content.parse()?;
|
||||
api_version = Some(ver.base10_parse::<u32>()?);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// If there is a cfg attribute containing api_version - save if for processing
|
||||
if let (Some(feature_name), Some(api_version)) = (feature_name, api_version) {
|
||||
cfg_api_version_attr.push((feature_name, api_version, cfg_attr.span()));
|
||||
}
|
||||
}
|
||||
|
||||
if cfg_api_version_attr.len() > 1 {
|
||||
let mut err = Error::new(span, format!("Found multiple feature gated api versions (cfg attribute with nested `{}` attribute). This is not supported.", API_VERSION_ATTRIBUTE));
|
||||
for (_, _, attr_span) in cfg_api_version_attr {
|
||||
err.combine(Error::new(attr_span, format!("`{}` found here", API_VERSION_ATTRIBUTE)));
|
||||
}
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(cfg_api_version_attr
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|(feature, name, _)| (feature, name)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_get_doc_literals() {
|
||||
const FIRST: &'static str = "hello";
|
||||
const SECOND: &'static str = "WORLD";
|
||||
|
||||
let doc: Attribute = parse_quote!(#[doc = #FIRST]);
|
||||
let doc_world: Attribute = parse_quote!(#[doc = #SECOND]);
|
||||
|
||||
let attrs = vec![
|
||||
doc.clone(),
|
||||
parse_quote!(#[derive(Debug)]),
|
||||
parse_quote!(#[test]),
|
||||
parse_quote!(#[allow(non_camel_case_types)]),
|
||||
doc_world.clone(),
|
||||
];
|
||||
|
||||
let docs = get_doc_literals(&attrs);
|
||||
assert_eq!(docs.len(), 2);
|
||||
assert_matches!(&docs[0], syn::Lit::Str(val) if val.value() == FIRST);
|
||||
assert_matches!(&docs[1], syn::Lit::Str(val) if val.value() == SECOND);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_filter_cfg_attributes() {
|
||||
let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]);
|
||||
let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]);
|
||||
|
||||
let attrs = vec![
|
||||
cfg_std.clone(),
|
||||
parse_quote!(#[derive(Debug)]),
|
||||
parse_quote!(#[test]),
|
||||
cfg_benchmarks.clone(),
|
||||
parse_quote!(#[allow(non_camel_case_types)]),
|
||||
];
|
||||
|
||||
let filtered = filter_cfg_attributes(&attrs);
|
||||
assert_eq!(filtered.len(), 2);
|
||||
assert_eq!(cfg_std, filtered[0]);
|
||||
assert_eq!(cfg_benchmarks, filtered[1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_deprecated_attr() {
|
||||
const FIRST: &'static str = "hello";
|
||||
const SECOND: &'static str = "WORLD";
|
||||
|
||||
let simple: Attribute = parse_quote!(#[deprecated]);
|
||||
let simple_path: Attribute = parse_quote!(#[deprecated = #FIRST]);
|
||||
let meta_list: Attribute = parse_quote!(#[deprecated(note = #FIRST)]);
|
||||
let meta_list_with_since: Attribute =
|
||||
parse_quote!(#[deprecated(note = #FIRST, since = #SECOND)]);
|
||||
let extra_fields: Attribute =
|
||||
parse_quote!(#[deprecated(note = #FIRST, since = #SECOND, extra = "Test")]);
|
||||
assert_eq!(
|
||||
get_deprecation("e! { crate }, &[simple]).unwrap().to_string(),
|
||||
quote! { crate::metadata_ir::ItemDeprecationInfoIR::DeprecatedWithoutNote }.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
get_deprecation("e! { crate }, &[simple_path]).unwrap().to_string(),
|
||||
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: None } }.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
get_deprecation("e! { crate }, &[meta_list]).unwrap().to_string(),
|
||||
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: None } }.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
get_deprecation("e! { crate }, &[meta_list_with_since]).unwrap().to_string(),
|
||||
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
get_deprecation("e! { crate }, &[extra_fields]).unwrap().to_string(),
|
||||
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,857 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Bizinikiwi runtime api
|
||||
//!
|
||||
//! The Bizinikiwi runtime api is the interface between the node and the runtime. There isn't a fixed
|
||||
//! set of runtime apis, instead it is up to the user to declare and implement these runtime apis.
|
||||
//! The declaration of a runtime api is normally done outside of a runtime, while the implementation
|
||||
//! of it has to be done in the runtime. We provide the [`decl_runtime_apis!`] macro for declaring
|
||||
//! a runtime api and the [`impl_runtime_apis!`] for implementing them. The macro docs provide more
|
||||
//! information on how to use them and what kind of attributes we support.
|
||||
//!
|
||||
//! It is required that each runtime implements at least the [`Core`] runtime api. This runtime api
|
||||
//! provides all the core functions that Bizinikiwi expects from a runtime.
|
||||
//!
|
||||
//! # Versioning
|
||||
//!
|
||||
//! Runtime apis support versioning. Each runtime api itself has a version attached. It is also
|
||||
//! supported to change function signatures or names in a non-breaking way. For more information on
|
||||
//! versioning check the [`decl_runtime_apis!`] macro.
|
||||
//!
|
||||
//! All runtime apis and their versions are returned as part of the [`RuntimeVersion`]. This can be
|
||||
//! used to check which runtime api version is currently provided by the on-chain runtime.
|
||||
//!
|
||||
//! # Testing
|
||||
//!
|
||||
//! For testing we provide the [`mock_impl_runtime_apis!`] macro that lets you implement a runtime
|
||||
//! api for a mocked object to use it in tests.
|
||||
//!
|
||||
//! # Logging
|
||||
//!
|
||||
//! Bizinikiwi supports logging from the runtime in native and in wasm. For that purpose it provides
|
||||
//! the [`RuntimeLogger`](pezsp_runtime::runtime_logger::RuntimeLogger). This runtime logger is
|
||||
//! automatically enabled for each call into the runtime through the runtime api. As logging
|
||||
//! introduces extra code that isn't actually required for the logic of your runtime and also
|
||||
//! increases the final wasm blob size, it is recommended to disable the logging for on-chain
|
||||
//! wasm blobs. This can be done by enabling the `disable-logging` feature of this crate. Be aware
|
||||
//! that this feature instructs `log` and `tracing` to disable logging at compile time by setting
|
||||
//! the `max_level_off` feature for these crates. So, you should not enable this feature for a
|
||||
//! native build as otherwise the node will not output any log messages.
|
||||
//!
|
||||
//! # How does it work?
|
||||
//!
|
||||
//! Each runtime api is declared as a trait with functions. When compiled to WASM, each implemented
|
||||
//! runtime api function is exported as a function with the following naming scheme
|
||||
//! `${TRAIT_NAME}_${FUNCTION_NAME}`. Such a function has the following signature
|
||||
//! `(ptr: *u8, length: u32) -> u64`. It takes a pointer to an `u8` array and its length as an
|
||||
//! argument. This `u8` array is expected to be the SCALE encoded parameters of the function as
|
||||
//! defined in the trait. The return value is an `u64` that represents `length << 32 | pointer` of
|
||||
//! an `u8` array. This return value `u8` array contains the SCALE encoded return value as defined
|
||||
//! by the trait function. The macros take care to encode the parameters and to decode the return
|
||||
//! value.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
// Make doc tests happy
|
||||
extern crate self as pezsp_api;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
/// Private exports used by the macros.
|
||||
///
|
||||
/// This is seen as internal API and can change at any point.
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
#[cfg(feature = "std")]
|
||||
mod std_imports {
|
||||
pub use hash_db::Hasher;
|
||||
pub use pezsp_core::traits::CallContext;
|
||||
pub use pezsp_externalities::{Extension, Extensions, TransactionType};
|
||||
pub use pezsp_runtime::StateVersion;
|
||||
pub use pezsp_state_machine::{
|
||||
Backend as StateBackend, InMemoryBackend, OverlayedChanges, StorageProof, TrieBackend,
|
||||
TrieBackendBuilder,
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
pub use std_imports::*;
|
||||
|
||||
pub use crate::*;
|
||||
pub use alloc::vec;
|
||||
pub use codec::{self, Decode, DecodeLimit, Encode};
|
||||
pub use core::{mem, slice};
|
||||
pub use scale_info;
|
||||
pub use pezsp_core::offchain;
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub use pezsp_core::to_bizinikiwi_wasm_fn_return_value;
|
||||
#[cfg(feature = "frame-metadata")]
|
||||
pub use pezsp_metadata_ir::{self as metadata_ir, frame_metadata as metadata};
|
||||
pub use pezsp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, NumberFor},
|
||||
transaction_validity::TransactionValidity,
|
||||
ExtrinsicInclusionMode, TransactionOutcome,
|
||||
};
|
||||
pub use pezsp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion};
|
||||
|
||||
#[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), bizinikiwi_runtime))]
|
||||
pub use pezsp_runtime_interface::polkavm::{polkavm_abi, polkavm_export};
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use pezsp_core::traits::CallContext;
|
||||
use pezsp_core::OpaqueMetadata;
|
||||
#[cfg(feature = "std")]
|
||||
use pezsp_externalities::{Extension, Extensions};
|
||||
#[cfg(feature = "std")]
|
||||
use pezsp_runtime::traits::HashingFor;
|
||||
#[cfg(feature = "std")]
|
||||
pub use pezsp_runtime::TransactionOutcome;
|
||||
use pezsp_runtime::{traits::Block as BlockT, ExtrinsicInclusionMode};
|
||||
#[cfg(feature = "std")]
|
||||
pub use pezsp_state_machine::StorageProof;
|
||||
#[cfg(feature = "std")]
|
||||
use pezsp_state_machine::{backend::AsTrieBackend, Backend as StateBackend, OverlayedChanges};
|
||||
use pezsp_version::RuntimeVersion;
|
||||
#[cfg(feature = "std")]
|
||||
use std::cell::RefCell;
|
||||
|
||||
/// Declares given traits as runtime apis.
|
||||
///
|
||||
/// The macro will create two declarations, one for using on the client side and one for using
|
||||
/// on the runtime side. The declaration for the runtime side is hidden in its own module.
|
||||
/// The client side declaration gets two extra parameters per function,
|
||||
/// `&self` and `at: Block::Hash`. The runtime side declaration will match the given trait
|
||||
/// declaration. Besides one exception, the macro adds an extra generic parameter `Block:
|
||||
/// BlockT` to the client side and the runtime side. This generic parameter is usable by the
|
||||
/// user.
|
||||
///
|
||||
/// For implementing these macros you should use the
|
||||
/// [`impl_runtime_apis!`] macro.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// pezsp_api::decl_runtime_apis! {
|
||||
/// /// Declare the api trait.
|
||||
/// pub trait Balance {
|
||||
/// /// Get the balance.
|
||||
/// fn get_balance() -> u64;
|
||||
/// /// Set the balance.
|
||||
/// fn set_balance(val: u64);
|
||||
/// }
|
||||
///
|
||||
/// /// You can declare multiple api traits in one macro call.
|
||||
/// /// In one module you can call the macro at maximum one time.
|
||||
/// pub trait BlockBuilder {
|
||||
/// /// The macro adds an explicit `Block: BlockT` generic parameter for you.
|
||||
/// /// You can use this generic parameter as you would defined it manually.
|
||||
/// fn build_block() -> Block;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Runtime api trait versioning
|
||||
///
|
||||
/// To support versioning of the traits, the macro supports the attribute `#[api_version(1)]`.
|
||||
/// The attribute supports any `u32` as version. By default, each trait is at version `1`, if
|
||||
/// no version is provided. We also support changing the signature of a method. This signature
|
||||
/// change is highlighted with the `#[changed_in(2)]` attribute above a method. A method that
|
||||
/// is tagged with this attribute is callable by the name `METHOD_before_version_VERSION`. This
|
||||
/// method will only support calling into wasm, trying to call into native will fail (change
|
||||
/// the spec version!). Such a method also does not need to be implemented in the runtime. It
|
||||
/// is required that there exist the "default" of the method without the `#[changed_in(_)]`
|
||||
/// attribute, this method will be used to call the current default implementation.
|
||||
///
|
||||
/// ```rust
|
||||
/// pezsp_api::decl_runtime_apis! {
|
||||
/// /// Declare the api trait.
|
||||
/// #[api_version(2)]
|
||||
/// pub trait Balance {
|
||||
/// /// Get the balance.
|
||||
/// fn get_balance() -> u64;
|
||||
/// /// Set balance.
|
||||
/// fn set_balance(val: u64);
|
||||
/// /// Set balance, old version.
|
||||
/// ///
|
||||
/// /// Is callable by `set_balance_before_version_2`.
|
||||
/// #[changed_in(2)]
|
||||
/// fn set_balance(val: u16);
|
||||
/// /// In version 2, we added this new function.
|
||||
/// fn increase_balance(val: u64);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// To check if a given runtime implements a runtime api trait, the `RuntimeVersion` has the
|
||||
/// function `has_api<A>()`. Also the `ApiExt` provides a function `has_api<A>(at: Hash)`
|
||||
/// to check if the runtime at the given block id implements the requested runtime api trait.
|
||||
///
|
||||
/// # Declaring multiple api versions
|
||||
///
|
||||
/// Optionally multiple versions of the same api can be declared. This is useful for
|
||||
/// development purposes. For example you want to have a testing version of the api which is
|
||||
/// available only on a testnet. You can define one stable and one development version. This
|
||||
/// can be done like this:
|
||||
/// ```rust
|
||||
/// pezsp_api::decl_runtime_apis! {
|
||||
/// /// Declare the api trait.
|
||||
/// #[api_version(2)]
|
||||
/// pub trait Balance {
|
||||
/// /// Get the balance.
|
||||
/// fn get_balance() -> u64;
|
||||
/// /// Set the balance.
|
||||
/// fn set_balance(val: u64);
|
||||
/// /// Transfer the balance to another user id
|
||||
/// #[api_version(3)]
|
||||
/// fn transfer_balance(uid: u64);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
/// The example above defines two api versions - 2 and 3. Version 2 contains `get_balance` and
|
||||
/// `set_balance`. Version 3 additionally contains `transfer_balance`, which is not available
|
||||
/// in version 2. Version 2 in this case is considered the default/base version of the api.
|
||||
/// More than two versions can be defined this way. For example:
|
||||
/// ```rust
|
||||
/// pezsp_api::decl_runtime_apis! {
|
||||
/// /// Declare the api trait.
|
||||
/// #[api_version(2)]
|
||||
/// pub trait Balance {
|
||||
/// /// Get the balance.
|
||||
/// fn get_balance() -> u64;
|
||||
/// /// Set the balance.
|
||||
/// fn set_balance(val: u64);
|
||||
/// /// Transfer the balance to another user id
|
||||
/// #[api_version(3)]
|
||||
/// fn transfer_balance(uid: u64);
|
||||
/// /// Clears the balance
|
||||
/// #[api_version(4)]
|
||||
/// fn clear_balance();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
/// Note that the latest version (4 in our example above) always contains all methods from all
|
||||
/// the versions before.
|
||||
///
|
||||
/// ## Note on deprecation.
|
||||
///
|
||||
/// - Usage of `deprecated` attribute will propagate deprecation information to the metadata.
|
||||
/// - For general usage examples of `deprecated` attribute please refer to <https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-deprecated-attribute>
|
||||
pub use pezsp_api_proc_macro::decl_runtime_apis;
|
||||
|
||||
/// Tags given trait implementations as runtime apis.
|
||||
///
|
||||
/// All traits given to this macro, need to be declared with the
|
||||
/// [`decl_runtime_apis!`](macro.decl_runtime_apis.html) macro. The implementation of the trait
|
||||
/// should follow the declaration given to the
|
||||
/// [`decl_runtime_apis!`](macro.decl_runtime_apis.html) macro, besides the `Block` type that
|
||||
/// is required as first generic parameter for each runtime api trait. When implementing a
|
||||
/// runtime api trait, it is required that the trait is referenced by a path, e.g. `impl
|
||||
/// my_trait::MyTrait for Runtime`. The macro will use this path to access the declaration of
|
||||
/// the trait for the runtime side.
|
||||
///
|
||||
/// The macro also generates the api implementations for the client side and provides it
|
||||
/// through the `RuntimeApi` type. The `RuntimeApi` is hidden behind a `feature` called `std`.
|
||||
///
|
||||
/// To expose version information about all implemented api traits, the constant
|
||||
/// `RUNTIME_API_VERSIONS` is generated. This constant should be used to instantiate the `apis`
|
||||
/// field of `RuntimeVersion`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate alloc;
|
||||
/// #
|
||||
/// # use pezsp_runtime::{ExtrinsicInclusionMode, traits::Block as BlockT};
|
||||
/// # use pezsp_test_primitives::Block;
|
||||
/// #
|
||||
/// # /// The declaration of the `Runtime` type is done by the `construct_runtime!` macro
|
||||
/// # /// in a real runtime.
|
||||
/// # pub enum Runtime {}
|
||||
/// #
|
||||
/// # pezsp_api::decl_runtime_apis! {
|
||||
/// # /// Declare the api trait.
|
||||
/// # pub trait Balance {
|
||||
/// # /// Get the balance.
|
||||
/// # fn get_balance() -> u64;
|
||||
/// # /// Set the balance.
|
||||
/// # fn set_balance(val: u64);
|
||||
/// # }
|
||||
/// # pub trait BlockBuilder {
|
||||
/// # fn build_block() -> Block;
|
||||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// /// All runtime api implementations need to be done in one call of the macro!
|
||||
/// pezsp_api::impl_runtime_apis! {
|
||||
/// # impl pezsp_api::Core<Block> for Runtime {
|
||||
/// # fn version() -> pezsp_version::RuntimeVersion {
|
||||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # fn execute_block(_block: <Block as BlockT>::LazyBlock) {}
|
||||
/// # fn initialize_block(_header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
|
||||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// impl self::Balance<Block> for Runtime {
|
||||
/// fn get_balance() -> u64 {
|
||||
/// 1
|
||||
/// }
|
||||
/// fn set_balance(_bal: u64) {
|
||||
/// // Store the balance
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl self::BlockBuilder<Block> for Runtime {
|
||||
/// fn build_block() -> Block {
|
||||
/// unimplemented!("Please implement me!")
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Runtime version. This needs to be declared for each runtime.
|
||||
/// pub const VERSION: pezsp_version::RuntimeVersion = pezsp_version::RuntimeVersion {
|
||||
/// spec_name: alloc::borrow::Cow::Borrowed("node"),
|
||||
/// impl_name: alloc::borrow::Cow::Borrowed("test-node"),
|
||||
/// authoring_version: 1,
|
||||
/// spec_version: 1,
|
||||
/// impl_version: 0,
|
||||
/// // Here we are exposing the runtime api versions.
|
||||
/// apis: RUNTIME_API_VERSIONS,
|
||||
/// transaction_version: 1,
|
||||
/// system_version: 1,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # Implementing specific api version
|
||||
///
|
||||
/// If `decl_runtime_apis!` declares multiple versions for an api `impl_runtime_apis!`
|
||||
/// should specify which version it implements by adding `api_version` attribute to the
|
||||
/// `impl` block. If omitted - the base/default version is implemented. Here is an example:
|
||||
/// ```ignore
|
||||
/// pezsp_api::impl_runtime_apis! {
|
||||
/// #[api_version(3)]
|
||||
/// impl self::Balance<Block> for Runtime {
|
||||
/// // implementation
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// In this case `Balance` api version 3 is being implemented for `Runtime`. The `impl` block
|
||||
/// must contain all methods declared in version 3 and below.
|
||||
///
|
||||
/// # Conditional version implementation
|
||||
///
|
||||
/// `impl_runtime_apis!` supports `cfg_attr` attribute for conditional compilation. For example
|
||||
/// let's say you want to implement a staging version of the runtime api and put it behind a
|
||||
/// feature flag. You can do it this way:
|
||||
/// ```ignore
|
||||
/// pub enum Runtime {}
|
||||
/// pezsp_api::decl_runtime_apis! {
|
||||
/// pub trait ApiWithStagingMethod {
|
||||
/// fn stable_one(data: u64);
|
||||
///
|
||||
/// #[api_version(99)]
|
||||
/// fn pezstaging_one();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pezsp_api::impl_runtime_apis! {
|
||||
/// #[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]
|
||||
/// impl self::ApiWithStagingMethod<Block> for Runtime {
|
||||
/// fn stable_one(_: u64) {}
|
||||
///
|
||||
/// #[cfg(feature = "enable-pezstaging-api")]
|
||||
/// fn pezstaging_one() {}
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`decl_runtime_apis!`] declares two version of the api - 1 (the default one, which is
|
||||
/// considered stable in our example) and 99 (which is considered staging). In
|
||||
/// `impl_runtime_apis!` a `cfg_attr` attribute is attached to the `ApiWithStagingMethod`
|
||||
/// implementation. If the code is compiled with `enable-pezstaging-api` feature a version 99 of
|
||||
/// the runtime api will be built which will include `pezstaging_one`. Note that `pezstaging_one`
|
||||
/// implementation is feature gated by `#[cfg(feature = ... )]` attribute.
|
||||
///
|
||||
/// If the code is compiled without `enable-pezstaging-api` version 1 (the default one) will be
|
||||
/// built which doesn't include `pezstaging_one`.
|
||||
///
|
||||
/// `cfg_attr` can also be used together with `api_version`. For the next snippet will build
|
||||
/// version 99 if `enable-pezstaging-api` is enabled and version 2 otherwise because both
|
||||
/// `cfg_attr` and `api_version` are attached to the impl block:
|
||||
/// ```ignore
|
||||
/// #[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]
|
||||
/// #[api_version(2)]
|
||||
/// impl self::ApiWithStagingAndVersionedMethods<Block> for Runtime {
|
||||
/// // impl skipped
|
||||
/// }
|
||||
/// ```
|
||||
pub use pezsp_api_proc_macro::impl_runtime_apis;
|
||||
|
||||
/// Mocks given trait implementations as runtime apis.
|
||||
///
|
||||
/// Accepts similar syntax as [`impl_runtime_apis!`] and generates simplified mock
|
||||
/// implementations of the given runtime apis. The difference in syntax is that the trait does
|
||||
/// not need to be referenced by a qualified path, methods accept the `&self` parameter and the
|
||||
/// error type can be specified as associated type. If no error type is specified [`String`] is
|
||||
/// used as error type.
|
||||
///
|
||||
/// Besides implementing the given traits, the [`Core`] and [`ApiExt`] are implemented
|
||||
/// automatically.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pezsp_runtime::traits::Block as BlockT;
|
||||
/// # use pezsp_test_primitives::Block;
|
||||
/// #
|
||||
/// # pezsp_api::decl_runtime_apis! {
|
||||
/// # /// Declare the api trait.
|
||||
/// # pub trait Balance {
|
||||
/// # /// Get the balance.
|
||||
/// # fn get_balance() -> u64;
|
||||
/// # /// Set the balance.
|
||||
/// # fn set_balance(val: u64);
|
||||
/// # }
|
||||
/// # pub trait BlockBuilder {
|
||||
/// # fn build_block() -> Block;
|
||||
/// # }
|
||||
/// # }
|
||||
/// struct MockApi {
|
||||
/// balance: u64,
|
||||
/// }
|
||||
///
|
||||
/// /// All runtime api mock implementations need to be done in one call of the macro!
|
||||
/// pezsp_api::mock_impl_runtime_apis! {
|
||||
/// impl Balance<Block> for MockApi {
|
||||
/// /// Here we take the `&self` to access the instance.
|
||||
/// fn get_balance(&self) -> u64 {
|
||||
/// self.balance
|
||||
/// }
|
||||
/// fn set_balance(_bal: u64) {
|
||||
/// // Store the balance
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl BlockBuilder<Block> for MockApi {
|
||||
/// fn build_block() -> Block {
|
||||
/// unimplemented!("Not Required in tests")
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// # `advanced` attribute
|
||||
///
|
||||
/// This attribute can be placed above individual function in the mock implementation to
|
||||
/// request more control over the function declaration. From the client side each runtime api
|
||||
/// function is called with the `at` parameter that is a [`Hash`](pezsp_runtime::traits::Hash).
|
||||
/// When using the `advanced` attribute, the macro expects that the first parameter of the
|
||||
/// function is this `at` parameter. Besides that the macro also doesn't do the automatic
|
||||
/// return value rewrite, which means that full return value must be specified. The full return
|
||||
/// value is constructed like [`Result`]`<<ReturnValue>, Error>` while `ReturnValue` being the
|
||||
/// return value that is specified in the trait declaration.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// # use pezsp_runtime::traits::Block as BlockT;
|
||||
/// # use pezsp_test_primitives::Block;
|
||||
/// # use codec;
|
||||
/// #
|
||||
/// # pezsp_api::decl_runtime_apis! {
|
||||
/// # /// Declare the api trait.
|
||||
/// # pub trait Balance {
|
||||
/// # /// Get the balance.
|
||||
/// # fn get_balance() -> u64;
|
||||
/// # /// Set the balance.
|
||||
/// # fn set_balance(val: u64);
|
||||
/// # }
|
||||
/// # }
|
||||
/// struct MockApi {
|
||||
/// balance: u64,
|
||||
/// }
|
||||
///
|
||||
/// pezsp_api::mock_impl_runtime_apis! {
|
||||
/// impl Balance<Block> for MockApi {
|
||||
/// #[advanced]
|
||||
/// fn get_balance(&self, at: <Block as BlockT>::Hash) -> Result<u64, pezsp_api::ApiError> {
|
||||
/// println!("Being called at: {}", at);
|
||||
///
|
||||
/// Ok(self.balance.into())
|
||||
/// }
|
||||
/// #[advanced]
|
||||
/// fn set_balance(at: <Block as BlockT>::Hash, val: u64) -> Result<(), pezsp_api::ApiError> {
|
||||
/// println!("Being called at: {}", at);
|
||||
///
|
||||
/// Ok(().into())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub use pezsp_api_proc_macro::mock_impl_runtime_apis;
|
||||
|
||||
/// A type that records all accessed trie nodes and generates a proof out of it.
|
||||
#[cfg(feature = "std")]
|
||||
pub type ProofRecorder<B> = pezsp_trie::recorder::Recorder<HashingFor<B>>;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type ProofRecorderIgnoredNodes<B> = pezsp_trie::recorder::IgnoredNodes<<B as BlockT>::Hash>;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type StorageChanges<Block> = pezsp_state_machine::StorageChanges<HashingFor<Block>>;
|
||||
|
||||
/// Something that can be constructed to a runtime api.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait ConstructRuntimeApi<Block: BlockT, C: CallApiAt<Block>> {
|
||||
/// The actual runtime api that will be constructed.
|
||||
type RuntimeApi: ApiExt<Block>;
|
||||
|
||||
/// Construct an instance of the runtime api.
|
||||
fn construct_runtime_api(call: &C) -> ApiRef<'_, Self::RuntimeApi>;
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
/// Init the [`RuntimeLogger`](pezsp_runtime::runtime_logger::RuntimeLogger).
|
||||
pub fn init_runtime_logger() {
|
||||
#[cfg(not(feature = "disable-logging"))]
|
||||
pezsp_runtime::runtime_logger::RuntimeLogger::init();
|
||||
}
|
||||
|
||||
/// An error describing which API call failed.
|
||||
#[cfg(feature = "std")]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ApiError {
|
||||
#[error("Failed to decode return value of {function}: {error} raw data: {raw:?}")]
|
||||
FailedToDecodeReturnValue {
|
||||
function: &'static str,
|
||||
#[source]
|
||||
error: codec::Error,
|
||||
raw: Vec<u8>,
|
||||
},
|
||||
#[error("Failed to convert return value from runtime to node of {function}")]
|
||||
FailedToConvertReturnValue {
|
||||
function: &'static str,
|
||||
#[source]
|
||||
error: codec::Error,
|
||||
},
|
||||
#[error("Failed to convert parameter `{parameter}` from node to runtime of {function}")]
|
||||
FailedToConvertParameter {
|
||||
function: &'static str,
|
||||
parameter: &'static str,
|
||||
#[source]
|
||||
error: codec::Error,
|
||||
},
|
||||
#[error("The given `StateBackend` isn't a `TrieBackend`.")]
|
||||
StateBackendIsNotTrie,
|
||||
#[error(transparent)]
|
||||
Application(#[from] Box<dyn std::error::Error + Send + Sync>),
|
||||
#[error("Api called for an unknown Block: {0}")]
|
||||
UnknownBlock(String),
|
||||
#[error("Using the same api instance to call into multiple independent blocks.")]
|
||||
UsingSameInstanceForDifferentBlocks,
|
||||
}
|
||||
|
||||
/// Extends the runtime api implementation with some common functionality.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait ApiExt<Block: BlockT> {
|
||||
/// Execute the given closure inside a new transaction.
|
||||
///
|
||||
/// Depending on the outcome of the closure, the transaction is committed or rolled-back.
|
||||
///
|
||||
/// The internal result of the closure is returned afterwards.
|
||||
fn execute_in_transaction<F: FnOnce(&Self) -> TransactionOutcome<R>, R>(&self, call: F) -> R
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Checks if the given api is implemented and versions match.
|
||||
fn has_api<A: RuntimeApiInfo + ?Sized>(&self, at_hash: Block::Hash) -> Result<bool, ApiError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Check if the given api is implemented and the version passes a predicate.
|
||||
fn has_api_with<A: RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
|
||||
&self,
|
||||
at_hash: Block::Hash,
|
||||
pred: P,
|
||||
) -> Result<bool, ApiError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Returns the version of the given api.
|
||||
fn api_version<A: RuntimeApiInfo + ?Sized>(
|
||||
&self,
|
||||
at_hash: Block::Hash,
|
||||
) -> Result<Option<u32>, ApiError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Start recording all accessed trie nodes.
|
||||
///
|
||||
/// The recorded trie nodes can be converted into a proof using [`Self::extract_proof`].
|
||||
fn record_proof(&mut self);
|
||||
|
||||
/// Start recording all accessed trie nodes using the given proof recorder.
|
||||
///
|
||||
/// The recorded trie nodes can be converted into a proof using [`Self::extract_proof`].
|
||||
fn record_proof_with_recorder(&mut self, recorder: ProofRecorder<Block>);
|
||||
|
||||
/// Extract the recorded proof.
|
||||
///
|
||||
/// This stops the proof recording.
|
||||
///
|
||||
/// If `record_proof` was not called before, this will return `None`.
|
||||
fn extract_proof(&mut self) -> Option<StorageProof>;
|
||||
|
||||
/// Returns the current active proof recorder.
|
||||
fn proof_recorder(&self) -> Option<ProofRecorder<Block>>;
|
||||
|
||||
/// Convert the api object into the storage changes that were done while executing runtime
|
||||
/// api functions.
|
||||
///
|
||||
/// After executing this function, all collected changes are reset.
|
||||
fn into_storage_changes<B: StateBackend<HashingFor<Block>>>(
|
||||
&self,
|
||||
backend: &B,
|
||||
parent_hash: Block::Hash,
|
||||
) -> Result<StorageChanges<Block>, String>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Set the [`CallContext`] to be used by the runtime api calls done by this instance.
|
||||
fn set_call_context(&mut self, call_context: CallContext);
|
||||
|
||||
/// Register an [`Extension`] that will be accessible while executing a runtime api call.
|
||||
fn register_extension<E: Extension>(&mut self, extension: E);
|
||||
}
|
||||
|
||||
/// Parameters for [`CallApiAt::call_api_at`].
|
||||
#[cfg(feature = "std")]
|
||||
pub struct CallApiAtParams<'a, Block: BlockT> {
|
||||
/// The block id that determines the state that should be setup when calling the function.
|
||||
pub at: Block::Hash,
|
||||
/// The name of the function that should be called.
|
||||
pub function: &'static str,
|
||||
/// The encoded arguments of the function.
|
||||
pub arguments: Vec<u8>,
|
||||
/// The overlayed changes that are on top of the state.
|
||||
pub overlayed_changes: &'a RefCell<OverlayedChanges<HashingFor<Block>>>,
|
||||
/// The call context of this call.
|
||||
pub call_context: CallContext,
|
||||
/// The optional proof recorder for recording storage accesses.
|
||||
pub recorder: &'a Option<ProofRecorder<Block>>,
|
||||
/// The extensions that should be used for this call.
|
||||
pub extensions: &'a RefCell<Extensions>,
|
||||
}
|
||||
|
||||
/// Something that can call into an api at a given block.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait CallApiAt<Block: BlockT> {
|
||||
/// The state backend that is used to store the block states.
|
||||
type StateBackend: StateBackend<HashingFor<Block>> + AsTrieBackend<HashingFor<Block>>;
|
||||
|
||||
/// Calls the given api function with the given encoded arguments at the given block and returns
|
||||
/// the encoded result.
|
||||
fn call_api_at(&self, params: CallApiAtParams<Block>) -> Result<Vec<u8>, ApiError>;
|
||||
|
||||
/// Returns the runtime version at the given block.
|
||||
fn runtime_version_at(&self, at_hash: Block::Hash) -> Result<RuntimeVersion, ApiError>;
|
||||
|
||||
/// Get the state `at` the given block.
|
||||
fn state_at(&self, at: Block::Hash) -> Result<Self::StateBackend, ApiError>;
|
||||
|
||||
/// Initialize the `extensions` for the given block `at` by using the global extensions factory.
|
||||
fn initialize_extensions(
|
||||
&self,
|
||||
at: Block::Hash,
|
||||
extensions: &mut Extensions,
|
||||
) -> Result<(), ApiError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Block: BlockT, T: CallApiAt<Block>> CallApiAt<Block> for std::sync::Arc<T> {
|
||||
type StateBackend = T::StateBackend;
|
||||
|
||||
fn call_api_at(&self, params: CallApiAtParams<Block>) -> Result<Vec<u8>, ApiError> {
|
||||
(**self).call_api_at(params)
|
||||
}
|
||||
|
||||
fn runtime_version_at(
|
||||
&self,
|
||||
at_hash: <Block as BlockT>::Hash,
|
||||
) -> Result<RuntimeVersion, ApiError> {
|
||||
(**self).runtime_version_at(at_hash)
|
||||
}
|
||||
|
||||
fn state_at(&self, at: <Block as BlockT>::Hash) -> Result<Self::StateBackend, ApiError> {
|
||||
(**self).state_at(at)
|
||||
}
|
||||
|
||||
fn initialize_extensions(
|
||||
&self,
|
||||
at: <Block as BlockT>::Hash,
|
||||
extensions: &mut Extensions,
|
||||
) -> Result<(), ApiError> {
|
||||
(**self).initialize_extensions(at, extensions)
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary wrapper that holds an api instance and binds it to the given lifetime.
|
||||
#[cfg(feature = "std")]
|
||||
pub struct ApiRef<'a, T>(T, std::marker::PhantomData<&'a ()>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, T> From<T> for ApiRef<'a, T> {
|
||||
fn from(api: T) -> Self {
|
||||
ApiRef(api, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, T> std::ops::Deref for ApiRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, T> std::ops::DerefMut for ApiRef<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that provides a runtime api.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait ProvideRuntimeApi<Block: BlockT> {
|
||||
/// The concrete type that provides the api.
|
||||
type Api: ApiExt<Block>;
|
||||
|
||||
/// Returns the runtime api.
|
||||
/// The returned instance will keep track of modifications to the storage. Any successful
|
||||
/// call to an api function, will `commit` its changes to an internal buffer. Otherwise,
|
||||
/// the modifications will be `discarded`. The modifications will not be applied to the
|
||||
/// storage, even on a `commit`.
|
||||
fn runtime_api(&self) -> ApiRef<'_, Self::Api>;
|
||||
}
|
||||
|
||||
/// Something that provides information about a runtime api.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait RuntimeApiInfo {
|
||||
/// The identifier of the runtime api.
|
||||
const ID: [u8; 8];
|
||||
/// The version of the runtime api.
|
||||
const VERSION: u32;
|
||||
}
|
||||
|
||||
/// The number of bytes required to encode a [`RuntimeApiInfo`].
|
||||
///
|
||||
/// 8 bytes for `ID` and 4 bytes for a version.
|
||||
pub const RUNTIME_API_INFO_SIZE: usize = 12;
|
||||
|
||||
/// Crude and simple way to serialize the `RuntimeApiInfo` into a bunch of bytes.
|
||||
pub const fn serialize_runtime_api_info(id: [u8; 8], version: u32) -> [u8; RUNTIME_API_INFO_SIZE] {
|
||||
let version = version.to_le_bytes();
|
||||
|
||||
let mut r = [0; RUNTIME_API_INFO_SIZE];
|
||||
r[0] = id[0];
|
||||
r[1] = id[1];
|
||||
r[2] = id[2];
|
||||
r[3] = id[3];
|
||||
r[4] = id[4];
|
||||
r[5] = id[5];
|
||||
r[6] = id[6];
|
||||
r[7] = id[7];
|
||||
|
||||
r[8] = version[0];
|
||||
r[9] = version[1];
|
||||
r[10] = version[2];
|
||||
r[11] = version[3];
|
||||
r
|
||||
}
|
||||
|
||||
/// Deserialize the runtime API info serialized by [`serialize_runtime_api_info`].
|
||||
pub fn deserialize_runtime_api_info(bytes: [u8; RUNTIME_API_INFO_SIZE]) -> ([u8; 8], u32) {
|
||||
let id: [u8; 8] = bytes[0..8]
|
||||
.try_into()
|
||||
.expect("the source slice size is equal to the dest array length; qed");
|
||||
|
||||
let version = u32::from_le_bytes(
|
||||
bytes[8..12]
|
||||
.try_into()
|
||||
.expect("the source slice size is equal to the array length; qed"),
|
||||
);
|
||||
|
||||
(id, version)
|
||||
}
|
||||
|
||||
decl_runtime_apis! {
|
||||
/// The `Core` runtime api that every Bizinikiwi runtime needs to implement.
|
||||
#[core_trait]
|
||||
#[api_version(5)]
|
||||
pub trait Core {
|
||||
/// Returns the version of the runtime.
|
||||
fn version() -> RuntimeVersion;
|
||||
/// Execute the given block.
|
||||
fn execute_block(block: Block::LazyBlock);
|
||||
/// Initialize a block with the given header.
|
||||
#[changed_in(5)]
|
||||
#[renamed("initialise_block", 2)]
|
||||
fn initialize_block(header: &<Block as BlockT>::Header);
|
||||
/// Initialize a block with the given header and return the runtime executive mode.
|
||||
fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode;
|
||||
}
|
||||
|
||||
/// The `Metadata` api trait that returns metadata for the runtime.
|
||||
#[api_version(2)]
|
||||
pub trait Metadata {
|
||||
/// Returns the metadata of a runtime.
|
||||
fn metadata() -> OpaqueMetadata;
|
||||
|
||||
/// Returns the metadata at a given version.
|
||||
///
|
||||
/// If the given `version` isn't supported, this will return `None`.
|
||||
/// Use [`Self::metadata_versions`] to find out about supported metadata version of the runtime.
|
||||
fn metadata_at_version(version: u32) -> Option<OpaqueMetadata>;
|
||||
|
||||
/// Returns the supported metadata versions.
|
||||
///
|
||||
/// This can be used to call `metadata_at_version`.
|
||||
fn metadata_versions() -> alloc::vec::Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $);
|
||||
pezsp_core::generate_feature_enabled_macro!(std_disabled, not(feature = "std"), $);
|
||||
pezsp_core::generate_feature_enabled_macro!(frame_metadata_enabled, feature = "frame-metadata", $);
|
||||
@@ -0,0 +1,55 @@
|
||||
[package]
|
||||
name = "pezsp-api-test"
|
||||
version = "2.0.1"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
publish = false
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true, default-features = true }
|
||||
rustversion = { workspace = true }
|
||||
pezsc-block-builder = { workspace = true, default-features = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
pezsp-api = { workspace = true, default-features = true }
|
||||
pezsp-consensus = { workspace = true, default-features = true }
|
||||
pezsp-externalities = { workspace = true, default-features = true }
|
||||
pezsp-metadata-ir = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
pezsp-state-machine = { workspace = true, default-features = true }
|
||||
pezsp-tracing = { workspace = true, default-features = true }
|
||||
pezsp-version = { workspace = true, default-features = true }
|
||||
bizinikiwi-test-runtime-client = { workspace = true }
|
||||
trybuild = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true, default-features = true }
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
static_assertions = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
enable-pezstaging-api = []
|
||||
disable-ui-tests = []
|
||||
runtime-benchmarks = [
|
||||
"pezsc-block-builder/runtime-benchmarks",
|
||||
"pezsp-api/runtime-benchmarks",
|
||||
"pezsp-consensus/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"pezsp-state-machine/runtime-benchmarks",
|
||||
"pezsp-version/runtime-benchmarks",
|
||||
"bizinikiwi-test-runtime-client/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,71 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use pezsp_api::ProvideRuntimeApi;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
fn pezsp_api_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("add one with same runtime api", |b| {
|
||||
let client = bizinikiwi_test_runtime_client::new();
|
||||
let runtime_api = client.runtime_api();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
|
||||
b.iter(|| runtime_api.benchmark_add_one(best_hash, &1))
|
||||
});
|
||||
|
||||
c.bench_function("add one with recreating runtime api", |b| {
|
||||
let client = bizinikiwi_test_runtime_client::new();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
|
||||
b.iter(|| client.runtime_api().benchmark_add_one(best_hash, &1))
|
||||
});
|
||||
|
||||
c.bench_function("vector add one with same runtime api", |b| {
|
||||
let client = bizinikiwi_test_runtime_client::new();
|
||||
let runtime_api = client.runtime_api();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
let data = vec![0; 1000];
|
||||
|
||||
b.iter_with_large_drop(|| runtime_api.benchmark_vector_add_one(best_hash, &data))
|
||||
});
|
||||
|
||||
c.bench_function("vector add one with recreating runtime api", |b| {
|
||||
let client = bizinikiwi_test_runtime_client::new();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
let data = vec![0; 1000];
|
||||
|
||||
b.iter_with_large_drop(|| client.runtime_api().benchmark_vector_add_one(best_hash, &data))
|
||||
});
|
||||
|
||||
c.bench_function("calling function by function pointer in wasm", |b| {
|
||||
let client = TestClientBuilder::new().build();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
b.iter(|| client.runtime_api().benchmark_indirect_call(best_hash).unwrap())
|
||||
});
|
||||
|
||||
c.bench_function("calling function", |b| {
|
||||
let client = TestClientBuilder::new().build();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
b.iter(|| client.runtime_api().benchmark_direct_call(best_hash).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, pezsp_api_benchmark);
|
||||
criterion_main!(benches);
|
||||
@@ -0,0 +1,378 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_api::{
|
||||
decl_runtime_apis, impl_runtime_apis, mock_impl_runtime_apis, ApiError, ApiExt, RuntimeApiInfo,
|
||||
};
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
|
||||
use bizinikiwi_test_runtime_client::runtime::{Block, Hash};
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
pub struct Runtime {}
|
||||
|
||||
decl_runtime_apis! {
|
||||
#[deprecated]
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
fn something_with_block(block: Block) -> Block;
|
||||
fn function_with_two_args(data: u64, block: Block);
|
||||
fn same_name();
|
||||
fn wild_card(_: u32);
|
||||
}
|
||||
|
||||
#[api_version(2)]
|
||||
pub trait ApiWithCustomVersion {
|
||||
fn same_name();
|
||||
#[changed_in(2)]
|
||||
fn same_name() -> String;
|
||||
}
|
||||
|
||||
#[api_version(2)]
|
||||
pub trait ApiWithMultipleVersions {
|
||||
fn stable_one(data: u64);
|
||||
#[api_version(3)]
|
||||
fn new_one();
|
||||
#[api_version(4)]
|
||||
fn glory_one();
|
||||
}
|
||||
|
||||
pub trait ApiWithStagingMethod {
|
||||
fn stable_one(data: u64);
|
||||
#[api_version(99)]
|
||||
fn pezstaging_one();
|
||||
}
|
||||
|
||||
pub trait ApiWithStagingAndVersionedMethods {
|
||||
fn stable_one(data: u64);
|
||||
#[api_version(2)]
|
||||
fn new_one();
|
||||
#[api_version(99)]
|
||||
fn pezstaging_one();
|
||||
}
|
||||
|
||||
#[api_version(2)]
|
||||
pub trait ApiWithStagingAndChangedBase {
|
||||
fn stable_one(data: u64);
|
||||
fn new_one();
|
||||
#[api_version(99)]
|
||||
fn pezstaging_one();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl_runtime_apis! {
|
||||
#[allow(deprecated)]
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test(_: u64) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// Ensure that we accept `mut`
|
||||
fn something_with_block(mut _block: Block) -> Block {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn function_with_two_args(_: u64, _: Block) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn same_name() {}
|
||||
|
||||
fn wild_card(_: u32) {}
|
||||
}
|
||||
|
||||
impl self::ApiWithCustomVersion<Block> for Runtime {
|
||||
fn same_name() {}
|
||||
}
|
||||
|
||||
#[api_version(3)]
|
||||
impl self::ApiWithMultipleVersions<Block> for Runtime {
|
||||
fn stable_one(_: u64) {}
|
||||
|
||||
fn new_one() {}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]
|
||||
impl self::ApiWithStagingMethod<Block> for Runtime {
|
||||
fn stable_one(_: u64) {}
|
||||
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
fn pezstaging_one() { }
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]
|
||||
#[api_version(2)]
|
||||
impl self::ApiWithStagingAndVersionedMethods<Block> for Runtime {
|
||||
fn stable_one(_: u64) {}
|
||||
fn new_one() {}
|
||||
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
fn pezstaging_one() {}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "enable-pezstaging-api", api_version(99))]
|
||||
impl self::ApiWithStagingAndChangedBase<Block> for Runtime {
|
||||
fn stable_one(_: u64) {}
|
||||
fn new_one() {}
|
||||
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
fn pezstaging_one() {}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_version::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MockApi {
|
||||
block: Option<Block>,
|
||||
}
|
||||
|
||||
mock_impl_runtime_apis! {
|
||||
#[allow(deprecated)]
|
||||
impl Api<Block> for MockApi {
|
||||
fn test(_: u64) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn something_with_block(&self, _: Block) -> Block {
|
||||
self.block.clone().unwrap()
|
||||
}
|
||||
|
||||
fn function_with_two_args(_: u64, _: Block) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[advanced]
|
||||
fn same_name(_: <Block as BlockT>::Hash) -> Result<(), ApiError> {
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
#[advanced]
|
||||
fn wild_card(at: <Block as BlockT>::Hash, _: u32) -> Result<(), ApiError> {
|
||||
if Hash::repeat_byte(0x0f) == at {
|
||||
// yeah
|
||||
Ok(().into())
|
||||
} else {
|
||||
Err((Box::from("Test error") as Box<dyn std::error::Error + Send + Sync>).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiWithCustomVersion<Block> for MockApi {
|
||||
fn same_name() {}
|
||||
}
|
||||
}
|
||||
|
||||
type TestClient = bizinikiwi_test_runtime_client::client::Client<
|
||||
bizinikiwi_test_runtime_client::Backend,
|
||||
bizinikiwi_test_runtime_client::ExecutorDispatch,
|
||||
Block,
|
||||
RuntimeApi,
|
||||
>;
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn test_client_side_function_signature() {
|
||||
let _test: fn(
|
||||
&RuntimeApiImpl<Block, TestClient>,
|
||||
<Block as BlockT>::Hash,
|
||||
u64,
|
||||
) -> Result<(), ApiError> = RuntimeApiImpl::<Block, TestClient>::test;
|
||||
let _something_with_block: fn(
|
||||
&RuntimeApiImpl<Block, TestClient>,
|
||||
<Block as BlockT>::Hash,
|
||||
Block,
|
||||
) -> Result<Block, ApiError> = RuntimeApiImpl::<Block, TestClient>::something_with_block;
|
||||
|
||||
#[allow(deprecated)]
|
||||
let _same_name_before_version_2: fn(
|
||||
&RuntimeApiImpl<Block, TestClient>,
|
||||
<Block as BlockT>::Hash,
|
||||
) -> Result<String, ApiError> = RuntimeApiImpl::<Block, TestClient>::same_name_before_version_2;
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn check_runtime_api_info() {
|
||||
assert_eq!(&<dyn Api::<Block>>::ID, &runtime_decl_for_api::ID);
|
||||
assert_eq!(<dyn Api::<Block>>::VERSION, runtime_decl_for_api::VERSION);
|
||||
assert_eq!(<dyn Api::<Block>>::VERSION, 1);
|
||||
|
||||
assert_eq!(
|
||||
<dyn ApiWithCustomVersion::<Block>>::VERSION,
|
||||
runtime_decl_for_api_with_custom_version::VERSION,
|
||||
);
|
||||
assert_eq!(
|
||||
&<dyn ApiWithCustomVersion::<Block>>::ID,
|
||||
&runtime_decl_for_api_with_custom_version::ID,
|
||||
);
|
||||
assert_eq!(<dyn ApiWithCustomVersion::<Block>>::VERSION, 2);
|
||||
|
||||
// The stable version of the API
|
||||
assert_eq!(<dyn ApiWithMultipleVersions::<Block>>::VERSION, 2);
|
||||
|
||||
assert_eq!(<dyn ApiWithStagingMethod::<Block>>::VERSION, 1);
|
||||
assert_eq!(<dyn ApiWithStagingAndVersionedMethods::<Block>>::VERSION, 1);
|
||||
assert_eq!(<dyn ApiWithStagingAndChangedBase::<Block>>::VERSION, 2);
|
||||
}
|
||||
|
||||
fn check_runtime_api_versions_contains<T: RuntimeApiInfo + ?Sized>() {
|
||||
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, T::VERSION)));
|
||||
}
|
||||
|
||||
fn check_staging_runtime_api_versions<T: RuntimeApiInfo + ?Sized>(_staging_ver: u32) {
|
||||
// Staging APIs should contain staging version if the feature is set...
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, _staging_ver)));
|
||||
//... otherwise the base version should be set
|
||||
#[cfg(not(feature = "enable-pezstaging-api"))]
|
||||
check_runtime_api_versions_contains::<dyn ApiWithStagingMethod<Block>>();
|
||||
}
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
fn check_staging_multiver_runtime_api_versions<T: RuntimeApiInfo + ?Sized>(
|
||||
_staging_ver: u32,
|
||||
_stable_ver: u32,
|
||||
) {
|
||||
// Staging APIs should contain staging version if the feature is set...
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, _staging_ver)));
|
||||
//... otherwise the base version should be set
|
||||
#[cfg(not(feature = "enable-pezstaging-api"))]
|
||||
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, _stable_ver)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn check_runtime_api_versions() {
|
||||
check_runtime_api_versions_contains::<dyn Api<Block>>();
|
||||
check_runtime_api_versions_contains::<dyn ApiWithCustomVersion<Block>>();
|
||||
assert!(RUNTIME_API_VERSIONS
|
||||
.iter()
|
||||
.any(|v| v == &(<dyn ApiWithMultipleVersions<Block>>::ID, 3)));
|
||||
|
||||
check_staging_runtime_api_versions::<dyn ApiWithStagingMethod<Block>>(99);
|
||||
check_staging_multiver_runtime_api_versions::<dyn ApiWithStagingAndVersionedMethods<Block>>(
|
||||
99, 2,
|
||||
);
|
||||
check_staging_runtime_api_versions::<dyn ApiWithStagingAndChangedBase<Block>>(99);
|
||||
|
||||
check_runtime_api_versions_contains::<dyn pezsp_api::Core<Block>>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn mock_runtime_api_has_api() {
|
||||
let mock = MockApi { block: None };
|
||||
|
||||
assert!(mock.has_api::<dyn ApiWithCustomVersion<Block>>(Hash::default()).unwrap());
|
||||
assert!(mock.has_api::<dyn Api<Block>>(Hash::default()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Calling deprecated methods is not supported by mocked runtime api.")]
|
||||
fn mock_runtime_api_panics_on_calling_old_version() {
|
||||
let mock = MockApi { block: None };
|
||||
|
||||
#[allow(deprecated)]
|
||||
let _ = mock.same_name_before_version_2(Hash::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn mock_runtime_api_works_with_advanced() {
|
||||
let mock = MockApi { block: None };
|
||||
|
||||
Api::<Block>::same_name(&mock, Hash::default()).unwrap();
|
||||
mock.wild_card(Hash::repeat_byte(0x0f), 1).unwrap();
|
||||
assert_eq!(
|
||||
"Test error".to_string(),
|
||||
mock.wild_card(Hash::repeat_byte(0x01), 1).unwrap_err().to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_api_metadata_matches_version_implemented() {
|
||||
use pezsp_metadata_ir::InternalImplRuntimeApis;
|
||||
|
||||
let rt = Runtime {};
|
||||
let runtime_metadata = rt.runtime_metadata();
|
||||
|
||||
// Check that the metadata for some runtime API matches expectation.
|
||||
let assert_has_api_with_methods = |api_name: &str, api_methods: &[&str]| {
|
||||
let Some(api) = runtime_metadata.iter().find(|api| api.name == api_name) else {
|
||||
panic!("Can't find runtime API '{api_name}'");
|
||||
};
|
||||
if api.methods.len() != api_methods.len() {
|
||||
panic!(
|
||||
"Wrong number of methods in '{api_name}'; expected {} methods but got {}: {:?}",
|
||||
api_methods.len(),
|
||||
api.methods.len(),
|
||||
api.methods
|
||||
);
|
||||
}
|
||||
for expected_name in api_methods {
|
||||
if !api.methods.iter().any(|method| &method.name == expected_name) {
|
||||
panic!("Can't find API method '{expected_name}' in '{api_name}'");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assert_has_api_with_methods("ApiWithCustomVersion", &["same_name"]);
|
||||
|
||||
assert_has_api_with_methods("ApiWithMultipleVersions", &["stable_one", "new_one"]);
|
||||
|
||||
assert_has_api_with_methods(
|
||||
"ApiWithStagingMethod",
|
||||
&[
|
||||
"stable_one",
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
"pezstaging_one",
|
||||
],
|
||||
);
|
||||
|
||||
assert_has_api_with_methods(
|
||||
"ApiWithStagingAndVersionedMethods",
|
||||
&[
|
||||
"stable_one",
|
||||
"new_one",
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
"pezstaging_one",
|
||||
],
|
||||
);
|
||||
|
||||
assert_has_api_with_methods(
|
||||
"ApiWithStagingAndChangedBase",
|
||||
&[
|
||||
"stable_one",
|
||||
"new_one",
|
||||
#[cfg(feature = "enable-pezstaging-api")]
|
||||
"pezstaging_one",
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{
|
||||
panic::UnwindSafe,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use pezsc_block_builder::BlockBuilderBuilder;
|
||||
use pezsp_api::{ApiExt, Core, ProvideRuntimeApi};
|
||||
use pezsp_externalities::{decl_extension, TransactionType};
|
||||
use pezsp_runtime::{
|
||||
traits::{HashingFor, Header as HeaderT},
|
||||
TransactionOutcome,
|
||||
};
|
||||
use pezsp_state_machine::{create_proof_check_backend, execution_proof_check_on_trie_backend};
|
||||
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
prelude::*,
|
||||
runtime::{Block, Header, TestAPI, Transfer},
|
||||
DefaultTestClientBuilderExt, TestClient, TestClientBuilder,
|
||||
};
|
||||
|
||||
use codec::Encode;
|
||||
use pezsp_consensus::SelectChain;
|
||||
use bizinikiwi_test_runtime_client::pezsc_executor::WasmExecutor;
|
||||
|
||||
#[test]
|
||||
fn calling_runtime_function() {
|
||||
let client = TestClientBuilder::new().build();
|
||||
let runtime_api = client.runtime_api();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
|
||||
assert_eq!(runtime_api.benchmark_add_one(best_hash, &1).unwrap(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calling_native_runtime_signature_changed_function() {
|
||||
let client = TestClientBuilder::new().build();
|
||||
let runtime_api = client.runtime_api();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
|
||||
assert_eq!(runtime_api.function_signature_changed(best_hash).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_trie_function() {
|
||||
let client = TestClientBuilder::new().build();
|
||||
let runtime_api = client.runtime_api();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
assert_eq!(runtime_api.use_trie(best_hash).unwrap(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialize_block_works() {
|
||||
let client = TestClientBuilder::new().build();
|
||||
let runtime_api = client.runtime_api();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
runtime_api
|
||||
.initialize_block(
|
||||
best_hash,
|
||||
&Header::new(
|
||||
1,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(runtime_api.get_block_number(best_hash).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_proof_works() {
|
||||
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
|
||||
|
||||
let storage_root =
|
||||
*futures::executor::block_on(longest_chain.best_chain()).unwrap().state_root();
|
||||
|
||||
let runtime_code = pezsp_core::traits::RuntimeCode {
|
||||
code_fetcher: &pezsp_core::traits::WrappedRuntimeCode(
|
||||
client.code_at(client.chain_info().best_hash).unwrap().into(),
|
||||
),
|
||||
hash: vec![1],
|
||||
heap_pages: None,
|
||||
};
|
||||
|
||||
let transaction = Transfer {
|
||||
amount: 1000,
|
||||
nonce: 0,
|
||||
from: Sr25519Keyring::Alice.into(),
|
||||
to: Sr25519Keyring::Bob.into(),
|
||||
}
|
||||
.into_unchecked_extrinsic();
|
||||
|
||||
// Build the block and record proof
|
||||
let mut builder = BlockBuilderBuilder::new(&client)
|
||||
.on_parent_block(client.chain_info().best_hash)
|
||||
.with_parent_block_number(client.chain_info().best_number)
|
||||
.enable_proof_recording()
|
||||
.build()
|
||||
.unwrap();
|
||||
builder.push(transaction.clone()).unwrap();
|
||||
let (block, _, proof) = builder.build().expect("Bake block").into_inner();
|
||||
|
||||
let backend = create_proof_check_backend::<HashingFor<Block>>(
|
||||
storage_root,
|
||||
proof.expect("Proof was generated"),
|
||||
)
|
||||
.expect("Creates proof backend.");
|
||||
|
||||
// Use the proof backend to execute `execute_block`.
|
||||
let mut overlay = Default::default();
|
||||
let executor: WasmExecutor = WasmExecutor::builder().build();
|
||||
execution_proof_check_on_trie_backend(
|
||||
&backend,
|
||||
&mut overlay,
|
||||
&executor,
|
||||
"Core_execute_block",
|
||||
&block.encode(),
|
||||
&runtime_code,
|
||||
)
|
||||
.expect("Executes block while using the proof backend");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_runtime_api_with_multiple_arguments() {
|
||||
let client = TestClientBuilder::new().build();
|
||||
|
||||
let data = vec![1, 2, 4, 5, 6, 7, 8, 8, 10, 12];
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
client
|
||||
.runtime_api()
|
||||
.test_multiple_arguments(best_hash, data.clone(), data.clone(), data.len() as u32)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disable_logging_works() {
|
||||
if std::env::var("RUN_TEST").is_ok() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let mut builder = TestClientBuilder::new();
|
||||
builder.genesis_init_mut().set_wasm_code(
|
||||
bizinikiwi_test_runtime_client::runtime::wasm_binary_logging_disabled_unwrap().to_vec(),
|
||||
);
|
||||
|
||||
let client = builder.build();
|
||||
let runtime_api = client.runtime_api();
|
||||
runtime_api
|
||||
.do_trace_log(client.chain_info().genesis_hash)
|
||||
.expect("Logging should not fail");
|
||||
log::error!("Logging from native works");
|
||||
} else {
|
||||
let executable = std::env::current_exe().unwrap();
|
||||
let output = std::process::Command::new(executable)
|
||||
.env("RUN_TEST", "1")
|
||||
.env("RUST_LOG", "info")
|
||||
.args(&["--nocapture", "disable_logging_works"])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let output = dbg!(String::from_utf8(output.stderr).unwrap());
|
||||
assert!(!output.contains("Hey I'm runtime"));
|
||||
assert!(output.contains("Logging from native works"));
|
||||
}
|
||||
}
|
||||
|
||||
// Certain logic like the transaction handling is not unwind safe.
|
||||
//
|
||||
// Ensure that the type is not unwind safe!
|
||||
static_assertions::assert_not_impl_any!(<TestClient as ProvideRuntimeApi<_>>::Api: UnwindSafe);
|
||||
|
||||
#[derive(Default)]
|
||||
struct TransactionTesterInner {
|
||||
started: AtomicUsize,
|
||||
committed: AtomicUsize,
|
||||
rolled_back: AtomicUsize,
|
||||
}
|
||||
|
||||
decl_extension! {
|
||||
struct TransactionTester(Arc<TransactionTesterInner>);
|
||||
|
||||
impl TransactionTester {
|
||||
fn start_transaction(&mut self, ty: TransactionType) {
|
||||
assert_eq!(ty, TransactionType::Host);
|
||||
self.0.started.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn commit_transaction(&mut self, ty: TransactionType) {
|
||||
assert_eq!(ty, TransactionType::Host);
|
||||
self.0.committed.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn rollback_transaction(&mut self, ty: TransactionType) {
|
||||
assert_eq!(ty, TransactionType::Host);
|
||||
self.0.rolled_back.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_transactional_works() {
|
||||
const KEY: &[u8] = b"test";
|
||||
|
||||
let client = TestClientBuilder::new().build();
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
let transaction_tester = Arc::new(TransactionTesterInner::default());
|
||||
|
||||
let mut runtime_api = client.runtime_api();
|
||||
runtime_api.register_extension(TransactionTester(transaction_tester.clone()));
|
||||
runtime_api.execute_in_transaction(|api| {
|
||||
api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3], false).unwrap();
|
||||
|
||||
api.execute_in_transaction(|api| {
|
||||
api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3, 4], false).unwrap();
|
||||
|
||||
TransactionOutcome::Commit(())
|
||||
});
|
||||
|
||||
TransactionOutcome::Commit(())
|
||||
});
|
||||
|
||||
let changes = runtime_api
|
||||
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
|
||||
.unwrap();
|
||||
assert_eq!(changes.main_storage_changes[0].1, Some(vec![1, 2, 3, 4]));
|
||||
|
||||
let mut runtime_api = client.runtime_api();
|
||||
runtime_api.register_extension(TransactionTester(transaction_tester.clone()));
|
||||
runtime_api.execute_in_transaction(|api| {
|
||||
assert!(api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3], true).is_err());
|
||||
|
||||
TransactionOutcome::Commit(())
|
||||
});
|
||||
|
||||
let changes = runtime_api
|
||||
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
|
||||
.unwrap();
|
||||
assert_eq!(changes.main_storage_changes[0].1, Some(vec![1, 2, 3]));
|
||||
|
||||
let mut runtime_api = client.runtime_api();
|
||||
runtime_api.register_extension(TransactionTester(transaction_tester.clone()));
|
||||
runtime_api.execute_in_transaction(|api| {
|
||||
assert!(api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2], true).is_err());
|
||||
|
||||
TransactionOutcome::Rollback(())
|
||||
});
|
||||
|
||||
let changes = runtime_api
|
||||
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
|
||||
.unwrap();
|
||||
assert!(changes.main_storage_changes.is_empty());
|
||||
|
||||
assert_eq!(transaction_tester.started.load(Ordering::Relaxed), 4);
|
||||
assert_eq!(transaction_tester.committed.load(Ordering::Relaxed), 3);
|
||||
assert_eq!(transaction_tester.rolled_back.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[rustversion::attr(not(stable), ignore)]
|
||||
#[cfg(not(feature = "disable-ui-tests"))]
|
||||
#[test]
|
||||
fn ui() {
|
||||
// Only run the ui tests when `RUN_UI_TESTS` is set.
|
||||
if std::env::var("RUN_UI_TESTS").is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
|
||||
std::env::set_var("SKIP_WASM_BUILD", "1");
|
||||
|
||||
// Warnings are part of our UI.
|
||||
std::env::set_var("CARGO_ENCODED_RUSTFLAGS", "--deny=warnings");
|
||||
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
t.pass("tests/ui/positive_cases/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(&self);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `self` as argument not supported.
|
||||
--> tests/ui/adding_self_parameter.rs:20:11
|
||||
|
|
||||
20 | fn test(&self);
|
||||
| ^
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
pub trait Api {
|
||||
#[changed_in(2)]
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,6 @@
|
||||
error: There is no 'default' method with this name (without `changed_in` attribute).
|
||||
The 'default' method is used to call into the latest implementation.
|
||||
--> tests/ui/changed_in_no_default_method.rs:26:6
|
||||
|
|
||||
26 | fn test(data: u64);
|
||||
| ^^^^
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
#[changed_in(2)]
|
||||
fn test(data: u64);
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `changed_in` version can not be greater than the `api_version`
|
||||
--> tests/ui/changed_in_unknown_version.rs:25:3
|
||||
|
|
||||
25 | fn test(data: u64);
|
||||
| ^^
|
||||
@@ -0,0 +1,24 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api<Block: BlockT> {
|
||||
fn test();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: `Block: BlockT` generic parameter will be added automatically by the `decl_runtime_apis!` macro! If you try to use a different trait than the bizinikiwi `Block` trait, please rename it locally.
|
||||
--> tests/ui/declaring_old_block.rs:19:23
|
||||
|
|
||||
19 | pub trait Api<Block: BlockT> {
|
||||
| ^^^^^^
|
||||
|
||||
error: `Block: BlockT` generic parameter will be added automatically by the `decl_runtime_apis!` macro!
|
||||
--> tests/ui/declaring_old_block.rs:19:16
|
||||
|
|
||||
19 | pub trait Api<Block: BlockT> {
|
||||
| ^^^^^
|
||||
@@ -0,0 +1,24 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api<B: BlockT> {
|
||||
fn test();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `Block: BlockT` generic parameter will be added automatically by the `decl_runtime_apis!` macro! If you try to use a different trait than the bizinikiwi `Block` trait, please rename it locally.
|
||||
--> tests/ui/declaring_own_block_with_different_name.rs:19:19
|
||||
|
|
||||
19 | pub trait Api<B: BlockT> {
|
||||
| ^^^^^^
|
||||
@@ -0,0 +1,29 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
#[deprecated(unknown_kw = "test")]
|
||||
fn test();
|
||||
#[deprecated(since = 5)]
|
||||
fn test2();
|
||||
#[deprecated = 5]
|
||||
fn test3();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,43 @@
|
||||
error: Invalid deprecation attribute: missing `note`
|
||||
help: the following are the possible correct uses
|
||||
|
|
||||
| #[deprecated = "reason"]
|
||||
|
|
||||
| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
||||
|
|
||||
| #[deprecated]
|
||||
|
|
||||
--> tests/ui/deprecation_info.rs:20:3
|
||||
|
|
||||
20 | #[deprecated(unknown_kw = "test")]
|
||||
| ^
|
||||
|
||||
error: malformed `deprecated` attribute input
|
||||
--> tests/ui/deprecation_info.rs:24:3
|
||||
|
|
||||
24 | #[deprecated = 5]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: the following are the possible correct uses
|
||||
|
|
||||
24 - #[deprecated = 5]
|
||||
24 + #[deprecated = "reason"]
|
||||
|
|
||||
24 - #[deprecated = 5]
|
||||
24 + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
||||
|
|
||||
24 - #[deprecated = 5]
|
||||
24 + #[deprecated]
|
||||
|
|
||||
|
||||
error[E0541]: unknown meta item 'unknown_kw'
|
||||
--> tests/ui/deprecation_info.rs:20:16
|
||||
|
|
||||
20 | #[deprecated(unknown_kw = "test")]
|
||||
| ^^^^^^^^^^^^^^^^^^^ expected one of `since`, `note`
|
||||
|
||||
error[E0565]: literal in `deprecated` value must be a string
|
||||
--> tests/ui/deprecation_info.rs:22:24
|
||||
|
|
||||
22 | #[deprecated(since = 5)]
|
||||
| ^
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
error: No api implementation given!
|
||||
--> tests/ui/empty_impl_runtime_apis_call.rs:28:1
|
||||
|
|
||||
28 | sp_api::impl_runtime_apis! {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `sp_api::impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,49 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test(data: String) {}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_api::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,66 @@
|
||||
error[E0603]: struct `RuntimeVersion` is private
|
||||
--> tests/ui/impl_incorrect_method_signature.rs:37:27
|
||||
|
|
||||
37 | fn version() -> sp_api::RuntimeVersion {
|
||||
| ^^^^^^^^^^^^^^ private struct
|
||||
|
|
||||
note: the struct `RuntimeVersion` is defined here
|
||||
--> $WORKSPACE/bizinikiwi/primitives/api/src/lib.rs
|
||||
|
|
||||
| use sp_version::RuntimeVersion;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider importing this struct instead
|
||||
|
|
||||
37 - fn version() -> sp_api::RuntimeVersion {
|
||||
37 + fn version() -> sp_version::RuntimeVersion {
|
||||
|
|
||||
help: import `RuntimeVersion` directly
|
||||
|
|
||||
37 - fn version() -> sp_api::RuntimeVersion {
|
||||
37 + fn version() -> sp_version::RuntimeVersion {
|
||||
|
|
||||
|
||||
error[E0053]: method `test` has an incompatible type for trait
|
||||
--> tests/ui/impl_incorrect_method_signature.rs:33:17
|
||||
|
|
||||
33 | fn test(data: String) {}
|
||||
| ^^^^^^ expected `u64`, found `std::string::String`
|
||||
|
|
||||
note: type in trait
|
||||
--> tests/ui/impl_incorrect_method_signature.rs:27:17
|
||||
|
|
||||
27 | fn test(data: u64);
|
||||
| ^^^
|
||||
= note: expected signature `fn(u64)`
|
||||
found signature `fn(std::string::String)`
|
||||
help: change the parameter type to match the trait
|
||||
|
|
||||
33 - fn test(data: String) {}
|
||||
33 + fn test(data: u64) {}
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/ui/impl_incorrect_method_signature.rs:33:11
|
||||
|
|
||||
31 | / sp_api::impl_runtime_apis! {
|
||||
32 | | impl self::Api<Block> for Runtime {
|
||||
33 | | fn test(data: String) {}
|
||||
| | ^^^^ expected `u64`, found `String`
|
||||
... |
|
||||
47 | | }
|
||||
| |_- arguments to this function are incorrect
|
||||
|
|
||||
note: associated function defined here
|
||||
--> tests/ui/impl_incorrect_method_signature.rs:27:6
|
||||
|
|
||||
27 | fn test(data: u64);
|
||||
| ^^^^ ----
|
||||
|
||||
error: unused variable: `data`
|
||||
--> tests/ui/impl_incorrect_method_signature.rs:33:11
|
||||
|
|
||||
33 | fn test(data: String) {}
|
||||
| ^^^^ help: if this is intentional, prefix it with an underscore: `_data`
|
||||
|
|
||||
= note: `-D unused-variables` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(unused_variables)]`
|
||||
@@ -0,0 +1,54 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
pub trait Api {
|
||||
fn test1();
|
||||
fn test2();
|
||||
#[api_version(3)]
|
||||
fn test3();
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
#[api_version(4)]
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test1() {}
|
||||
fn test2() {}
|
||||
fn test3() {}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_version::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error[E0405]: cannot find trait `ApiV4` in module `self::runtime_decl_for_api`
|
||||
--> tests/ui/impl_missing_version.rs:35:13
|
||||
|
|
||||
25 | pub trait Api {
|
||||
| ------------- similarly named trait `ApiV2` defined here
|
||||
...
|
||||
35 | impl self::Api<Block> for Runtime {
|
||||
| ^^^ help: a trait with a similar name exists: `ApiV2`
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
mod second {
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test2(data: u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test(data: u64) {}
|
||||
}
|
||||
|
||||
impl second::Api<Block> for Runtime {
|
||||
fn test2(data: u64) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Two traits with the same name detected! The trait name is used to generate its ID. Please rename one trait at the declaration!
|
||||
--> tests/ui/impl_two_traits_with_same_name.rs:42:15
|
||||
|
|
||||
42 | impl second::Api<Block> for Runtime {
|
||||
| ^^^
|
||||
|
||||
error: First trait implementation.
|
||||
--> tests/ui/impl_two_traits_with_same_name.rs:38:13
|
||||
|
|
||||
38 | impl self::Api<Block> for Runtime {
|
||||
| ^^^
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version]
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
|
||||
--> tests/ui/invalid_api_version_1.rs:19:2
|
||||
|
|
||||
19 | #[api_version]
|
||||
| ^
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version("1")]
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
|
||||
--> tests/ui/invalid_api_version_2.rs:19:2
|
||||
|
|
||||
19 | #[api_version("1")]
|
||||
| ^
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version()]
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
|
||||
--> tests/ui/invalid_api_version_3.rs:19:2
|
||||
|
|
||||
19 | #[api_version()]
|
||||
| ^
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
#[api_version("1")]
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
|
||||
--> tests/ui/invalid_api_version_4.rs:20:3
|
||||
|
|
||||
20 | #[api_version("1")]
|
||||
| ^
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
pub trait Api {
|
||||
#[api_version(1)]
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Method version `1` is older than (or equal to) trait version `2`.Methods can't define versions older than the trait version.
|
||||
--> tests/ui/method_ver_lower_than_trait_ver.rs:21:3
|
||||
|
|
||||
21 | #[api_version(1)]
|
||||
| ^
|
||||
|
||||
error: Trait version is set here.
|
||||
--> tests/ui/method_ver_lower_than_trait_ver.rs:19:2
|
||||
|
|
||||
19 | #[api_version(2)]
|
||||
| ^
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
impl self::Api for Runtime {
|
||||
fn test(data: u64) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Missing `Block` generic parameter.
|
||||
--> tests/ui/missing_block_generic_parameter.rs:29:13
|
||||
|
|
||||
29 | impl self::Api for Runtime {
|
||||
| ^^^
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
impl Api<Block> for Runtime {
|
||||
fn test(data: u64) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: The implemented trait has to be referenced with a path, e.g. `impl client::Core for Runtime`.
|
||||
--> tests/ui/missing_path_for_trait.rs:29:7
|
||||
|
|
||||
29 | impl Api<Block> for Runtime {
|
||||
| ^^^
|
||||
@@ -0,0 +1,53 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
pub trait Api {
|
||||
fn test1();
|
||||
fn test2();
|
||||
#[api_version(3)]
|
||||
fn test3();
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
#[api_version(3)]
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test1() {}
|
||||
fn test2() {}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_version::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error[E0046]: not all trait items implemented, missing: `test3`
|
||||
--> tests/ui/missing_versioned_method.rs:35:2
|
||||
|
|
||||
29 | fn test3();
|
||||
| ----------- `test3` from trait
|
||||
...
|
||||
35 | impl self::Api<Block> for Runtime {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `test3` in implementation
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
pub trait Api {
|
||||
fn test1();
|
||||
fn test2();
|
||||
#[api_version(3)]
|
||||
fn test3();
|
||||
#[api_version(4)]
|
||||
fn test4();
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
#[api_version(4)]
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test1() {}
|
||||
fn test2() {}
|
||||
fn test4() {}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_version::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error[E0046]: not all trait items implemented, missing: `test3`
|
||||
--> tests/ui/missing_versioned_method_multiple_vers.rs:37:2
|
||||
|
|
||||
29 | fn test3();
|
||||
| ----------- `test3` from trait
|
||||
...
|
||||
37 | impl self::Api<Block> for Runtime {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `test3` in implementation
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
use pezsp_api::ApiError;
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test();
|
||||
}
|
||||
}
|
||||
|
||||
struct MockApi;
|
||||
|
||||
pezsp_api::mock_impl_runtime_apis! {
|
||||
impl Api<Block> for MockApi {
|
||||
#[advanced]
|
||||
fn test(&self, _: &Hash) -> Result<(), ApiError> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
error: `Hash` needs to be taken by value and not by reference!
|
||||
--> tests/ui/mock_advanced_hash_by_reference.rs:29:1
|
||||
|
|
||||
29 | / sp_api::mock_impl_runtime_apis! {
|
||||
30 | | impl Api<Block> for MockApi {
|
||||
31 | | #[advanced]
|
||||
32 | | fn test(&self, _: &Hash) -> Result<(), ApiError> {
|
||||
... |
|
||||
36 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
use pezsp_api::ApiError;
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test();
|
||||
}
|
||||
}
|
||||
|
||||
struct MockApi;
|
||||
|
||||
pezsp_api::mock_impl_runtime_apis! {
|
||||
impl Api<Block> for MockApi {
|
||||
#[advanced]
|
||||
fn test(&self) -> Result<(), ApiError> {
|
||||
Ok(().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: If using the `advanced` attribute, it is required that the function takes at least one argument, the `Hash`.
|
||||
--> tests/ui/mock_advanced_missing_hash.rs:32:3
|
||||
|
|
||||
32 | fn test(&self) -> Result<(), ApiError> {
|
||||
| ^^
|
||||
@@ -0,0 +1,42 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
struct Block2;
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
|
||||
pub trait Api2 {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
struct MockApi;
|
||||
|
||||
pezsp_api::mock_impl_runtime_apis! {
|
||||
impl Api<Block> for MockApi {
|
||||
fn test(data: u64) {}
|
||||
}
|
||||
|
||||
impl Api2<Block2> for MockApi {
|
||||
fn test(data: u64) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Block type should be the same between all runtime apis.
|
||||
--> tests/ui/mock_only_one_block_type.rs:37:12
|
||||
|
|
||||
37 | impl Api2<Block2> for MockApi {
|
||||
| ^^^^^^
|
||||
|
||||
error: First block type found here
|
||||
--> tests/ui/mock_only_one_block_type.rs:33:11
|
||||
|
|
||||
33 | impl Api<Block> for MockApi {
|
||||
| ^^^^^
|
||||
@@ -0,0 +1,41 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
|
||||
pub trait Api2 {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
struct MockApi;
|
||||
struct MockApi2;
|
||||
|
||||
pezsp_api::mock_impl_runtime_apis! {
|
||||
impl Api<Block> for MockApi {
|
||||
fn test(data: u64) {}
|
||||
}
|
||||
|
||||
impl Api2<Block> for MockApi2 {
|
||||
fn test(data: u64) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Self type should not change between runtime apis
|
||||
--> tests/ui/mock_only_one_self_type.rs:36:23
|
||||
|
|
||||
36 | impl Api2<Block> for MockApi2 {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: First self type found here
|
||||
--> tests/ui/mock_only_one_self_type.rs:32:22
|
||||
|
|
||||
32 | impl Api<Block> for MockApi {
|
||||
| ^^^^^^^
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
fn test2(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
struct MockApi;
|
||||
|
||||
pezsp_api::mock_impl_runtime_apis! {
|
||||
impl Api<Block> for MockApi {
|
||||
fn test(self, data: u64) {}
|
||||
|
||||
fn test2(&mut self, data: u64) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,46 @@
|
||||
error: Only `&self` is supported!
|
||||
--> tests/ui/mock_only_self_reference.rs:31:11
|
||||
|
|
||||
31 | fn test(self, data: u64) {}
|
||||
| ^^^^
|
||||
|
||||
error: Only `&self` is supported!
|
||||
--> tests/ui/mock_only_self_reference.rs:33:12
|
||||
|
|
||||
33 | fn test2(&mut self, data: u64) {}
|
||||
| ^
|
||||
|
||||
error[E0050]: method `test` has 2 parameters but the declaration in trait `Api::test` has 3
|
||||
--> tests/ui/mock_only_self_reference.rs:29:1
|
||||
|
|
||||
20 | / sp_api::decl_runtime_apis! {
|
||||
21 | | pub trait Api {
|
||||
22 | | fn test(data: u64);
|
||||
| |_________________________- trait requires 3 parameters
|
||||
...
|
||||
29 | / sp_api::mock_impl_runtime_apis! {
|
||||
30 | | impl Api<Block> for MockApi {
|
||||
31 | | fn test(self, data: u64) {}
|
||||
... |
|
||||
35 | | }
|
||||
| |_^ expected 3 parameters, found 2
|
||||
|
|
||||
= note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0050]: method `test2` has 2 parameters but the declaration in trait `Api::test2` has 3
|
||||
--> tests/ui/mock_only_self_reference.rs:29:1
|
||||
|
|
||||
20 | / sp_api::decl_runtime_apis! {
|
||||
21 | | pub trait Api {
|
||||
22 | | fn test(data: u64);
|
||||
23 | | fn test2(data: u64);
|
||||
| |__________________________- trait requires 3 parameters
|
||||
...
|
||||
29 | / sp_api::mock_impl_runtime_apis! {
|
||||
30 | | impl Api<Block> for MockApi {
|
||||
31 | | fn test(self, data: u64) {}
|
||||
... |
|
||||
35 | | }
|
||||
| |_^ expected 3 parameters, found 2
|
||||
|
|
||||
= note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test() {
|
||||
println!("Hey, I'm a default implementation!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error: A runtime API function cannot have a default implementation!
|
||||
--> tests/ui/no_default_implementation.rs:20:13
|
||||
|
|
||||
20 | fn test() {
|
||||
| ___________________^
|
||||
21 | | println!("Hey, I'm a default implementation!");
|
||||
22 | | }
|
||||
| |_________^
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use scale_info::TypeInfo;
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
struct Runtime {}
|
||||
|
||||
pub trait CustomTrait: Encode + Decode + TypeInfo {}
|
||||
|
||||
#[derive(Encode, Decode, TypeInfo)]
|
||||
pub struct SomeImpl;
|
||||
impl CustomTrait for SomeImpl {}
|
||||
|
||||
#[derive(Encode, Decode, TypeInfo)]
|
||||
pub struct SomeOtherType<C: CustomTrait>(C);
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api<A> where A: CustomTrait {
|
||||
fn test() -> A;
|
||||
fn test2() -> SomeOtherType<A>;
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
impl self::Api<Block, SomeImpl> for Runtime {
|
||||
fn test() -> SomeImpl { SomeImpl }
|
||||
fn test2() -> SomeOtherType<SomeImpl> { SomeOtherType(SomeImpl) }
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_version::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
pub trait Api {
|
||||
fn test1();
|
||||
fn test2();
|
||||
#[api_version(3)]
|
||||
fn test3();
|
||||
#[api_version(4)]
|
||||
fn test4();
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
#[api_version(2)]
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test1() {}
|
||||
fn test2() {}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_version::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,51 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezsp_runtime::traits::Block as BlockT;
|
||||
use bizinikiwi_test_runtime_client::runtime::Block;
|
||||
|
||||
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
|
||||
/// runtime.
|
||||
struct Runtime {}
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait Api {
|
||||
fn test(data: u64);
|
||||
}
|
||||
}
|
||||
|
||||
pezsp_api::impl_runtime_apis! {
|
||||
impl self::Api<Block> for Runtime {
|
||||
fn test(data: &u64) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl pezsp_api::Core<Block> for Runtime {
|
||||
fn version() -> pezsp_api::RuntimeVersion {
|
||||
unimplemented!()
|
||||
}
|
||||
fn execute_block(_: <Block as BlockT>::LazyBlock) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn initialize_block(_: &<Block as BlockT>::Header) -> pezsp_runtime::ExtrinsicInclusionMode {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
error[E0603]: struct `RuntimeVersion` is private
|
||||
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:39:27
|
||||
|
|
||||
39 | fn version() -> sp_api::RuntimeVersion {
|
||||
| ^^^^^^^^^^^^^^ private struct
|
||||
|
|
||||
note: the struct `RuntimeVersion` is defined here
|
||||
--> $WORKSPACE/bizinikiwi/primitives/api/src/lib.rs
|
||||
|
|
||||
| use sp_version::RuntimeVersion;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider importing this struct instead
|
||||
|
|
||||
39 - fn version() -> sp_api::RuntimeVersion {
|
||||
39 + fn version() -> sp_version::RuntimeVersion {
|
||||
|
|
||||
help: import `RuntimeVersion` directly
|
||||
|
|
||||
39 - fn version() -> sp_api::RuntimeVersion {
|
||||
39 + fn version() -> sp_version::RuntimeVersion {
|
||||
|
|
||||
|
||||
error[E0053]: method `test` has an incompatible type for trait
|
||||
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:17
|
||||
|
|
||||
33 | fn test(data: &u64) {
|
||||
| ^^^^ expected `u64`, found `&u64`
|
||||
|
|
||||
note: type in trait
|
||||
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:27:17
|
||||
|
|
||||
27 | fn test(data: u64);
|
||||
| ^^^
|
||||
= note: expected signature `fn(_)`
|
||||
found signature `fn(&_)`
|
||||
help: change the parameter type to match the trait
|
||||
|
|
||||
33 - fn test(data: &u64) {
|
||||
33 + fn test(data: u64) {
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:11
|
||||
|
|
||||
31 | / sp_api::impl_runtime_apis! {
|
||||
32 | | impl self::Api<Block> for Runtime {
|
||||
33 | | fn test(data: &u64) {
|
||||
| | ^^^^^^^ expected `u64`, found `&u64`
|
||||
34 | | unimplemented!()
|
||||
... |
|
||||
49 | | }
|
||||
| |_- arguments to this function are incorrect
|
||||
|
|
||||
note: associated function defined here
|
||||
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:27:6
|
||||
|
|
||||
27 | fn test(data: u64);
|
||||
| ^^^^ ----
|
||||
|
||||
error: unused variable: `data`
|
||||
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:11
|
||||
|
|
||||
33 | fn test(data: &u64) {
|
||||
| ^^^^ help: if this is intentional, prefix it with an underscore: `_data`
|
||||
|
|
||||
= note: `-D unused-variables` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(unused_variables)]`
|
||||
@@ -0,0 +1,62 @@
|
||||
[package]
|
||||
name = "pezsp-application-crypto"
|
||||
version = "30.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Provides facilities for generating application specific crypto wrapper types."
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
documentation = "https://docs.rs/pezsp-application-crypto"
|
||||
readme = "README.md"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
serde = { optional = true, features = ["alloc", "derive"], workspace = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-io = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"full_crypto",
|
||||
"scale-info/std",
|
||||
"serde/std",
|
||||
"pezsp-core/std",
|
||||
"pezsp-io/std",
|
||||
]
|
||||
|
||||
# Serde support without relying on std features.
|
||||
serde = ["dep:serde", "scale-info/serde", "pezsp-core/serde"]
|
||||
|
||||
# This feature enables all crypto primitives for `no_std` builds like microcontrollers
|
||||
# or Intel SGX.
|
||||
# For the regular wasm runtime builds this should not be used.
|
||||
full_crypto = [
|
||||
"pezsp-core/full_crypto",
|
||||
"pezsp-io/disable_oom",
|
||||
# Don't add `panic_handler` and `alloc_error_handler` since they are expected to be provided
|
||||
# by the user anyway.
|
||||
"pezsp-io/disable_panic_handler",
|
||||
]
|
||||
|
||||
# This feature adds BLS crypto primitives.
|
||||
# It should not be used in production since the implementation and interface may still
|
||||
# be subject to significant changes.
|
||||
bls-experimental = ["pezsp-core/bls-experimental", "pezsp-io/bls-experimental"]
|
||||
|
||||
# This feature adds Bandersnatch crypto primitives.
|
||||
# It should not be used in production since the implementation and interface may still
|
||||
# be subject to significant changes.
|
||||
bandersnatch-experimental = [
|
||||
"pezsp-core/bandersnatch-experimental",
|
||||
"pezsp-io/bandersnatch-experimental",
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
Traits and macros for constructing application specific strongly typed crypto wrappers.
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env -S bash -eux
|
||||
|
||||
export RUSTFLAGS="-Cdebug-assertions=y -Dwarnings"
|
||||
cargo check --release
|
||||
|
||||
export RUSTFLAGS="$RUSTFLAGS --cfg bizinikiwi_runtime"
|
||||
T=wasm32-unknown-unknown
|
||||
cargo check --release --target=$T --no-default-features
|
||||
cargo check --release --target=$T --no-default-features --features="full_crypto"
|
||||
cargo check --release --target=$T --no-default-features --features="serde"
|
||||
cargo check --release --target=$T --no-default-features --features="serde,full_crypto"
|
||||
cargo check --release --target=$T --no-default-features --features="bandersnatch-experimental"
|
||||
cargo check --release --target=$T --no-default-features --features="bls-experimental"
|
||||
cargo check --release --target=$T --no-default-features --features="bls-experimental,full_crypto"
|
||||
@@ -0,0 +1,88 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Bandersnatch VRF application crypto types.
|
||||
|
||||
use crate::{KeyTypeId, RuntimePublic};
|
||||
use alloc::vec::Vec;
|
||||
pub use pezsp_core::bandersnatch::*;
|
||||
|
||||
use pezsp_core::{
|
||||
crypto::CryptoType,
|
||||
proof_of_possession::{NonAggregatable, ProofOfPossessionVerifier},
|
||||
Pair as TraitPair,
|
||||
};
|
||||
|
||||
mod app {
|
||||
crate::app_crypto!(super, pezsp_core::testing::BANDERSNATCH);
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub use app::Pair as AppPair;
|
||||
pub use app::{
|
||||
ProofOfPossession as AppProofOfPossession, Public as AppPublic, Signature as AppSignature,
|
||||
};
|
||||
|
||||
impl RuntimePublic for Public {
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = Signature;
|
||||
|
||||
/// Dummy implementation. Returns an empty vector.
|
||||
fn all(_key_type: KeyTypeId) -> Vec<Self> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
|
||||
pezsp_io::crypto::bandersnatch_generate(key_type, seed)
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
|
||||
pezsp_io::crypto::bandersnatch_sign(key_type, self, msg.as_ref())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
let sig = AppSignature::from(*signature);
|
||||
let pub_key = AppPublic::from(*self);
|
||||
<AppPublic as CryptoType>::Pair::verify(&sig, msg.as_ref(), &pub_key)
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::ProofOfPossession> {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::bandersnatch_sign(key_type, self, &proof_of_possession_statement)
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(
|
||||
&self,
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::Signature,
|
||||
) -> bool {
|
||||
let pub_key = AppPublic::from(*self);
|
||||
<AppPublic as CryptoType>::Pair::verify_proof_of_possession(
|
||||
owner,
|
||||
&proof_of_possession,
|
||||
&pub_key,
|
||||
)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
pezsp_core::crypto::ByteArray::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! BLS12-381 crypto applications.
|
||||
use crate::{KeyTypeId, RuntimePublic};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub use pezsp_core::bls::{
|
||||
bls381::{BlsEngine as Bls381Engine, *},
|
||||
Pair as BLS_Pair, ProofOfPossession as BLSPoP,
|
||||
};
|
||||
use pezsp_core::{crypto::CryptoType, proof_of_possession::ProofOfPossessionVerifier};
|
||||
|
||||
mod app {
|
||||
crate::app_crypto!(super, pezsp_core::testing::BLS381);
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub use app::Pair as AppPair;
|
||||
pub use app::{
|
||||
ProofOfPossession as AppProofOfPossession, Public as AppPublic, Signature as AppSignature,
|
||||
};
|
||||
|
||||
impl RuntimePublic for Public {
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
|
||||
/// Dummy implementation. Returns an empty vector.
|
||||
fn all(_key_type: KeyTypeId) -> Vec<Self> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
|
||||
pezsp_io::crypto::bls381_generate(key_type, seed)
|
||||
}
|
||||
|
||||
/// Dummy implementation. Returns `None`.
|
||||
fn sign<M: AsRef<[u8]>>(&self, _key_type: KeyTypeId, _msg: &M) -> Option<Self::Signature> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Dummy implementation. Returns `false`.
|
||||
fn verify<M: AsRef<[u8]>>(&self, _msg: &M, _signature: &Self::Signature) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::ProofOfPossession> {
|
||||
pezsp_io::crypto::bls381_generate_proof_of_possession(key_type, self, owner)
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(
|
||||
&self,
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::ProofOfPossession,
|
||||
) -> bool {
|
||||
let pub_key = AppPublic::from(*self);
|
||||
<AppPublic as CryptoType>::Pair::verify_proof_of_possession(
|
||||
owner,
|
||||
&proof_of_possession,
|
||||
&pub_key,
|
||||
)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
pezsp_core::crypto::ByteArray::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Ecdsa crypto types.
|
||||
|
||||
use crate::{KeyTypeId, RuntimePublic};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub use pezsp_core::ecdsa::*;
|
||||
use pezsp_core::proof_of_possession::NonAggregatable;
|
||||
|
||||
mod app {
|
||||
crate::app_crypto!(super, pezsp_core::testing::ECDSA);
|
||||
}
|
||||
|
||||
pub use app::{
|
||||
Pair as AppPair, ProofOfPossession as AppProofOfPossession, Public as AppPublic,
|
||||
Signature as AppSignature,
|
||||
};
|
||||
|
||||
impl RuntimePublic for Public {
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = Signature;
|
||||
|
||||
fn all(key_type: KeyTypeId) -> crate::Vec<Self> {
|
||||
pezsp_io::crypto::ecdsa_public_keys(key_type)
|
||||
}
|
||||
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
|
||||
pezsp_io::crypto::ecdsa_generate(key_type, seed)
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
|
||||
pezsp_io::crypto::ecdsa_sign(key_type, self, msg.as_ref())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
pezsp_io::crypto::ecdsa_verify(signature, msg.as_ref(), self)
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::Signature> {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::ecdsa_sign(key_type, self, &proof_of_possession_statement)
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(
|
||||
&self,
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::Signature,
|
||||
) -> bool {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::ecdsa_verify(&proof_of_possession, &proof_of_possession_statement, &self)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
pezsp_core::crypto::ByteArray::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! ECDSA and BLS12-381 paired crypto applications.
|
||||
|
||||
use crate::{KeyTypeId, RuntimePublic};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub use pezsp_core::paired_crypto::ecdsa_bls381::*;
|
||||
use pezsp_core::{
|
||||
bls381,
|
||||
crypto::CryptoType,
|
||||
ecdsa, ecdsa_bls381,
|
||||
proof_of_possession::{NonAggregatable, ProofOfPossessionVerifier},
|
||||
};
|
||||
|
||||
mod app {
|
||||
crate::app_crypto!(super, pezsp_core::testing::ECDSA_BLS381);
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub use app::Pair as AppPair;
|
||||
pub use app::{
|
||||
ProofOfPossession as AppProofOfPossession, Public as AppPublic, Signature as AppSignature,
|
||||
};
|
||||
|
||||
impl RuntimePublic for Public {
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ecdsa_bls381::ProofOfPossession;
|
||||
|
||||
/// Dummy implementation. Returns an empty vector.
|
||||
fn all(_key_type: KeyTypeId) -> Vec<Self> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
|
||||
pezsp_io::crypto::ecdsa_bls381_generate(key_type, seed)
|
||||
}
|
||||
|
||||
/// Dummy implementation. Returns `None`.
|
||||
fn sign<M: AsRef<[u8]>>(&self, _key_type: KeyTypeId, _msg: &M) -> Option<Self::Signature> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Dummy implementation. Returns `false`.
|
||||
fn verify<M: AsRef<[u8]>>(&self, _msg: &M, _signature: &Self::Signature) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::ProofOfPossession> {
|
||||
let pub_key_as_bytes = self.to_raw_vec();
|
||||
let (ecdsa_pub_as_bytes, bls381_pub_as_bytes) = split_pub_key_bytes(&pub_key_as_bytes)?;
|
||||
let ecdsa_proof_of_possession =
|
||||
generate_ecdsa_proof_of_possession(key_type, ecdsa_pub_as_bytes, owner)?;
|
||||
let bls381_proof_of_possession =
|
||||
generate_bls381_proof_of_possession(key_type, bls381_pub_as_bytes, owner)?;
|
||||
let combined_proof_of_possession_raw =
|
||||
combine_proof_of_possession(&ecdsa_proof_of_possession, &bls381_proof_of_possession)?;
|
||||
Some(Self::ProofOfPossession::from_raw(combined_proof_of_possession_raw))
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(
|
||||
&self,
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::ProofOfPossession,
|
||||
) -> bool {
|
||||
let pub_key = AppPublic::from(*self);
|
||||
<AppPublic as CryptoType>::Pair::verify_proof_of_possession(
|
||||
owner,
|
||||
&proof_of_possession,
|
||||
&pub_key,
|
||||
)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
pezsp_core::crypto::ByteArray::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper: Split public key bytes into ECDSA and BLS381 parts
|
||||
fn split_pub_key_bytes(
|
||||
pub_key_as_bytes: &[u8],
|
||||
) -> Option<([u8; ecdsa::PUBLIC_KEY_SERIALIZED_SIZE], [u8; bls381::PUBLIC_KEY_SERIALIZED_SIZE])> {
|
||||
let ecdsa_pub_as_bytes =
|
||||
pub_key_as_bytes[..ecdsa::PUBLIC_KEY_SERIALIZED_SIZE].try_into().ok()?;
|
||||
let bls381_pub_as_bytes =
|
||||
pub_key_as_bytes[ecdsa::PUBLIC_KEY_SERIALIZED_SIZE..].try_into().ok()?;
|
||||
Some((ecdsa_pub_as_bytes, bls381_pub_as_bytes))
|
||||
}
|
||||
|
||||
/// Helper: Generate ECDSA proof of possession
|
||||
fn generate_ecdsa_proof_of_possession(
|
||||
key_type: KeyTypeId,
|
||||
ecdsa_pub_as_bytes: [u8; ecdsa::PUBLIC_KEY_SERIALIZED_SIZE],
|
||||
owner: &[u8],
|
||||
) -> Option<ecdsa::Signature> {
|
||||
let ecdsa_pub = ecdsa::Public::from_raw(ecdsa_pub_as_bytes);
|
||||
let proof_of_possession_statement = ecdsa::Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::ecdsa_sign(key_type, &ecdsa_pub, &proof_of_possession_statement)
|
||||
}
|
||||
|
||||
/// Helper: Generate BLS381 proof of possession
|
||||
fn generate_bls381_proof_of_possession(
|
||||
key_type: KeyTypeId,
|
||||
bls381_pub_as_bytes: [u8; bls381::PUBLIC_KEY_SERIALIZED_SIZE],
|
||||
owner: &[u8],
|
||||
) -> Option<bls381::ProofOfPossession> {
|
||||
let bls381_pub = bls381::Public::from_raw(bls381_pub_as_bytes);
|
||||
pezsp_io::crypto::bls381_generate_proof_of_possession(key_type, &bls381_pub, owner)
|
||||
}
|
||||
|
||||
/// Helper: Combine ECDSA and BLS381 proof_of_possessions into a single raw proof_of_possession
|
||||
fn combine_proof_of_possession(
|
||||
ecdsa_proof_of_possession: &ecdsa::Signature,
|
||||
bls381_proof_of_possession: &bls381::ProofOfPossession,
|
||||
) -> Option<[u8; ecdsa_bls381::POP_LEN]> {
|
||||
let mut combined_proof_of_possession_raw = [0u8; ecdsa_bls381::POP_LEN];
|
||||
combined_proof_of_possession_raw[..ecdsa::SIGNATURE_SERIALIZED_SIZE]
|
||||
.copy_from_slice(ecdsa_proof_of_possession.as_ref());
|
||||
combined_proof_of_possession_raw[ecdsa::SIGNATURE_SERIALIZED_SIZE..]
|
||||
.copy_from_slice(bls381_proof_of_possession.as_ref());
|
||||
Some(combined_proof_of_possession_raw)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pezsp_core::{bls381, crypto::Pair, ecdsa};
|
||||
|
||||
/// Helper function to generate test public keys for ECDSA and BLS381
|
||||
fn generate_test_keys(
|
||||
) -> ([u8; ecdsa::PUBLIC_KEY_SERIALIZED_SIZE], [u8; bls381::PUBLIC_KEY_SERIALIZED_SIZE]) {
|
||||
let ecdsa_pair = ecdsa::Pair::generate().0;
|
||||
let bls381_pair = bls381::Pair::generate().0;
|
||||
|
||||
let ecdsa_pub = ecdsa_pair.public();
|
||||
let bls381_pub = bls381_pair.public();
|
||||
|
||||
(ecdsa_pub.to_raw_vec().try_into().unwrap(), bls381_pub.to_raw_vec().try_into().unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_pub_key_bytes() {
|
||||
let (ecdsa_pub, bls381_pub) = generate_test_keys();
|
||||
let mut combined_pub_key = Vec::new();
|
||||
combined_pub_key.extend_from_slice(&ecdsa_pub);
|
||||
combined_pub_key.extend_from_slice(&bls381_pub);
|
||||
|
||||
let result = split_pub_key_bytes(&combined_pub_key).unwrap();
|
||||
assert_eq!(result.0, ecdsa_pub, "ECDSA public key does not match");
|
||||
assert_eq!(result.1, bls381_pub, "BLS381 public key does not match");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Ed25519 crypto types.
|
||||
|
||||
use crate::{KeyTypeId, RuntimePublic};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use pezsp_core::proof_of_possession::NonAggregatable;
|
||||
pub use pezsp_core::{
|
||||
crypto::{CryptoBytes, SignatureBytes},
|
||||
ed25519::*,
|
||||
};
|
||||
|
||||
mod app {
|
||||
crate::app_crypto!(super, pezsp_core::testing::ED25519);
|
||||
}
|
||||
|
||||
pub use app::{
|
||||
Pair as AppPair, ProofOfPossession as AppProofOfPossession, Public as AppPublic,
|
||||
Signature as AppSignature,
|
||||
};
|
||||
|
||||
impl RuntimePublic for Public {
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = Signature;
|
||||
|
||||
fn all(key_type: KeyTypeId) -> crate::Vec<Self> {
|
||||
pezsp_io::crypto::ed25519_public_keys(key_type)
|
||||
}
|
||||
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
|
||||
pezsp_io::crypto::ed25519_generate(key_type, seed)
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
|
||||
pezsp_io::crypto::ed25519_sign(key_type, self, msg.as_ref())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
pezsp_io::crypto::ed25519_verify(signature, msg.as_ref(), self)
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::ProofOfPossession> {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::ed25519_sign(key_type, self, &proof_of_possession_statement)
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(
|
||||
&self,
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::ProofOfPossession,
|
||||
) -> bool {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::ed25519_verify(&proof_of_possession, &proof_of_possession_statement, &self)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
pezsp_core::crypto::ByteArray::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,791 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Traits and macros for constructing application specific strongly typed crypto wrappers.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub use pezsp_core::crypto::{key_types, CryptoTypeId, DeriveJunction, KeyTypeId, Ss58Codec};
|
||||
#[doc(hidden)]
|
||||
pub use pezsp_core::crypto::{DeriveError, Pair, SecretStringError};
|
||||
#[doc(hidden)]
|
||||
pub use pezsp_core::{
|
||||
self,
|
||||
crypto::{ByteArray, CryptoType, Derive, IsWrappedBy, Public, Signature, UncheckedFrom, Wraps},
|
||||
proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
|
||||
RuntimeDebug,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use alloc::vec::Vec;
|
||||
#[doc(hidden)]
|
||||
pub use codec;
|
||||
#[doc(hidden)]
|
||||
pub use core::ops::Deref;
|
||||
#[doc(hidden)]
|
||||
pub use scale_info;
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde;
|
||||
|
||||
#[cfg(feature = "bandersnatch-experimental")]
|
||||
pub mod bandersnatch;
|
||||
#[cfg(feature = "bls-experimental")]
|
||||
pub mod bls381;
|
||||
pub mod ecdsa;
|
||||
#[cfg(feature = "bls-experimental")]
|
||||
pub mod ecdsa_bls381;
|
||||
pub mod ed25519;
|
||||
pub mod sr25519;
|
||||
mod traits;
|
||||
|
||||
pub use traits::*;
|
||||
|
||||
/// Declares `Public`, `Pair`, `Signature` and `ProofOfPossession` types which are functionally
|
||||
/// equivalent to the corresponding types defined by `$module` but are new application-specific
|
||||
/// types whose identifier is `$key_type`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pezsp_application_crypto::{app_crypto, ed25519, KeyTypeId};
|
||||
/// // Declare a new set of crypto types using ed25519 logic that identifies as `KeyTypeId`
|
||||
/// // of value `b"fuba"`.
|
||||
/// app_crypto!(ed25519, KeyTypeId(*b"fuba"));
|
||||
/// ```
|
||||
#[cfg(feature = "full_crypto")]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto {
|
||||
($module:ident, $key_type:expr) => {
|
||||
$crate::app_crypto_public_full_crypto!($module::Public, $key_type, $module::CRYPTO_ID);
|
||||
$crate::app_crypto_public_common!(
|
||||
$module::Public,
|
||||
$module::Signature,
|
||||
$key_type,
|
||||
$module::CRYPTO_ID
|
||||
);
|
||||
$crate::app_crypto_signature_full_crypto!(
|
||||
$module::Signature,
|
||||
$key_type,
|
||||
$module::CRYPTO_ID
|
||||
);
|
||||
$crate::app_crypto_signature_common!($module::Signature, $key_type);
|
||||
$crate::app_crypto_proof_of_possession_full_crypto!(
|
||||
$module::ProofOfPossession,
|
||||
$key_type,
|
||||
$module::CRYPTO_ID
|
||||
);
|
||||
$crate::app_crypto_proof_of_possession_common!($module::ProofOfPossession, $key_type);
|
||||
$crate::app_crypto_pair_common!($module::Pair, $key_type, $module::CRYPTO_ID);
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `Public`, `Pair` and `Signature` types which are functionally equivalent
|
||||
/// to the corresponding types defined by `$module` but that are new application-specific
|
||||
/// types whose identifier is `$key_type`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pezsp_application_crypto::{app_crypto, ed25519, KeyTypeId};
|
||||
/// // Declare a new set of crypto types using ed25519 logic that identifies as `KeyTypeId`
|
||||
/// // of value `b"fuba"`.
|
||||
/// app_crypto!(ed25519, KeyTypeId(*b"fuba"));
|
||||
/// ```
|
||||
#[cfg(not(feature = "full_crypto"))]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto {
|
||||
($module:ident, $key_type:expr) => {
|
||||
$crate::app_crypto_public_not_full_crypto!($module::Public, $key_type, $module::CRYPTO_ID);
|
||||
$crate::app_crypto_public_common!(
|
||||
$module::Public,
|
||||
$module::Signature,
|
||||
$key_type,
|
||||
$module::CRYPTO_ID
|
||||
);
|
||||
$crate::app_crypto_signature_not_full_crypto!(
|
||||
$module::Signature,
|
||||
$key_type,
|
||||
$module::CRYPTO_ID
|
||||
);
|
||||
$crate::app_crypto_signature_common!($module::Signature, $key_type);
|
||||
$crate::app_crypto_proof_of_possession_not_full_crypto!(
|
||||
$module::ProofOfPossession,
|
||||
$key_type,
|
||||
$module::CRYPTO_ID
|
||||
);
|
||||
$crate::app_crypto_proof_of_possession_common!($module::ProofOfPossession, $key_type);
|
||||
$crate::app_crypto_pair_common!($module::Pair, $key_type, $module::CRYPTO_ID);
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `Pair` type which is functionally equivalent to `$pair`, but is
|
||||
/// new application-specific type whose identifier is `$key_type`.
|
||||
/// It is a common part shared between full_crypto and non full_crypto environments.
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_pair_common {
|
||||
($pair:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $pair crypto; this has no specific App.
|
||||
#[derive(Clone)]
|
||||
pub struct Pair($pair);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for Pair {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::Pair for Pair {
|
||||
type Public = Public;
|
||||
type Seed = <$pair as $crate::Pair>::Seed;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = <$pair as $crate::Pair>::ProofOfPossession;
|
||||
|
||||
$crate::app_crypto_pair_functions_if_std!($pair);
|
||||
$crate::app_crypto_pair_functions_if_full_crypto!($pair);
|
||||
|
||||
fn from_phrase(
|
||||
phrase: &str,
|
||||
password: Option<&str>,
|
||||
) -> Result<(Self, Self::Seed), $crate::SecretStringError> {
|
||||
<$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1))
|
||||
}
|
||||
fn derive<Iter: Iterator<Item = $crate::DeriveJunction>>(
|
||||
&self,
|
||||
path: Iter,
|
||||
seed: Option<Self::Seed>,
|
||||
) -> Result<(Self, Option<Self::Seed>), $crate::DeriveError> {
|
||||
self.0.derive(path, seed).map(|x| (Self(x.0), x.1))
|
||||
}
|
||||
fn from_seed(seed: &Self::Seed) -> Self {
|
||||
Self(<$pair>::from_seed(seed))
|
||||
}
|
||||
fn from_seed_slice(seed: &[u8]) -> Result<Self, $crate::SecretStringError> {
|
||||
<$pair>::from_seed_slice(seed).map(Self)
|
||||
}
|
||||
fn verify<M: AsRef<[u8]>>(
|
||||
sig: &Self::Signature,
|
||||
message: M,
|
||||
pubkey: &Self::Public,
|
||||
) -> bool {
|
||||
<$pair>::verify(&sig.0, message, pubkey.as_ref())
|
||||
}
|
||||
fn public(&self) -> Self::Public {
|
||||
Public(self.0.public())
|
||||
}
|
||||
fn to_raw_vec(&self) -> $crate::Vec<u8> {
|
||||
self.0.to_raw_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::ProofOfPossessionVerifier for Pair {
|
||||
fn verify_proof_of_possession(
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::ProofOfPossession,
|
||||
allegedly_possessed_pubkey: &Self::Public,
|
||||
) -> bool {
|
||||
<$pair>::verify_proof_of_possession(
|
||||
owner,
|
||||
&proof_of_possession,
|
||||
allegedly_possessed_pubkey.as_ref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for Pair {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
|
||||
impl $crate::AppPair for Pair {
|
||||
type Generic = $pair;
|
||||
}
|
||||
|
||||
impl Pair {
|
||||
/// Convert into wrapped generic key pair type.
|
||||
pub fn into_inner(self) -> $pair {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implements functions for the `Pair` trait when `feature = "std"` is enabled.
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_pair_functions_if_std {
|
||||
($pair:ty) => {
|
||||
fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) {
|
||||
let r = <$pair>::generate_with_phrase(password);
|
||||
(Self(r.0), r.1, r.2)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_pair_functions_if_std {
|
||||
($pair:ty) => {};
|
||||
}
|
||||
|
||||
/// Implements functions for the `Pair` trait when `feature = "full_crypto"` is enabled.
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "full_crypto")]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_pair_functions_if_full_crypto {
|
||||
($pair:ty) => {
|
||||
fn sign(&self, msg: &[u8]) -> Self::Signature {
|
||||
Signature(self.0.sign(msg))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(feature = "full_crypto"))]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_pair_functions_if_full_crypto {
|
||||
($pair:ty) => {};
|
||||
}
|
||||
|
||||
/// Declares `Public` type which is functionally equivalent to `$public` but is
|
||||
/// new application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, `app_crypto_public_common!` must be called too.
|
||||
/// Can only be used with `full_crypto` feature.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_public_full_crypto {
|
||||
($public:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
|
||||
#[derive(
|
||||
Clone, Eq, Hash, PartialEq, PartialOrd, Ord,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::codec::DecodeWithMemTracking,
|
||||
$crate::RuntimeDebug,
|
||||
$crate::codec::MaxEncodedLen,
|
||||
$crate::scale_info::TypeInfo,
|
||||
)]
|
||||
#[codec(crate = $crate::codec)]
|
||||
pub struct Public($public);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for Public {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for Public {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `Public` type which is functionally equivalent to `$public` but is
|
||||
/// new application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, `app_crypto_public_common!` must be called too.
|
||||
/// Can only be used without `full_crypto` feature.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_public_not_full_crypto {
|
||||
($public:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
|
||||
#[derive(
|
||||
Clone, Eq, Hash, PartialEq, Ord, PartialOrd,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::codec::DecodeWithMemTracking,
|
||||
$crate::RuntimeDebug,
|
||||
$crate::codec::MaxEncodedLen,
|
||||
$crate::scale_info::TypeInfo,
|
||||
)]
|
||||
pub struct Public($public);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for Public {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for Public {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `Public` type which is functionally equivalent to `$public` but is
|
||||
/// new application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, `app_crypto_public_(not)_full_crypto!` must be called too.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_public_common {
|
||||
($public:ty, $sig:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::app_crypto_public_common_if_serde!();
|
||||
|
||||
impl AsRef<[u8]> for Public {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for Public {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::ByteArray for Public {
|
||||
const LEN: usize = <$public>::LEN;
|
||||
}
|
||||
|
||||
impl $crate::Public for Public {}
|
||||
|
||||
impl $crate::AppPublic for Public {
|
||||
type Generic = $public;
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Public {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
<$public>::try_from(data).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Public {
|
||||
/// Convert into wrapped generic public key type.
|
||||
pub fn into_inner(self) -> $public {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod module_format_string_prelude {
|
||||
#[cfg(all(not(feature = "std"), feature = "serde"))]
|
||||
pub use alloc::{format, string::String};
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::{format, string::String};
|
||||
}
|
||||
|
||||
/// Implements traits for the public key type if `feature = "serde"` is enabled.
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_public_common_if_serde {
|
||||
() => {
|
||||
impl $crate::Derive for Public {
|
||||
fn derive<Iter: Iterator<Item = $crate::DeriveJunction>>(
|
||||
&self,
|
||||
path: Iter,
|
||||
) -> Option<Self> {
|
||||
self.0.derive(path).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Public {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
use $crate::Ss58Codec;
|
||||
write!(f, "{}", self.0.to_ss58check())
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::serde::Serialize for Public {
|
||||
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: $crate::serde::Serializer,
|
||||
{
|
||||
use $crate::Ss58Codec;
|
||||
serializer.serialize_str(&self.to_ss58check())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> $crate::serde::Deserialize<'de> for Public {
|
||||
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
|
||||
where
|
||||
D: $crate::serde::Deserializer<'de>,
|
||||
{
|
||||
use $crate::{module_format_string_prelude::*, Ss58Codec};
|
||||
|
||||
Public::from_ss58check(&String::deserialize(deserializer)?)
|
||||
.map_err(|e| $crate::serde::de::Error::custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "serde"))]
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_public_common_if_serde {
|
||||
() => {
|
||||
impl $crate::Derive for Public {}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares Signature type which is functionally equivalent to `$sig`, but is new
|
||||
/// Application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, app_crypto_public_common! must be called too.
|
||||
/// Can only be used with `full_crypto` feature
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_signature_full_crypto {
|
||||
($sig:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
|
||||
#[derive(Clone, Eq, PartialEq,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::codec::DecodeWithMemTracking,
|
||||
$crate::RuntimeDebug,
|
||||
$crate::scale_info::TypeInfo,
|
||||
)]
|
||||
#[derive(Hash)]
|
||||
pub struct Signature($sig);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for Signature {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for Signature {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `Signature` type which is functionally equivalent to `$sig`, but is new
|
||||
/// application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, `app_crypto_signature_common` must be called too.
|
||||
/// Can only be used without `full_crypto` feature.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_signature_not_full_crypto {
|
||||
($sig:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
|
||||
#[derive(Clone, Eq, PartialEq,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::codec::DecodeWithMemTracking,
|
||||
$crate::RuntimeDebug,
|
||||
$crate::scale_info::TypeInfo,
|
||||
)]
|
||||
pub struct Signature($sig);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for Signature {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for Signature {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `Signature` type which is functionally equivalent to `$sig`, but is new
|
||||
/// application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, app_crypto_signature_(not)_full_crypto! must be called too.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_signature_common {
|
||||
($sig:ty, $key_type:expr) => {
|
||||
impl $crate::Deref for Signature {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Signature {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for Signature {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::AppSignature for Signature {
|
||||
type Generic = $sig;
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Signature {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
<$sig>::try_from(data).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<$crate::Vec<u8>> for Signature {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: $crate::Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(&data[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::Signature for Signature {}
|
||||
|
||||
impl $crate::ByteArray for Signature {
|
||||
const LEN: usize = <$sig>::LEN;
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Convert into wrapped generic signature type.
|
||||
pub fn into_inner(self) -> $sig {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares ProofOfPossession type which is functionally equivalent to `$sig`, but is new
|
||||
/// Application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, `app_crypto_proof_of_possession_common` must be called too.
|
||||
/// Can only be used with `full_crypto` feature
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_proof_of_possession_full_crypto {
|
||||
($sig:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
|
||||
#[derive(Clone, Eq, PartialEq, Hash,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::codec::DecodeWithMemTracking,
|
||||
$crate::RuntimeDebug,
|
||||
$crate::scale_info::TypeInfo,
|
||||
)]
|
||||
pub struct ProofOfPossession($sig);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for ProofOfPossession {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for ProofOfPossession {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `ProofOfPossession` type which is functionally equivalent to `$sig`, but is new
|
||||
/// application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, `app_crypto_proof_of_possession_common` must be called too.
|
||||
/// Can only be used without `full_crypto` feature.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_proof_of_possession_not_full_crypto {
|
||||
($sig:ty, $key_type:expr, $crypto_type:expr) => {
|
||||
$crate::wrap! {
|
||||
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
|
||||
#[derive(Clone, Eq, PartialEq,
|
||||
$crate::codec::Encode,
|
||||
$crate::codec::Decode,
|
||||
$crate::codec::DecodeWithMemTracking,
|
||||
$crate::RuntimeDebug,
|
||||
$crate::scale_info::TypeInfo,
|
||||
)]
|
||||
pub struct ProofOfPossession($sig);
|
||||
}
|
||||
|
||||
impl $crate::CryptoType for ProofOfPossession {
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl $crate::AppCrypto for ProofOfPossession {
|
||||
type Public = Public;
|
||||
type Pair = Pair;
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = ProofOfPossession;
|
||||
const ID: $crate::KeyTypeId = $key_type;
|
||||
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Declares `ProofOfPossession` type which is functionally equivalent to `$sig`, but is new
|
||||
/// application-specific type whose identifier is `$key_type`.
|
||||
/// For full functionality, app_crypto_proof_of_possession_(not)_full_crypto! must be called too.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! app_crypto_proof_of_possession_common {
|
||||
($sig:ty, $key_type:expr) => {
|
||||
impl $crate::Deref for ProofOfPossession {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for ProofOfPossession {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for ProofOfPossession {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::AppSignature for ProofOfPossession {
|
||||
type Generic = $sig;
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for ProofOfPossession {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
<$sig>::try_from(data).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<$crate::Vec<u8>> for ProofOfPossession {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: $crate::Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(&data[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::Signature for ProofOfPossession {}
|
||||
|
||||
impl $crate::ByteArray for ProofOfPossession {
|
||||
const LEN: usize = <$sig>::LEN;
|
||||
}
|
||||
|
||||
impl ProofOfPossession {
|
||||
/// Convert into wrapped generic signature type.
|
||||
pub fn into_inner(self) -> $sig {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement bidirectional `From` and on-way `AsRef`/`AsMut` for two types, `$inner` and `$outer`.
|
||||
///
|
||||
/// ```rust
|
||||
/// pezsp_application_crypto::wrap! {
|
||||
/// pub struct Wrapper(u32);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! wrap {
|
||||
($( #[ $attr:meta ] )* struct $outer:ident($inner:ty);) => {
|
||||
$( #[ $attr ] )*
|
||||
struct $outer( $inner );
|
||||
$crate::wrap!($inner, $outer);
|
||||
};
|
||||
($( #[ $attr:meta ] )* pub struct $outer:ident($inner:ty);) => {
|
||||
$( #[ $attr ] )*
|
||||
pub struct $outer( $inner );
|
||||
$crate::wrap!($inner, $outer);
|
||||
};
|
||||
($inner:ty, $outer:ty) => {
|
||||
impl $crate::Wraps for $outer {
|
||||
type Inner = $inner;
|
||||
}
|
||||
impl From<$inner> for $outer {
|
||||
fn from(inner: $inner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
impl From<$outer> for $inner {
|
||||
fn from(outer: $outer) -> Self {
|
||||
outer.0
|
||||
}
|
||||
}
|
||||
impl AsRef<$inner> for $outer {
|
||||
fn as_ref(&self) -> &$inner {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl AsMut<$inner> for $outer {
|
||||
fn as_mut(&mut self) -> &mut $inner {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the given code if the pair type is available.
|
||||
///
|
||||
/// The pair type is available when `feature = "std"` || `feature = "full_crypto"`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// pezsp_application_crypto::with_pair! {
|
||||
/// pub type Pair = ();
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(any(feature = "std", feature = "full_crypto"))]
|
||||
macro_rules! with_pair {
|
||||
( $( $def:tt )* ) => {
|
||||
$( $def )*
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
#[cfg(all(not(feature = "std"), not(feature = "full_crypto")))]
|
||||
macro_rules! with_pair {
|
||||
( $( $def:tt )* ) => {};
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Sr25519 crypto types.
|
||||
|
||||
use crate::{KeyTypeId, RuntimePublic};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use pezsp_core::proof_of_possession::NonAggregatable;
|
||||
pub use pezsp_core::sr25519::*;
|
||||
|
||||
mod app {
|
||||
crate::app_crypto!(super, pezsp_core::testing::SR25519);
|
||||
}
|
||||
|
||||
pub use app::{
|
||||
Pair as AppPair, ProofOfPossession as AppProofOfPossession, Public as AppPublic,
|
||||
Signature as AppSignature,
|
||||
};
|
||||
|
||||
impl RuntimePublic for Public {
|
||||
type Signature = Signature;
|
||||
type ProofOfPossession = Signature;
|
||||
|
||||
fn all(key_type: KeyTypeId) -> crate::Vec<Self> {
|
||||
pezsp_io::crypto::sr25519_public_keys(key_type)
|
||||
}
|
||||
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
|
||||
pezsp_io::crypto::sr25519_generate(key_type, seed)
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
|
||||
pezsp_io::crypto::sr25519_sign(key_type, self, msg.as_ref())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
pezsp_io::crypto::sr25519_verify(signature, msg.as_ref(), self)
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::ProofOfPossession> {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::sr25519_sign(key_type, self, &proof_of_possession_statement)
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(
|
||||
&self,
|
||||
owner: &[u8],
|
||||
proof_of_possession: &Self::ProofOfPossession,
|
||||
) -> bool {
|
||||
let proof_of_possession_statement = Pair::proof_of_possession_statement(owner);
|
||||
pezsp_io::crypto::sr25519_verify(&proof_of_possession, &proof_of_possession_statement, &self)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
pezsp_core::crypto::ByteArray::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use codec::Codec;
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
use pezsp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Pair, Public, Signature};
|
||||
|
||||
/// Application-specific cryptographic object.
|
||||
///
|
||||
/// Combines all the core types and constants that are defined by a particular
|
||||
/// cryptographic scheme when it is used in a specific application domain.
|
||||
///
|
||||
/// Typically, the implementers of this trait are its associated types themselves.
|
||||
/// This provides a convenient way to access generic information about the scheme
|
||||
/// given any of the associated types.
|
||||
pub trait AppCrypto: 'static + Sized + CryptoType {
|
||||
/// Identifier for application-specific key type.
|
||||
const ID: KeyTypeId;
|
||||
|
||||
/// Identifier of the crypto type of this application-specific key type.
|
||||
const CRYPTO_ID: CryptoTypeId;
|
||||
|
||||
/// The corresponding public key type in this application scheme.
|
||||
type Public: AppPublic;
|
||||
|
||||
/// The corresponding signature type in this application scheme.
|
||||
type Signature: AppSignature;
|
||||
|
||||
/// The corresponding proof of possession type in this application scheme.
|
||||
type ProofOfPossession: AppSignature;
|
||||
|
||||
/// The corresponding key pair type in this application scheme.
|
||||
type Pair: AppPair;
|
||||
}
|
||||
|
||||
/// Type which implements Hash in std, not when no-std (std variant).
|
||||
pub trait MaybeHash: core::hash::Hash {}
|
||||
impl<T: core::hash::Hash> MaybeHash for T {}
|
||||
|
||||
/// Application-specific key pair.
|
||||
pub trait AppPair:
|
||||
AppCrypto + Pair<Public = <Self as AppCrypto>::Public, Signature = <Self as AppCrypto>::Signature>
|
||||
{
|
||||
/// The wrapped type which is just a plain instance of `Pair`.
|
||||
type Generic: IsWrappedBy<Self>
|
||||
+ Pair<Public = <<Self as AppCrypto>::Public as AppPublic>::Generic>
|
||||
+ Pair<Signature = <<Self as AppCrypto>::Signature as AppSignature>::Generic>;
|
||||
}
|
||||
|
||||
/// Application-specific public key.
|
||||
pub trait AppPublic: AppCrypto + Public + Debug + MaybeHash + Codec {
|
||||
/// The wrapped type which is just a plain instance of `Public`.
|
||||
type Generic: IsWrappedBy<Self> + Public + Debug + MaybeHash + Codec;
|
||||
}
|
||||
|
||||
/// Application-specific signature and Proof Of Possession
|
||||
pub trait AppSignature: AppCrypto + Signature + Eq + PartialEq + Debug + Clone {
|
||||
/// The wrapped type which is just a plain instance of `Signature`.
|
||||
type Generic: IsWrappedBy<Self> + Signature + Eq + PartialEq + Debug;
|
||||
}
|
||||
|
||||
/// Runtime interface for a public key.
|
||||
pub trait RuntimePublic: Sized {
|
||||
/// The signature that will be generated when signing with the corresponding private key.
|
||||
type Signature: Debug + Eq + PartialEq + Clone;
|
||||
|
||||
/// The Proof Of Possession the corresponding private key.
|
||||
type ProofOfPossession: Debug + Eq + PartialEq + Clone;
|
||||
|
||||
/// Returns all public keys for the given key type in the keystore.
|
||||
fn all(key_type: KeyTypeId) -> crate::Vec<Self>;
|
||||
|
||||
/// Generate a public/private pair for the given key type with an optional `seed` and
|
||||
/// store it in the keystore.
|
||||
///
|
||||
/// The `seed` needs to be valid utf8.
|
||||
///
|
||||
/// Returns the generated public key.
|
||||
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self;
|
||||
|
||||
/// Sign the given message with the corresponding private key of this public key.
|
||||
///
|
||||
/// The private key will be requested from the keystore using the given key type.
|
||||
///
|
||||
/// Returns the signature or `None` if the private key could not be found or some other error
|
||||
/// occurred.
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature>;
|
||||
|
||||
/// Verify that the given signature matches the given message using this public key.
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
|
||||
|
||||
/// Generates the necessary proof(s) usually as a signature or list of signatures, for the
|
||||
/// corresponding public key to be accepted as legitimate by the network.
|
||||
///
|
||||
/// This includes attestation of the owner of the public key in the form of signing the owner's
|
||||
/// identity. It might also includes other signatures such as signature obtained by signing
|
||||
/// the corresponding public in different context than normal signatures in case of BLS
|
||||
/// key pair.
|
||||
///
|
||||
/// The `owner` is an arbitrary byte array representing the identity of the owner of
|
||||
/// the key which has been signed by the private key in process of generating the proof.
|
||||
///
|
||||
/// The private key will be requested from the keystore using the given key type.
|
||||
///
|
||||
/// Returns the proof of possession or `None` if it failed or is not able to do
|
||||
/// so.
|
||||
fn generate_proof_of_possession(
|
||||
&mut self,
|
||||
key_type: KeyTypeId,
|
||||
owner: &[u8],
|
||||
) -> Option<Self::ProofOfPossession>;
|
||||
|
||||
/// Verifies that the given proof is valid for the corresponding public key.
|
||||
/// The proof is usually a signature or list of signatures, for the corresponding
|
||||
/// public key to be accepted by the network. It include attestation of the owner of
|
||||
/// the public key in the form signing the owner's identity but might also includes
|
||||
/// other signatures.
|
||||
///
|
||||
/// The `owner` is an arbitrary byte array representing the identity of the owner of
|
||||
/// the key which has been signed by the private key in process of generating the proof.
|
||||
///
|
||||
/// Returns `true` if the proof is deemed correct by the cryto type.
|
||||
fn verify_proof_of_possession(&self, owner: &[u8], pop: &Self::ProofOfPossession) -> bool;
|
||||
|
||||
/// Returns `Self` as raw vec.
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Runtime interface for an application's public key.
|
||||
pub trait RuntimeAppPublic: Sized {
|
||||
/// An identifier for this application-specific key type.
|
||||
const ID: KeyTypeId;
|
||||
|
||||
/// The signature that will be generated when signing with the corresponding private key.
|
||||
type Signature: Debug + Eq + PartialEq + Clone + TypeInfo + Codec;
|
||||
|
||||
/// The Proof Of Possession the corresponding private key.
|
||||
type ProofOfPossession: Debug + Eq + PartialEq + TypeInfo + Clone;
|
||||
|
||||
/// Returns all public keys for this application in the keystore.
|
||||
fn all() -> crate::Vec<Self>;
|
||||
|
||||
/// Generate a public/private pair with an optional `seed` and store it in the keystore.
|
||||
///
|
||||
/// The `seed` needs to be valid utf8.
|
||||
///
|
||||
/// Returns the generated public key.
|
||||
fn generate_pair(seed: Option<Vec<u8>>) -> Self;
|
||||
|
||||
/// Sign the given message with the corresponding private key of this public key.
|
||||
///
|
||||
/// The private key will be requested from the keystore.
|
||||
///
|
||||
/// Returns the signature or `None` if the private key could not be found or some other error
|
||||
/// occurred.
|
||||
fn sign<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature>;
|
||||
|
||||
/// Verify that the given signature matches the given message using this public key.
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
|
||||
|
||||
/// Generate proof of legitimacy for the corresponding public key
|
||||
///
|
||||
/// Returns the proof of possession, usually a signature or a list of signature, or `None` if
|
||||
/// it failed or is not able to do so.
|
||||
fn generate_proof_of_possession(&mut self, owner: &[u8]) -> Option<Self::ProofOfPossession>;
|
||||
|
||||
/// Verify that the given proof of possession is valid for the corresponding public key.
|
||||
fn verify_proof_of_possession(&self, owner: &[u8], pop: &Self::ProofOfPossession) -> bool;
|
||||
|
||||
/// Returns `Self` as raw vec.
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl<T> RuntimeAppPublic for T
|
||||
where
|
||||
T: AppPublic + AsRef<<T as AppPublic>::Generic> + AsMut<<T as AppPublic>::Generic>,
|
||||
<T as AppPublic>::Generic: RuntimePublic,
|
||||
<T as AppCrypto>::Signature: TypeInfo
|
||||
+ Codec
|
||||
+ From<<<T as AppPublic>::Generic as RuntimePublic>::Signature>
|
||||
+ AsRef<<<T as AppPublic>::Generic as RuntimePublic>::Signature>,
|
||||
<T as AppCrypto>::ProofOfPossession: TypeInfo
|
||||
+ Codec
|
||||
+ From<<<T as AppPublic>::Generic as RuntimePublic>::ProofOfPossession>
|
||||
+ AsRef<<<T as AppPublic>::Generic as RuntimePublic>::ProofOfPossession>,
|
||||
{
|
||||
const ID: KeyTypeId = <T as AppCrypto>::ID;
|
||||
|
||||
type Signature = <T as AppCrypto>::Signature;
|
||||
type ProofOfPossession = <T as AppCrypto>::ProofOfPossession;
|
||||
|
||||
fn all() -> crate::Vec<Self> {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::all(Self::ID)
|
||||
.into_iter()
|
||||
.map(|p| p.into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_pair(seed: Option<Vec<u8>>) -> Self {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::generate_pair(Self::ID, seed).into()
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature> {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::sign(self.as_ref(), Self::ID, msg)
|
||||
.map(|s| s.into())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::verify(self.as_ref(), msg, signature.as_ref())
|
||||
}
|
||||
|
||||
fn generate_proof_of_possession(&mut self, owner: &[u8]) -> Option<Self::ProofOfPossession> {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::generate_proof_of_possession(
|
||||
self.as_mut(),
|
||||
Self::ID,
|
||||
owner,
|
||||
)
|
||||
.map(|s| s.into())
|
||||
}
|
||||
|
||||
fn verify_proof_of_possession(&self, owner: &[u8], pop: &Self::ProofOfPossession) -> bool {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::verify_proof_of_possession(
|
||||
self.as_ref(),
|
||||
owner,
|
||||
pop.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
<<T as AppPublic>::Generic as RuntimePublic>::to_raw_vec(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that is bound to a fixed [`RuntimeAppPublic`].
|
||||
pub trait BoundToRuntimeAppPublic {
|
||||
/// The [`RuntimeAppPublic`] this type is bound to.
|
||||
type Public: RuntimeAppPublic;
|
||||
}
|
||||
|
||||
impl<T: RuntimeAppPublic> BoundToRuntimeAppPublic for T {
|
||||
type Public = Self;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "pezsp-application-crypto-test"
|
||||
version = "2.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
description = "Integration tests for application-crypto"
|
||||
license = "Apache-2.0"
|
||||
publish = false
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
pezsp-api = { workspace = true, default-features = true }
|
||||
pezsp-application-crypto = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-keystore = { workspace = true }
|
||||
pezsp-tracing = { workspace = true, default-features = true }
|
||||
bizinikiwi-test-runtime-client = { workspace = true }
|
||||
|
||||
[features]
|
||||
bls-experimental = ["bizinikiwi-test-runtime-client/bls-experimental"]
|
||||
runtime-benchmarks = [
|
||||
"pezsp-api/runtime-benchmarks",
|
||||
"bizinikiwi-test-runtime-client/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,69 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Integration tests for bls12-381
|
||||
|
||||
use pezsp_api::{ApiExt, ProvideRuntimeApi};
|
||||
use pezsp_application_crypto::{bls381::AppPair, RuntimePublic};
|
||||
use pezsp_core::{
|
||||
bls381::Pair as Bls381Pair,
|
||||
crypto::ByteArray,
|
||||
proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
|
||||
testing::BLS381,
|
||||
Pair,
|
||||
};
|
||||
use pezsp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
|
||||
use std::sync::Arc;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::{TestAPI, TEST_OWNER},
|
||||
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn bls381_works_in_runtime() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let keystore = Arc::new(MemoryKeystore::new());
|
||||
let test_client = TestClientBuilder::new().build();
|
||||
|
||||
let mut runtime_api = test_client.runtime_api();
|
||||
runtime_api.register_extension(KeystoreExt::new(keystore.clone()));
|
||||
|
||||
let (proof_of_possession, public) = runtime_api
|
||||
.test_bls381_crypto(test_client.chain_info().genesis_hash)
|
||||
.expect("Tests `bls381` crypto.");
|
||||
|
||||
let supported_keys = keystore.keys(BLS381).unwrap();
|
||||
assert!(supported_keys.contains(&public.to_raw_vec()));
|
||||
|
||||
assert!(AppPair::verify_proof_of_possession(
|
||||
TEST_OWNER,
|
||||
&proof_of_possession.into(),
|
||||
&public.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bls381_client_proof_of_possession_verified_by_runtime_public() {
|
||||
let (mut test_pair, _) = Bls381Pair::generate();
|
||||
|
||||
let client_generated_proof_of_possession = test_pair.generate_proof_of_possession(TEST_OWNER);
|
||||
assert!(RuntimePublic::verify_proof_of_possession(
|
||||
&test_pair.public(),
|
||||
TEST_OWNER,
|
||||
&client_generated_proof_of_possession
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Integration tests for ecdsa
|
||||
use pezsp_api::{ApiExt, ProvideRuntimeApi};
|
||||
use pezsp_application_crypto::{ecdsa::AppPair, RuntimePublic};
|
||||
use pezsp_core::{
|
||||
crypto::{ByteArray, Pair},
|
||||
ecdsa::Pair as ECDSAPair,
|
||||
proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
|
||||
testing::ECDSA,
|
||||
};
|
||||
use pezsp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
|
||||
use std::sync::Arc;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::{TestAPI, TEST_OWNER},
|
||||
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn ecdsa_works_in_runtime() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let keystore = Arc::new(MemoryKeystore::new());
|
||||
let test_client = TestClientBuilder::new().build();
|
||||
|
||||
let mut runtime_api = test_client.runtime_api();
|
||||
runtime_api.register_extension(KeystoreExt::new(keystore.clone()));
|
||||
|
||||
let (signature, public, proof_of_possession) = runtime_api
|
||||
.test_ecdsa_crypto(test_client.chain_info().genesis_hash)
|
||||
.expect("Tests `ecdsa` crypto.");
|
||||
|
||||
let supported_keys = keystore.keys(ECDSA).unwrap();
|
||||
assert!(supported_keys.contains(&public.to_raw_vec()));
|
||||
assert!(AppPair::verify(&signature, "ecdsa", &public));
|
||||
assert!(AppPair::verify_proof_of_possession(
|
||||
TEST_OWNER,
|
||||
&proof_of_possession.into(),
|
||||
&public.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecdsa_client_generated_proof_of_possession_verified_by_runtime_public() {
|
||||
let (mut test_pair, _) = ECDSAPair::generate();
|
||||
|
||||
let client_generated_proof_of_possession = test_pair.generate_proof_of_possession(TEST_OWNER);
|
||||
assert!(RuntimePublic::verify_proof_of_possession(
|
||||
&test_pair.public(),
|
||||
TEST_OWNER,
|
||||
&client_generated_proof_of_possession
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Integration tests for ecdsa-bls12-381
|
||||
|
||||
use pezsp_api::{ApiExt, ProvideRuntimeApi};
|
||||
use pezsp_application_crypto::{ecdsa_bls381::AppPair, RuntimePublic};
|
||||
use pezsp_core::{
|
||||
crypto::ByteArray,
|
||||
ecdsa_bls381::Pair as EcdsaBls381Pair,
|
||||
proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
|
||||
testing::ECDSA_BLS381,
|
||||
Pair,
|
||||
};
|
||||
use pezsp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
|
||||
use std::sync::Arc;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::{TestAPI, TEST_OWNER},
|
||||
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn ecdsa_bls381_works_in_runtime() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let keystore = Arc::new(MemoryKeystore::new());
|
||||
let test_client = TestClientBuilder::new().build();
|
||||
|
||||
let mut runtime_api = test_client.runtime_api();
|
||||
runtime_api.register_extension(KeystoreExt::new(keystore.clone()));
|
||||
|
||||
let (proof_of_possession, public) = runtime_api
|
||||
.test_ecdsa_bls381_crypto(test_client.chain_info().genesis_hash)
|
||||
.expect("Tests `ecdsa_bls381` crypto.");
|
||||
|
||||
let supported_keys = keystore.keys(ECDSA_BLS381).unwrap();
|
||||
assert!(supported_keys.contains(&public.to_raw_vec()));
|
||||
assert!(supported_keys.len() == 3);
|
||||
|
||||
assert!(AppPair::verify_proof_of_possession(
|
||||
TEST_OWNER,
|
||||
&proof_of_possession.into(),
|
||||
&public.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecdsa_bls381_client_proof_of_possession_verified_by_runtime_public() {
|
||||
let (mut test_pair, _) = EcdsaBls381Pair::generate();
|
||||
|
||||
let client_generated_proof_of_possession = test_pair.generate_proof_of_possession(TEST_OWNER);
|
||||
assert!(RuntimePublic::verify_proof_of_possession(
|
||||
&test_pair.public(),
|
||||
TEST_OWNER,
|
||||
&client_generated_proof_of_possession
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Integration tests for ed25519
|
||||
|
||||
use pezsp_api::{ApiExt, ProvideRuntimeApi};
|
||||
use pezsp_application_crypto::{ed25519::AppPair, RuntimePublic};
|
||||
use pezsp_core::{
|
||||
crypto::{ByteArray, Pair},
|
||||
ed25519::Pair as Ed25519Pair,
|
||||
proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
|
||||
testing::ED25519,
|
||||
};
|
||||
use pezsp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
|
||||
use std::sync::Arc;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::{TestAPI, TEST_OWNER},
|
||||
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn ed25519_works_in_runtime() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let keystore = Arc::new(MemoryKeystore::new());
|
||||
let test_client = TestClientBuilder::new().build();
|
||||
|
||||
let mut runtime_api = test_client.runtime_api();
|
||||
runtime_api.register_extension(KeystoreExt::new(keystore.clone()));
|
||||
|
||||
let (signature, public, proof_of_possession) = runtime_api
|
||||
.test_ed25519_crypto(test_client.chain_info().genesis_hash)
|
||||
.expect("Tests `ed25519` crypto.");
|
||||
|
||||
let supported_keys = keystore.keys(ED25519).unwrap();
|
||||
assert!(supported_keys.contains(&public.to_raw_vec()));
|
||||
assert!(AppPair::verify(&signature, "ed25519", &public));
|
||||
assert!(AppPair::verify_proof_of_possession(
|
||||
TEST_OWNER,
|
||||
&proof_of_possession.into(),
|
||||
&public.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ed25519_client_proof_of_possession_verified_by_runtime_public() {
|
||||
let (mut test_pair, _) = Ed25519Pair::generate();
|
||||
|
||||
let client_generated_proof_of_possession = test_pair.generate_proof_of_possession(TEST_OWNER);
|
||||
assert!(RuntimePublic::verify_proof_of_possession(
|
||||
&test_pair.public(),
|
||||
TEST_OWNER,
|
||||
&client_generated_proof_of_possession
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Integration tests for application crypto
|
||||
|
||||
#[cfg(all(test, feature = "bls-experimental"))]
|
||||
mod bls381;
|
||||
#[cfg(test)]
|
||||
mod ecdsa;
|
||||
#[cfg(test)]
|
||||
mod ed25519;
|
||||
#[cfg(test)]
|
||||
mod sr25519;
|
||||
|
||||
#[cfg(all(test, feature = "bls-experimental"))]
|
||||
mod ecdsa_bls381;
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Integration tests for sr25519
|
||||
|
||||
use pezsp_api::{ApiExt, ProvideRuntimeApi};
|
||||
use pezsp_application_crypto::{sr25519::AppPair, RuntimePublic};
|
||||
use pezsp_core::{
|
||||
crypto::{ByteArray, Pair},
|
||||
proof_of_possession::{ProofOfPossessionGenerator, ProofOfPossessionVerifier},
|
||||
sr25519::Pair as Sr25519Pair,
|
||||
testing::SR25519,
|
||||
};
|
||||
use pezsp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
|
||||
use std::sync::Arc;
|
||||
use bizinikiwi_test_runtime_client::{
|
||||
runtime::{TestAPI, TEST_OWNER},
|
||||
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn sr25519_works_in_runtime() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let keystore = Arc::new(MemoryKeystore::new());
|
||||
let test_client = TestClientBuilder::new().build();
|
||||
|
||||
let mut runtime_api = test_client.runtime_api();
|
||||
runtime_api.register_extension(KeystoreExt::new(keystore.clone()));
|
||||
|
||||
let (signature, public, proof_of_possession) = runtime_api
|
||||
.test_sr25519_crypto(test_client.chain_info().genesis_hash)
|
||||
.expect("Tests `sr25519` crypto.");
|
||||
|
||||
let supported_keys = keystore.keys(SR25519).unwrap();
|
||||
assert!(supported_keys.contains(&public.to_raw_vec()));
|
||||
assert!(AppPair::verify(&signature, "sr25519", &public));
|
||||
assert!(AppPair::verify_proof_of_possession(
|
||||
TEST_OWNER,
|
||||
&proof_of_possession.into(),
|
||||
&public.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sr25519_client_proof_of_possession_verified_by_runtime_public() {
|
||||
let (mut test_pair, _) = Sr25519Pair::generate();
|
||||
|
||||
let client_generated_proof_of_possession = test_pair.generate_proof_of_possession(TEST_OWNER);
|
||||
assert!(RuntimePublic::verify_proof_of_possession(
|
||||
&test_pair.public(),
|
||||
TEST_OWNER,
|
||||
&client_generated_proof_of_possession
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
[package]
|
||||
name = "pezsp-arithmetic"
|
||||
version = "23.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Minimal fixed point arithmetic primitives and types for runtime."
|
||||
documentation = "https://docs.rs/pezsp-arithmetic"
|
||||
readme = "README.md"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive", "max-encoded-len"], workspace = true }
|
||||
docify = { workspace = true }
|
||||
integer-sqrt = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
serde = { features = ["alloc", "derive"], optional = true, workspace = true }
|
||||
static_assertions = { workspace = true, default-features = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true, default-features = true }
|
||||
primitive-types = { workspace = true, default-features = true }
|
||||
rand = { workspace = true, default-features = true }
|
||||
pezsp-crypto-hashing = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"num-traits/std",
|
||||
"scale-info/std",
|
||||
"serde/std",
|
||||
"pezsp-crypto-hashing/std",
|
||||
]
|
||||
# Serde support without relying on std features.
|
||||
serde = ["dep:serde", "scale-info/serde"]
|
||||
@@ -0,0 +1,3 @@
|
||||
Minimal fixed point arithmetic primitives and types for runtime.
|
||||
|
||||
License: Apache-2.0
|
||||
@@ -0,0 +1,81 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
||||
use rand::Rng;
|
||||
use pezsp_arithmetic::biguint::{BigUint, Single};
|
||||
|
||||
fn random_big_uint(size: usize) -> BigUint {
|
||||
let mut rng = rand::thread_rng();
|
||||
let digits: Vec<_> = (0..size).map(|_| rng.gen_range(0..Single::MAX)).collect();
|
||||
BigUint::from_limbs(&digits)
|
||||
}
|
||||
|
||||
fn bench_op<F: Fn(&BigUint, &BigUint)>(c: &mut Criterion, name: &str, op: F) {
|
||||
let mut group = c.benchmark_group(name);
|
||||
|
||||
for size in [2, 4, 6, 8, 10].iter() {
|
||||
group.throughput(Throughput::Elements(*size));
|
||||
group.bench_with_input(BenchmarkId::from_parameter(size), size, |bencher, &size| {
|
||||
let a = random_big_uint(size as usize);
|
||||
let b = random_big_uint(size as usize);
|
||||
|
||||
bencher.iter(|| op(&a, &b));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_addition(c: &mut Criterion) {
|
||||
bench_op(c, "addition", |a, b| {
|
||||
let _ = a.clone().add(b);
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_subtraction(c: &mut Criterion) {
|
||||
bench_op(c, "subtraction", |a, b| {
|
||||
let _ = a.clone().sub(b);
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_multiplication(c: &mut Criterion) {
|
||||
bench_op(c, "multiplication", |a, b| {
|
||||
let _ = a.clone().mul(b);
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_division(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("division");
|
||||
|
||||
for size in [4, 6, 8, 10].iter() {
|
||||
group.throughput(Throughput::Elements(*size));
|
||||
group.bench_with_input(BenchmarkId::from_parameter(size), size, |bencher, &size| {
|
||||
let a = random_big_uint(size as usize);
|
||||
let b = random_big_uint(rand::thread_rng().gen_range(2..size as usize));
|
||||
|
||||
bencher.iter(|| {
|
||||
let _ = a.clone().div(&b, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default();
|
||||
targets = bench_addition, bench_subtraction, bench_multiplication, bench_division
|
||||
}
|
||||
criterion_main!(benches);
|
||||
@@ -0,0 +1,48 @@
|
||||
[package]
|
||||
name = "pezsp-arithmetic-fuzzer"
|
||||
version = "2.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Fuzzer for fixed point arithmetic primitives."
|
||||
documentation = "https://docs.rs/pezsp-arithmetic-fuzzer"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[[bin]]
|
||||
name = "biguint"
|
||||
path = "src/biguint.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "normalize"
|
||||
path = "src/normalize.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "per_thing_from_rational"
|
||||
path = "src/per_thing_from_rational.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "per_thing_mult_fraction"
|
||||
path = "src/per_thing_mult_fraction.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "multiply_by_rational_with_rounding"
|
||||
path = "src/multiply_by_rational_with_rounding.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fixed_point"
|
||||
path = "src/fixed_point.rs"
|
||||
|
||||
[dependencies]
|
||||
arbitrary = { workspace = true }
|
||||
fraction = { workspace = true }
|
||||
honggfuzz = { workspace = true }
|
||||
num-bigint = { workspace = true }
|
||||
pezsp-arithmetic = { workspace = true, default-features = true }
|
||||
@@ -0,0 +1,211 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run biguint`. `honggfuzz` CLI options can
|
||||
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
|
||||
//!
|
||||
//! # Debugging a panic
|
||||
//! Once a panic is found, it can be debugged with
|
||||
//! `cargo hfuzz run-debug biguint hfuzz_workspace/biguint/*.fuzz`.
|
||||
//!
|
||||
//! # More information
|
||||
//! More information about `honggfuzz` can be found
|
||||
//! [here](https://docs.rs/honggfuzz/).
|
||||
|
||||
use honggfuzz::fuzz;
|
||||
use pezsp_arithmetic::biguint::{BigUint, Single};
|
||||
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (Vec<Single>, Vec<Single>, bool)| {
|
||||
let (mut digits_u, mut digits_v, return_remainder) = data;
|
||||
|
||||
let mut u = BigUint::from_limbs(&digits_u);
|
||||
let mut v = BigUint::from_limbs(&digits_v);
|
||||
|
||||
u.lstrip();
|
||||
v.lstrip();
|
||||
|
||||
let ue = u128::try_from(u.clone());
|
||||
let ve = u128::try_from(v.clone());
|
||||
|
||||
digits_u.reverse();
|
||||
digits_v.reverse();
|
||||
|
||||
let num_u = num_bigint::BigUint::new(digits_u);
|
||||
let num_v = num_bigint::BigUint::new(digits_v);
|
||||
|
||||
if check_digit_lengths(&u, &v, 4) {
|
||||
assert_eq!(u.cmp(&v), ue.cmp(&ve));
|
||||
assert_eq!(u.eq(&v), ue.eq(&ve));
|
||||
}
|
||||
|
||||
if check_digit_lengths(&u, &v, 3) {
|
||||
let expected = ue.unwrap() + ve.unwrap();
|
||||
let t = u.clone().add(&v);
|
||||
assert_eq!(
|
||||
u128::try_from(t.clone()).unwrap(),
|
||||
expected,
|
||||
"{:?} + {:?} ===> {:?} != {:?}",
|
||||
u,
|
||||
v,
|
||||
t,
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
if check_digit_lengths(&u, &v, 4) {
|
||||
let expected = ue.unwrap().checked_sub(ve.unwrap());
|
||||
let t = u.clone().sub(&v);
|
||||
if expected.is_none() {
|
||||
assert!(t.is_err())
|
||||
} else {
|
||||
let t = t.unwrap();
|
||||
let expected = expected.unwrap();
|
||||
assert_eq!(
|
||||
u128::try_from(t.clone()).unwrap(),
|
||||
expected,
|
||||
"{:?} - {:?} ===> {:?} != {:?}",
|
||||
u,
|
||||
v,
|
||||
t,
|
||||
expected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if check_digit_lengths(&u, &v, 2) {
|
||||
let expected = ue.unwrap() * ve.unwrap();
|
||||
let t = u.clone().mul(&v);
|
||||
assert_eq!(
|
||||
u128::try_from(t.clone()).unwrap(),
|
||||
expected,
|
||||
"{:?} * {:?} ===> {:?} != {:?}",
|
||||
u,
|
||||
v,
|
||||
t,
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
if check_digit_lengths(&u, &v, 4) {
|
||||
let (ue, ve) = (ue.unwrap(), ve.unwrap());
|
||||
if ve == 0 {
|
||||
return;
|
||||
}
|
||||
let (q, r) = (ue / ve, ue % ve);
|
||||
if let Some((qq, rr)) = u.clone().div(&v, true) {
|
||||
assert_eq!(
|
||||
u128::try_from(qq.clone()).unwrap(),
|
||||
q,
|
||||
"{:?} / {:?} ===> {:?} != {:?}",
|
||||
u,
|
||||
v,
|
||||
qq,
|
||||
q,
|
||||
);
|
||||
assert_eq!(
|
||||
u128::try_from(rr.clone()).unwrap(),
|
||||
r,
|
||||
"{:?} % {:?} ===> {:?} != {:?}",
|
||||
u,
|
||||
v,
|
||||
rr,
|
||||
r,
|
||||
);
|
||||
} else if v.len() == 1 {
|
||||
let qq = u.clone().div_unit(ve as Single);
|
||||
assert_eq!(
|
||||
u128::try_from(qq.clone()).unwrap(),
|
||||
q,
|
||||
"[single] {:?} / {:?} ===> {:?} != {:?}",
|
||||
u,
|
||||
v,
|
||||
qq,
|
||||
q,
|
||||
);
|
||||
} else if v.msb() != 0 && u.msb() != 0 && u.len() > v.len() {
|
||||
panic!("div returned none for an unexpected reason");
|
||||
}
|
||||
}
|
||||
|
||||
// Test against num_bigint
|
||||
|
||||
// Equality
|
||||
|
||||
assert_eq!(u.cmp(&v), num_u.cmp(&num_v));
|
||||
|
||||
// Addition
|
||||
|
||||
let w = u.clone().add(&v);
|
||||
let num_w = num_u.clone() + &num_v;
|
||||
|
||||
assert_biguints_eq(&w, &num_w);
|
||||
|
||||
// Subtraction
|
||||
|
||||
if let Ok(w) = u.clone().sub(&v) {
|
||||
let num_w = num_u.clone() - &num_v;
|
||||
|
||||
assert_biguints_eq(&w, &num_w);
|
||||
}
|
||||
|
||||
// Multiplication
|
||||
|
||||
let w = u.clone().mul(&v);
|
||||
let num_w = num_u.clone() * &num_v;
|
||||
|
||||
assert_biguints_eq(&w, &num_w);
|
||||
|
||||
// Division
|
||||
|
||||
if v.len() == 1 && v.get(0) != 0 {
|
||||
let w = u.div_unit(v.get(0));
|
||||
let num_w = num_u / &num_v;
|
||||
assert_biguints_eq(&w, &num_w);
|
||||
} else if u.len() > v.len() && v.len() > 1 {
|
||||
let num_remainder = num_u.clone() % num_v.clone();
|
||||
|
||||
let (w, remainder) = u.div(&v, return_remainder).unwrap();
|
||||
let num_w = num_u / &num_v;
|
||||
|
||||
assert_biguints_eq(&w, &num_w);
|
||||
|
||||
if return_remainder {
|
||||
assert_biguints_eq(&remainder, &num_remainder);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_digit_lengths(u: &BigUint, v: &BigUint, max_limbs: usize) -> bool {
|
||||
1 <= u.len() && u.len() <= max_limbs && 1 <= v.len() && v.len() <= max_limbs
|
||||
}
|
||||
|
||||
fn assert_biguints_eq(a: &BigUint, b: &num_bigint::BigUint) {
|
||||
let mut a = a.clone();
|
||||
a.lstrip();
|
||||
|
||||
// `num_bigint::BigUint` doesn't expose it's internals, so we need to convert into that to
|
||||
// compare.
|
||||
let limbs = (0..a.len()).map(|i| a.get(i)).collect();
|
||||
let num_a = num_bigint::BigUint::new(limbs);
|
||||
|
||||
assert!(&num_a == b, "\narithmetic: {:?}\nnum-bigint: {:?}", a, b);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run fixed_point`. `honggfuzz` CLI options can
|
||||
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
|
||||
//!
|
||||
//! # Debugging a panic
|
||||
//! Once a panic is found, it can be debugged with
|
||||
//! `cargo hfuzz run-debug fixed_point hfuzz_workspace/fixed_point/*.fuzz`.
|
||||
//!
|
||||
//! # More information
|
||||
//! More information about `honggfuzz` can be found
|
||||
//! [here](https://docs.rs/honggfuzz/).
|
||||
|
||||
use honggfuzz::fuzz;
|
||||
use pezsp_arithmetic::{traits::Saturating, FixedI64, FixedPointNumber};
|
||||
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (i32, i32)| {
|
||||
let x: i128 = data.0.into();
|
||||
let y: i128 = data.1.into();
|
||||
|
||||
// Check `from_rational` and division are consistent.
|
||||
if y != 0 {
|
||||
let f1 =
|
||||
FixedI64::saturating_from_integer(x) / FixedI64::saturating_from_integer(y);
|
||||
let f2 = FixedI64::saturating_from_rational(x, y);
|
||||
assert_eq!(f1.into_inner(), f2.into_inner());
|
||||
}
|
||||
|
||||
// Check `saturating_mul`.
|
||||
let a = FixedI64::saturating_from_rational(2, 5);
|
||||
let b = a.saturating_mul(FixedI64::saturating_from_integer(x));
|
||||
let n = b.into_inner() as i128;
|
||||
let m = 2i128 * x * FixedI64::accuracy() as i128 / 5i128;
|
||||
assert_eq!(n, m);
|
||||
|
||||
// Check `saturating_mul` and division are inverse.
|
||||
if x != 0 {
|
||||
assert_eq!(a, b / FixedI64::saturating_from_integer(x));
|
||||
}
|
||||
|
||||
// Check `reciprocal`.
|
||||
let r = a.reciprocal().unwrap().reciprocal().unwrap();
|
||||
assert_eq!(a, r);
|
||||
|
||||
// Check addition.
|
||||
let a = FixedI64::saturating_from_integer(x);
|
||||
let b = FixedI64::saturating_from_integer(y);
|
||||
let c = FixedI64::saturating_from_integer(x.saturating_add(y));
|
||||
assert_eq!(a.saturating_add(b), c);
|
||||
|
||||
// Check subtraction.
|
||||
let a = FixedI64::saturating_from_integer(x);
|
||||
let b = FixedI64::saturating_from_integer(y);
|
||||
let c = FixedI64::saturating_from_integer(x.saturating_sub(y));
|
||||
assert_eq!(a.saturating_sub(b), c);
|
||||
|
||||
// Check `saturating_mul_acc_int`.
|
||||
let a = FixedI64::saturating_from_rational(2, 5);
|
||||
let b = a.saturating_mul_acc_int(x);
|
||||
let xx = FixedI64::saturating_from_integer(x);
|
||||
let d = a.saturating_mul(xx).saturating_add(xx).into_inner() as i128 /
|
||||
FixedI64::accuracy() as i128;
|
||||
assert_eq!(b, d);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run multiply_by_rational_with_rounding`.
|
||||
//! `honggfuzz` CLI options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4
|
||||
//! threads.
|
||||
//!
|
||||
//! # Debugging a panic
|
||||
//! Once a panic is found, it can be debugged with
|
||||
//! `cargo hfuzz run-debug multiply_by_rational_with_rounding
|
||||
//! hfuzz_workspace/multiply_by_rational_with_rounding/*.fuzz`.
|
||||
//!
|
||||
//! # More information
|
||||
//! More information about `honggfuzz` can be found
|
||||
//! [here](https://docs.rs/honggfuzz/).
|
||||
|
||||
use fraction::prelude::BigFraction as Fraction;
|
||||
use honggfuzz::fuzz;
|
||||
use pezsp_arithmetic::{MultiplyRational, Rounding, Rounding::*};
|
||||
|
||||
/// Tries to demonstrate that `multiply_by_rational_with_rounding` is incorrect.
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (u128, u128, u128, ArbitraryRounding)| {
|
||||
let (f, n, d, r) = (data.0, data.1, data.2, data.3 .0);
|
||||
|
||||
check::<u8>(f as u8, n as u8, d as u8, r);
|
||||
check::<u16>(f as u16, n as u16, d as u16, r);
|
||||
check::<u32>(f as u32, n as u32, d as u32, r);
|
||||
check::<u64>(f as u64, n as u64, d as u64, r);
|
||||
check::<u128>(f, n, d, r);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn check<N>(f: N, n: N, d: N, r: Rounding)
|
||||
where
|
||||
N: MultiplyRational + Into<u128> + Copy + core::fmt::Debug,
|
||||
{
|
||||
let Some(got) = f.multiply_rational(n, d, r) else { return };
|
||||
|
||||
let (ae, be, ce) =
|
||||
(Fraction::from(f.into()), Fraction::from(n.into()), Fraction::from(d.into()));
|
||||
let want = round(ae * be / ce, r);
|
||||
|
||||
assert_eq!(
|
||||
Fraction::from(got.into()),
|
||||
want,
|
||||
"{:?} * {:?} / {:?} = {:?} != {:?}",
|
||||
f,
|
||||
n,
|
||||
d,
|
||||
got,
|
||||
want
|
||||
);
|
||||
}
|
||||
|
||||
/// Round a `Fraction` according to the given mode.
|
||||
fn round(f: Fraction, r: Rounding) -> Fraction {
|
||||
match r {
|
||||
Up => f.ceil(),
|
||||
NearestPrefUp =>
|
||||
if f.fract() < Fraction::from(0.5) {
|
||||
f.floor()
|
||||
} else {
|
||||
f.ceil()
|
||||
},
|
||||
Down => f.floor(),
|
||||
NearestPrefDown =>
|
||||
if f.fract() > Fraction::from(0.5) {
|
||||
f.ceil()
|
||||
} else {
|
||||
f.floor()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`arbitrary::Arbitrary`] [`Rounding`] mode.
|
||||
struct ArbitraryRounding(Rounding);
|
||||
impl arbitrary::Arbitrary<'_> for ArbitraryRounding {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
Ok(Self(match u.int_in_range(0..=3).unwrap() {
|
||||
0 => Up,
|
||||
1 => NearestPrefUp,
|
||||
2 => Down,
|
||||
3 => NearestPrefDown,
|
||||
_ => unreachable!(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run normalize`. `honggfuzz` CLI options can
|
||||
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
|
||||
//!
|
||||
//! # Debugging a panic
|
||||
//! Once a panic is found, it can be debugged with
|
||||
//! `cargo hfuzz run-debug normalize hfuzz_workspace/normalize/*.fuzz`.
|
||||
|
||||
use honggfuzz::fuzz;
|
||||
use pezsp_arithmetic::Normalizable;
|
||||
|
||||
type Ty = u64;
|
||||
|
||||
fn main() {
|
||||
let sum_limit = Ty::max_value() as u128;
|
||||
let len_limit: usize = Ty::max_value().try_into().unwrap();
|
||||
|
||||
loop {
|
||||
fuzz!(|data: (Vec<Ty>, Ty)| {
|
||||
let (data, norm) = data;
|
||||
if data.is_empty() {
|
||||
return;
|
||||
}
|
||||
let pre_sum: u128 = data.iter().map(|x| *x as u128).sum();
|
||||
|
||||
let normalized = data.normalize(norm);
|
||||
// error cases.
|
||||
if pre_sum > sum_limit || data.len() > len_limit {
|
||||
assert!(normalized.is_err())
|
||||
} else if let Ok(normalized) = normalized {
|
||||
// if sum goes beyond u128, panic.
|
||||
let sum: u128 = normalized.iter().map(|x| *x as u128).sum();
|
||||
|
||||
// if this function returns Ok(), then it will ALWAYS be accurate.
|
||||
assert_eq!(sum, norm as u128, "sums don't match {:?}, {}", normalized, norm);
|
||||
} else {
|
||||
panic!("Should have returned Ok for input = {:?}, target = {:?}", data, norm);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run per_thing_from_rational`. `honggfuzz` CLI
|
||||
//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
|
||||
//!
|
||||
//! # Debugging a panic
|
||||
//! Once a panic is found, it can be debugged with
|
||||
//! `cargo hfuzz run-debug per_thing_from_rational hfuzz_workspace/per_thing_from_rational/*.fuzz`.
|
||||
|
||||
use fraction::prelude::BigFraction as Fraction;
|
||||
use honggfuzz::fuzz;
|
||||
use pezsp_arithmetic::{
|
||||
traits::SaturatedConversion, PerThing, Perbill, Percent, Perquintill, Rounding::*, *,
|
||||
};
|
||||
|
||||
/// Tries to demonstrate that `from_rational` is incorrect for any rounding modes.
|
||||
///
|
||||
/// NOTE: This `Fraction` library is really slow. Using f128/f256 does not work for the large
|
||||
/// numbers. But an optimization could be done do use either floats or Fraction depending on the
|
||||
/// size of the inputs.
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (u128, u128, ArbitraryRounding)| {
|
||||
let (n, d, r) = (data.0.min(data.1), data.0.max(data.1).max(1), data.2);
|
||||
|
||||
check::<PerU16>(n, d, r.0);
|
||||
check::<Percent>(n, d, r.0);
|
||||
check::<Permill>(n, d, r.0);
|
||||
check::<Perbill>(n, d, r.0);
|
||||
check::<Perquintill>(n, d, r.0);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that the parts of `from_rational` are correct for the given rounding mode.
|
||||
fn check<Per: PerThing>(a: u128, b: u128, r: Rounding)
|
||||
where
|
||||
Per::Inner: Into<u128>,
|
||||
{
|
||||
let approx_ratio = Per::from_rational_with_rounding(a, b, r).unwrap();
|
||||
let approx_parts = Fraction::from(approx_ratio.deconstruct().saturated_into::<u128>());
|
||||
|
||||
let perfect_ratio = if a == 0 && b == 0 {
|
||||
Fraction::from(1)
|
||||
} else {
|
||||
Fraction::from(a) / Fraction::from(b.max(1))
|
||||
};
|
||||
let perfect_parts = round(perfect_ratio * Fraction::from(Per::ACCURACY.into()), r);
|
||||
|
||||
assert_eq!(
|
||||
approx_parts, perfect_parts,
|
||||
"approx_parts: {}, perfect_parts: {}, a: {}, b: {}",
|
||||
approx_parts, perfect_parts, a, b
|
||||
);
|
||||
}
|
||||
|
||||
/// Round a `Fraction` according to the given mode.
|
||||
fn round(f: Fraction, r: Rounding) -> Fraction {
|
||||
match r {
|
||||
Up => f.ceil(),
|
||||
NearestPrefUp =>
|
||||
if f.fract() < Fraction::from(0.5) {
|
||||
f.floor()
|
||||
} else {
|
||||
f.ceil()
|
||||
},
|
||||
Down => f.floor(),
|
||||
NearestPrefDown =>
|
||||
if f.fract() > Fraction::from(0.5) {
|
||||
f.ceil()
|
||||
} else {
|
||||
f.floor()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`arbitrary::Arbitrary`] [`Rounding`] mode.
|
||||
struct ArbitraryRounding(Rounding);
|
||||
impl arbitrary::Arbitrary<'_> for ArbitraryRounding {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
Ok(Self(match u.int_in_range(0..=3).unwrap() {
|
||||
0 => Up,
|
||||
1 => NearestPrefUp,
|
||||
2 => Down,
|
||||
3 => NearestPrefDown,
|
||||
_ => unreachable!(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run per_thing_mult_fraction`. `honggfuzz` CLI
|
||||
//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
|
||||
//!
|
||||
//! # Debugging a panic
|
||||
//! Once a panic is found, it can be debugged with
|
||||
//! `cargo hfuzz run-debug per_thing_mult_fraction hfuzz_workspace/per_thing_mult_fraction/*.fuzz`.
|
||||
|
||||
use honggfuzz::fuzz;
|
||||
use pezsp_arithmetic::{PerThing, Perbill, Percent, Perquintill, *};
|
||||
|
||||
/// Tries to disprove `(n / d) * d <= n` for any `PerThing`s.
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (u128, u128)| {
|
||||
let (n, d) = (data.0.min(data.1), data.0.max(data.1).max(1));
|
||||
|
||||
check_mul::<PerU16>(n, d);
|
||||
check_mul::<Percent>(n, d);
|
||||
check_mul::<Perbill>(n, d);
|
||||
check_mul::<Perquintill>(n, d);
|
||||
|
||||
check_reciprocal_mul::<PerU16>(n, d);
|
||||
check_reciprocal_mul::<Percent>(n, d);
|
||||
check_reciprocal_mul::<Perbill>(n, d);
|
||||
check_reciprocal_mul::<Perquintill>(n, d);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that `(n / d) * d <= n`.
|
||||
fn check_mul<P: PerThing>(n: u128, d: u128)
|
||||
where
|
||||
P: PerThing + core::ops::Mul<u128, Output = u128>,
|
||||
{
|
||||
let q = P::from_rational_with_rounding(n, d, Rounding::Down).unwrap();
|
||||
assert!(q * d <= n, "{:?} * {:?} <= {:?}", q, d, n);
|
||||
}
|
||||
|
||||
/// Checks that `n / (n / d) >= d`.
|
||||
fn check_reciprocal_mul<P: PerThing>(n: u128, d: u128)
|
||||
where
|
||||
P: PerThing + core::ops::Mul<u128, Output = u128>,
|
||||
{
|
||||
let q = P::from_rational_with_rounding(n, d, Rounding::Down).unwrap();
|
||||
if q.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
let r = q.saturating_reciprocal_mul_floor(n);
|
||||
assert!(r >= d, "{} / ({} / {}) != {} but {}", n, n, d, d, r);
|
||||
}
|
||||
@@ -0,0 +1,755 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Infinite precision unsigned integer for bizinikiwi runtime.
|
||||
|
||||
use alloc::{vec, vec::Vec};
|
||||
use codec::{Decode, Encode};
|
||||
use core::{cell::RefCell, cmp::Ordering, ops};
|
||||
use num_traits::{One, Zero};
|
||||
|
||||
// A sensible value for this would be half of the dword size of the host machine. Since the
|
||||
// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively
|
||||
// should yield the most performance.
|
||||
|
||||
/// Representation of a single limb.
|
||||
pub type Single = u32;
|
||||
/// Representation of two limbs.
|
||||
pub type Double = u64;
|
||||
/// Difference in the number of bits of [`Single`] and [`Double`].
|
||||
const SHIFT: usize = 32;
|
||||
/// short form of _Base_. Analogous to the value 10 in base-10 decimal numbers.
|
||||
const B: Double = Single::max_value() as Double + 1;
|
||||
|
||||
static_assertions::const_assert!(
|
||||
core::mem::size_of::<Double>() - core::mem::size_of::<Single>() == SHIFT / 8
|
||||
);
|
||||
|
||||
/// Splits a [`Double`] limb number into a tuple of two [`Single`] limb numbers.
|
||||
pub fn split(a: Double) -> (Single, Single) {
|
||||
let al = a as Single;
|
||||
let ah = (a >> SHIFT) as Single;
|
||||
(ah, al)
|
||||
}
|
||||
|
||||
/// Assumed as a given primitive.
|
||||
///
|
||||
/// Multiplication of two singles, which at most yields 1 double.
|
||||
pub fn mul_single(a: Single, b: Single) -> Double {
|
||||
let a: Double = a.into();
|
||||
let b: Double = b.into();
|
||||
a * b
|
||||
}
|
||||
|
||||
/// Assumed as a given primitive.
|
||||
///
|
||||
/// Addition of two singles, which at most takes a single limb of result and a carry,
|
||||
/// returned as a tuple respectively.
|
||||
pub fn add_single(a: Single, b: Single) -> (Single, Single) {
|
||||
let a: Double = a.into();
|
||||
let b: Double = b.into();
|
||||
let q = a + b;
|
||||
let (carry, r) = split(q);
|
||||
(r, carry)
|
||||
}
|
||||
|
||||
/// Assumed as a given primitive.
|
||||
///
|
||||
/// Division of double by a single limb. Always returns a double limb of quotient and a single
|
||||
/// limb of remainder.
|
||||
fn div_single(a: Double, b: Single) -> (Double, Single) {
|
||||
let b: Double = b.into();
|
||||
let q = a / b;
|
||||
let r = a % b;
|
||||
// both conversions are trivially safe.
|
||||
(q, r as Single)
|
||||
}
|
||||
|
||||
/// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`].
|
||||
#[derive(Encode, Decode, Clone, Default)]
|
||||
pub struct BigUint {
|
||||
/// digits (limbs) of this number (sorted as msb -> lsb).
|
||||
pub(crate) digits: Vec<Single>,
|
||||
}
|
||||
|
||||
impl BigUint {
|
||||
/// Create a new instance with `size` limbs. This prevents any number with zero limbs to be
|
||||
/// created.
|
||||
///
|
||||
/// The behavior of the type is undefined with zero limbs.
|
||||
pub fn with_capacity(size: usize) -> Self {
|
||||
Self { digits: vec![0; size.max(1)] }
|
||||
}
|
||||
|
||||
/// Raw constructor from custom limbs. If `limbs` is empty, `Zero::zero()` implementation is
|
||||
/// used.
|
||||
pub fn from_limbs(limbs: &[Single]) -> Self {
|
||||
if !limbs.is_empty() {
|
||||
Self { digits: limbs.to_vec() }
|
||||
} else {
|
||||
Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of limbs.
|
||||
pub fn len(&self) -> usize {
|
||||
self.digits.len()
|
||||
}
|
||||
|
||||
/// A naive getter for limb at `index`. Note that the order is lsb -> msb.
|
||||
///
|
||||
/// #### Panics
|
||||
///
|
||||
/// This panics if index is out of range.
|
||||
pub fn get(&self, index: usize) -> Single {
|
||||
self.digits[self.len() - 1 - index]
|
||||
}
|
||||
|
||||
/// A naive getter for limb at `index`. Note that the order is lsb -> msb.
|
||||
pub fn checked_get(&self, index: usize) -> Option<Single> {
|
||||
let i = self.len().checked_sub(1)?;
|
||||
let j = i.checked_sub(index)?;
|
||||
self.digits.get(j).cloned()
|
||||
}
|
||||
|
||||
/// A naive setter for limb at `index`. Note that the order is lsb -> msb.
|
||||
///
|
||||
/// #### Panics
|
||||
///
|
||||
/// This panics if index is out of range.
|
||||
pub fn set(&mut self, index: usize, value: Single) {
|
||||
let len = self.digits.len();
|
||||
self.digits[len - 1 - index] = value;
|
||||
}
|
||||
|
||||
/// returns the least significant limb of the number.
|
||||
///
|
||||
/// #### Panics
|
||||
///
|
||||
/// While the constructor of the type prevents this, this can panic if `self` has no digits.
|
||||
pub fn lsb(&self) -> Single {
|
||||
self.digits[self.len() - 1]
|
||||
}
|
||||
|
||||
/// returns the most significant limb of the number.
|
||||
///
|
||||
/// #### Panics
|
||||
///
|
||||
/// While the constructor of the type prevents this, this can panic if `self` has no digits.
|
||||
pub fn msb(&self) -> Single {
|
||||
self.digits[0]
|
||||
}
|
||||
|
||||
/// Strips zeros from the left side (the most significant limbs) of `self`, if any.
|
||||
pub fn lstrip(&mut self) {
|
||||
// by definition, a big-int number should never have leading zero limbs. This function
|
||||
// has the ability to cause this. There is nothing to do if the number already has 1
|
||||
// limb only. call it a day and return.
|
||||
if self.len().is_zero() {
|
||||
return;
|
||||
}
|
||||
let index = self.digits.iter().position(|&elem| elem != 0).unwrap_or(self.len() - 1);
|
||||
|
||||
if index > 0 {
|
||||
self.digits = self.digits[index..].to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero-pad `self` from left to reach `size` limbs. Will not make any difference if `self`
|
||||
/// is already bigger than `size` limbs.
|
||||
pub fn lpad(&mut self, size: usize) {
|
||||
let n = self.len();
|
||||
if n >= size {
|
||||
return;
|
||||
}
|
||||
let pad = size - n;
|
||||
let mut new_digits = (0..pad).map(|_| 0).collect::<Vec<Single>>();
|
||||
new_digits.extend(self.digits.iter());
|
||||
self.digits = new_digits;
|
||||
}
|
||||
|
||||
/// Adds `self` with `other`. self and other do not have to have any particular size. Given
|
||||
/// that the `n = max{size(self), size(other)}`, it will produce a number with `n + 1`
|
||||
/// limbs.
|
||||
///
|
||||
/// This function does not strip the output and returns the original allocated `n + 1`
|
||||
/// limbs. The caller may strip the output if desired.
|
||||
///
|
||||
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
|
||||
pub fn add(self, other: &Self) -> Self {
|
||||
let n = self.len().max(other.len());
|
||||
let mut k: Double = 0;
|
||||
let mut w = Self::with_capacity(n + 1);
|
||||
|
||||
for j in 0..n {
|
||||
let u = Double::from(self.checked_get(j).unwrap_or(0));
|
||||
let v = Double::from(other.checked_get(j).unwrap_or(0));
|
||||
let s = u + v + k;
|
||||
// proof: any number % B will fit into `Single`.
|
||||
w.set(j, (s % B) as Single);
|
||||
k = s / B;
|
||||
}
|
||||
// k is always 0 or 1.
|
||||
w.set(n, k as Single);
|
||||
w
|
||||
}
|
||||
|
||||
/// Subtracts `other` from `self`. self and other do not have to have any particular size.
|
||||
/// Given that the `n = max{size(self), size(other)}`, it will produce a number of size `n`.
|
||||
///
|
||||
/// If `other` is bigger than `self`, `Err(B - borrow)` is returned.
|
||||
///
|
||||
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
|
||||
pub fn sub(self, other: &Self) -> Result<Self, Self> {
|
||||
let n = self.len().max(other.len());
|
||||
let mut k = 0;
|
||||
let mut w = Self::with_capacity(n);
|
||||
for j in 0..n {
|
||||
let s = {
|
||||
let u = Double::from(self.checked_get(j).unwrap_or(0));
|
||||
let v = Double::from(other.checked_get(j).unwrap_or(0));
|
||||
|
||||
if let Some(v2) = u.checked_sub(v).and_then(|v1| v1.checked_sub(k)) {
|
||||
// no borrow is needed. u - v - k can be computed as-is
|
||||
let t = v2;
|
||||
k = 0;
|
||||
|
||||
t
|
||||
} else {
|
||||
// borrow is needed. Add a `B` to u, before subtracting.
|
||||
// PROOF: addition: `u + B < 2*B`, thus can fit in double.
|
||||
// PROOF: subtraction: if `u - v - k < 0`, then `u + B - v - k < B`.
|
||||
// NOTE: the order of operations is critical to ensure underflow won't happen.
|
||||
let t = u + B - v - k;
|
||||
k = 1;
|
||||
|
||||
t
|
||||
}
|
||||
};
|
||||
w.set(j, s as Single);
|
||||
}
|
||||
|
||||
if k.is_zero() {
|
||||
Ok(w)
|
||||
} else {
|
||||
Err(w)
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies n-limb number `self` with m-limb number `other`.
|
||||
///
|
||||
/// The resulting number will always have `n + m` limbs.
|
||||
///
|
||||
/// This function does not strip the output and returns the original allocated `n + m`
|
||||
/// limbs. The caller may strip the output if desired.
|
||||
///
|
||||
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
|
||||
pub fn mul(self, other: &Self) -> Self {
|
||||
let n = self.len();
|
||||
let m = other.len();
|
||||
let mut w = Self::with_capacity(m + n);
|
||||
|
||||
for j in 0..n {
|
||||
if self.get(j) == 0 {
|
||||
// Note: `with_capacity` allocates with 0. Explicitly set j + m to zero if
|
||||
// otherwise.
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut k = 0;
|
||||
for i in 0..m {
|
||||
// PROOF: (B−1) × (B−1) + (B−1) + (B−1) = B^2 −1 < B^2. addition is safe.
|
||||
let t = mul_single(self.get(j), other.get(i)) +
|
||||
Double::from(w.get(i + j)) +
|
||||
Double::from(k);
|
||||
w.set(i + j, (t % B) as Single);
|
||||
// PROOF: (B^2 - 1) / B < B. conversion is safe.
|
||||
k = (t / B) as Single;
|
||||
}
|
||||
w.set(j + m, k);
|
||||
}
|
||||
w
|
||||
}
|
||||
|
||||
/// Divides `self` by a single limb `other`. This can be used in cases where the original
|
||||
/// division cannot work due to the divisor (`other`) being just one limb.
|
||||
///
|
||||
/// Invariant: `other` cannot be zero.
|
||||
pub fn div_unit(self, mut other: Single) -> Self {
|
||||
other = other.max(1);
|
||||
let n = self.len();
|
||||
let mut out = Self::with_capacity(n);
|
||||
let mut r: Single = 0;
|
||||
// PROOF: (B-1) * B + (B-1) still fits in double
|
||||
let with_r = |x: Single, r: Single| Double::from(r) * B + Double::from(x);
|
||||
for d in (0..n).rev() {
|
||||
let (q, rr) = div_single(with_r(self.get(d), r), other);
|
||||
out.set(d, q as Single);
|
||||
r = rr;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Divides an `n + m` limb self by a `n` limb `other`. The result is a `m + 1` limb
|
||||
/// quotient and a `n` limb remainder, if enabled by passing `true` in `rem` argument, both
|
||||
/// in the form of an option's `Ok`.
|
||||
///
|
||||
/// - requires `other` to be stripped and have no leading zeros.
|
||||
/// - requires `self` to be stripped and have no leading zeros.
|
||||
/// - requires `other` to have at least two limbs.
|
||||
/// - requires `self` to have a greater length compared to `other`.
|
||||
///
|
||||
/// All arguments are examined without being stripped for the above conditions. If any of
|
||||
/// the above fails, `None` is returned.`
|
||||
///
|
||||
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
|
||||
pub fn div(self, other: &Self, rem: bool) -> Option<(Self, Self)> {
|
||||
if other.len() <= 1 || other.msb() == 0 || self.msb() == 0 || self.len() <= other.len() {
|
||||
return None;
|
||||
}
|
||||
let n = other.len();
|
||||
let m = self.len() - n;
|
||||
|
||||
let mut q = Self::with_capacity(m + 1);
|
||||
let mut r = Self::with_capacity(n);
|
||||
|
||||
// PROOF: 0 <= normalizer_bits < SHIFT 0 <= normalizer < B. all conversions are
|
||||
// safe.
|
||||
let normalizer_bits = other.msb().leading_zeros() as Single;
|
||||
let normalizer = 2_u32.pow(normalizer_bits as u32) as Single;
|
||||
|
||||
// step D1.
|
||||
let mut self_norm = self.mul(&Self::from(normalizer));
|
||||
let mut other_norm = other.clone().mul(&Self::from(normalizer));
|
||||
|
||||
// defensive only; the mul implementation should always create this.
|
||||
self_norm.lpad(n + m + 1);
|
||||
other_norm.lstrip();
|
||||
|
||||
// step D2.
|
||||
for j in (0..=m).rev() {
|
||||
// step D3.0 Find an estimate of q[j], named qhat.
|
||||
let (qhat, rhat) = {
|
||||
// PROOF: this always fits into `Double`. In the context of Single = u8, and
|
||||
// Double = u16, think of 255 * 256 + 255 which is just u16::MAX.
|
||||
let dividend =
|
||||
Double::from(self_norm.get(j + n)) * B + Double::from(self_norm.get(j + n - 1));
|
||||
let divisor = other_norm.get(n - 1);
|
||||
div_single(dividend, divisor)
|
||||
};
|
||||
|
||||
// D3.1 test qhat
|
||||
// replace qhat and rhat with RefCells. This helps share state with the closure
|
||||
let qhat = RefCell::new(qhat);
|
||||
let rhat = RefCell::new(Double::from(rhat));
|
||||
|
||||
let test = || {
|
||||
// decrease qhat if it is bigger than the base (B)
|
||||
let qhat_local = *qhat.borrow();
|
||||
let rhat_local = *rhat.borrow();
|
||||
let predicate_1 = qhat_local >= B;
|
||||
let predicate_2 = {
|
||||
let lhs = qhat_local * Double::from(other_norm.get(n - 2));
|
||||
let rhs = B * rhat_local + Double::from(self_norm.get(j + n - 2));
|
||||
lhs > rhs
|
||||
};
|
||||
if predicate_1 || predicate_2 {
|
||||
*qhat.borrow_mut() -= 1;
|
||||
*rhat.borrow_mut() += Double::from(other_norm.get(n - 1));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
test();
|
||||
while (*rhat.borrow() as Double) < B {
|
||||
if !test() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let qhat = qhat.into_inner();
|
||||
// we don't need rhat anymore. just let it go out of scope when it does.
|
||||
|
||||
// step D4
|
||||
let lhs = Self { digits: (j..=j + n).rev().map(|d| self_norm.get(d)).collect() };
|
||||
let rhs = other_norm.clone().mul(&Self::from(qhat));
|
||||
|
||||
let maybe_sub = lhs.sub(&rhs);
|
||||
let mut negative = false;
|
||||
let sub = match maybe_sub {
|
||||
Ok(t) => t,
|
||||
Err(t) => {
|
||||
negative = true;
|
||||
t
|
||||
},
|
||||
};
|
||||
(j..=j + n).for_each(|d| {
|
||||
self_norm.set(d, sub.get(d - j));
|
||||
});
|
||||
|
||||
// step D5
|
||||
// PROOF: the `test()` specifically decreases qhat until it is below `B`. conversion
|
||||
// is safe.
|
||||
q.set(j, qhat as Single);
|
||||
|
||||
// step D6: add back if negative happened.
|
||||
if negative {
|
||||
q.set(j, q.get(j) - 1);
|
||||
let u = Self { digits: (j..=j + n).rev().map(|d| self_norm.get(d)).collect() };
|
||||
let r = other_norm.clone().add(&u);
|
||||
(j..=j + n).rev().for_each(|d| {
|
||||
self_norm.set(d, r.get(d - j));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// if requested, calculate remainder.
|
||||
if rem {
|
||||
// undo the normalization.
|
||||
if normalizer_bits > 0 {
|
||||
let s = SHIFT as u32;
|
||||
let nb = normalizer_bits;
|
||||
for d in 0..n - 1 {
|
||||
let v =
|
||||
(self_norm.get(d) >> nb) | self_norm.get(d + 1).overflowing_shl(s - nb).0;
|
||||
r.set(d, v);
|
||||
}
|
||||
r.set(n - 1, self_norm.get(n - 1) >> normalizer_bits);
|
||||
} else {
|
||||
r = self_norm;
|
||||
}
|
||||
}
|
||||
|
||||
Some((q, r))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for BigUint {
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"BigUint {{ {:?} ({:?})}}",
|
||||
self.digits,
|
||||
u128::try_from(self.clone()).unwrap_or(0),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BigUint {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other) == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BigUint {}
|
||||
|
||||
impl Ord for BigUint {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let lhs_first = self.digits.iter().position(|&e| e != 0);
|
||||
let rhs_first = other.digits.iter().position(|&e| e != 0);
|
||||
|
||||
match (lhs_first, rhs_first) {
|
||||
// edge cases that should not happen. This basically means that one or both were
|
||||
// zero.
|
||||
(None, None) => Ordering::Equal,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(lhs_idx), Some(rhs_idx)) => {
|
||||
let lhs = &self.digits[lhs_idx..];
|
||||
let rhs = &other.digits[rhs_idx..];
|
||||
let len_cmp = lhs.len().cmp(&rhs.len());
|
||||
match len_cmp {
|
||||
Ordering::Equal => lhs.cmp(rhs),
|
||||
_ => len_cmp,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for BigUint {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add for BigUint {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
self.add(&rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for BigUint {
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self.sub(&rhs).unwrap_or_else(|e| e)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul for BigUint {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
self.mul(&rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for BigUint {
|
||||
fn zero() -> Self {
|
||||
Self { digits: vec![Zero::zero()] }
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.digits.iter().all(|d| d.is_zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl One for BigUint {
|
||||
fn one() -> Self {
|
||||
Self { digits: vec![Single::one()] }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_try_from_number_for {
|
||||
($([$type:ty, $len:expr]),+) => {
|
||||
$(
|
||||
impl TryFrom<BigUint> for $type {
|
||||
type Error = &'static str;
|
||||
fn try_from(mut value: BigUint) -> Result<$type, Self::Error> {
|
||||
value.lstrip();
|
||||
let error_message = concat!("cannot fit a number into ", stringify!($type));
|
||||
if value.len() * SHIFT > $len {
|
||||
Err(error_message)
|
||||
} else {
|
||||
let mut acc: $type = Zero::zero();
|
||||
for (i, d) in value.digits.iter().rev().cloned().enumerate() {
|
||||
let d: $type = d.into();
|
||||
acc += d << (SHIFT * i);
|
||||
}
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
// can only be implemented for sizes bigger than two limb.
|
||||
impl_try_from_number_for!([u128, 128], [u64, 64]);
|
||||
|
||||
macro_rules! impl_from_for_smaller_than_word {
|
||||
($($type:ty),+) => {
|
||||
$(impl From<$type> for BigUint {
|
||||
fn from(a: $type) -> Self {
|
||||
Self { digits: vec! [a.into()] }
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
impl_from_for_smaller_than_word!(u8, u16, u32);
|
||||
|
||||
impl From<u64> for BigUint {
|
||||
fn from(a: Double) -> Self {
|
||||
let (ah, al) = split(a);
|
||||
Self { digits: vec![ah, al] }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for BigUint {
|
||||
fn from(a: u128) -> Self {
|
||||
crate::helpers_128bit::to_big_uint(a)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
fn with_limbs(n: usize) -> BigUint {
|
||||
BigUint { digits: vec![1; n] }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_works() {
|
||||
let a = SHIFT / 2;
|
||||
let b = SHIFT * 3 / 2;
|
||||
let num: Double = (1 << a) | (1 << b);
|
||||
assert_eq!(num, 0x_0001_0000_0001_0000);
|
||||
assert_eq!(split(num), (1 << a, 1 << a));
|
||||
|
||||
let a = SHIFT / 2 + 4;
|
||||
let b = SHIFT / 2 - 4;
|
||||
let num: Double = (1 << (SHIFT + a)) | (1 << b);
|
||||
assert_eq!(num, 0x_0010_0000_0000_1000);
|
||||
assert_eq!(split(num), (1 << a, 1 << b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_works() {
|
||||
let mut a = BigUint::from_limbs(&[0, 1, 0]);
|
||||
a.lstrip();
|
||||
assert_eq!(a.digits, vec![1, 0]);
|
||||
|
||||
let mut a = BigUint::from_limbs(&[0, 0, 1]);
|
||||
a.lstrip();
|
||||
assert_eq!(a.digits, vec![1]);
|
||||
|
||||
let mut a = BigUint::from_limbs(&[0, 0]);
|
||||
a.lstrip();
|
||||
assert_eq!(a.digits, vec![0]);
|
||||
|
||||
let mut a = BigUint::from_limbs(&[0, 0, 0]);
|
||||
a.lstrip();
|
||||
assert_eq!(a.digits, vec![0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lpad_works() {
|
||||
let mut a = BigUint::from_limbs(&[0, 1, 0]);
|
||||
a.lpad(2);
|
||||
assert_eq!(a.digits, vec![0, 1, 0]);
|
||||
|
||||
let mut a = BigUint::from_limbs(&[0, 1, 0]);
|
||||
a.lpad(3);
|
||||
assert_eq!(a.digits, vec![0, 1, 0]);
|
||||
|
||||
let mut a = BigUint::from_limbs(&[0, 1, 0]);
|
||||
a.lpad(4);
|
||||
assert_eq!(a.digits, vec![0, 0, 1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality_works() {
|
||||
assert_eq!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, true);
|
||||
assert_eq!(BigUint { digits: vec![3, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, false);
|
||||
assert_eq!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordering_works() {
|
||||
assert!(BigUint { digits: vec![0] } < BigUint { digits: vec![1] });
|
||||
assert!(BigUint { digits: vec![0] } == BigUint { digits: vec![0] });
|
||||
assert!(BigUint { digits: vec![] } == BigUint { digits: vec![0] });
|
||||
assert!(BigUint { digits: vec![] } == BigUint { digits: vec![] });
|
||||
assert!(BigUint { digits: vec![] } < BigUint { digits: vec![1] });
|
||||
|
||||
assert!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] });
|
||||
assert!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] });
|
||||
|
||||
assert!(BigUint { digits: vec![1, 2, 4] } > BigUint { digits: vec![1, 2, 3] });
|
||||
assert!(BigUint { digits: vec![0, 1, 2, 4] } > BigUint { digits: vec![1, 2, 3] });
|
||||
assert!(BigUint { digits: vec![1, 2, 1, 0] } > BigUint { digits: vec![1, 2, 3] });
|
||||
|
||||
assert!(BigUint { digits: vec![0, 1, 2, 1] } < BigUint { digits: vec![1, 2, 3] });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_try_build_numbers_from_types() {
|
||||
assert_eq!(u64::try_from(with_limbs(1)).unwrap(), 1);
|
||||
assert_eq!(u64::try_from(with_limbs(2)).unwrap(), u32::MAX as u64 + 2);
|
||||
assert_eq!(u64::try_from(with_limbs(3)).unwrap_err(), "cannot fit a number into u64");
|
||||
assert_eq!(u128::try_from(with_limbs(3)).unwrap(), u32::MAX as u128 + u64::MAX as u128 + 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_works() {
|
||||
assert_eq!(BigUint::zero(), BigUint { digits: vec![0] });
|
||||
assert_eq!(BigUint { digits: vec![0, 1, 0] }.is_zero(), false);
|
||||
assert_eq!(BigUint { digits: vec![0, 0, 0] }.is_zero(), true);
|
||||
|
||||
let a = BigUint::zero();
|
||||
let b = BigUint::zero();
|
||||
let c = a * b;
|
||||
assert_eq!(c.digits, vec![0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_negative_works() {
|
||||
assert_eq!(
|
||||
BigUint::from(10 as Single).sub(&BigUint::from(5 as Single)).unwrap(),
|
||||
BigUint::from(5 as Single)
|
||||
);
|
||||
assert_eq!(
|
||||
BigUint::from(10 as Single).sub(&BigUint::from(10 as Single)).unwrap(),
|
||||
BigUint::from(0 as Single)
|
||||
);
|
||||
assert_eq!(
|
||||
BigUint::from(10 as Single).sub(&BigUint::from(13 as Single)).unwrap_err(),
|
||||
BigUint::from((B - 3) as Single),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mul_always_appends_one_digit() {
|
||||
let a = BigUint::from(10 as Single);
|
||||
let b = BigUint::from(4 as Single);
|
||||
assert_eq!(a.len(), 1);
|
||||
assert_eq!(b.len(), 1);
|
||||
|
||||
let n = a.mul(&b);
|
||||
|
||||
assert_eq!(n.len(), 2);
|
||||
assert_eq!(n.digits, vec![0, 40]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_conditions_work() {
|
||||
let a = BigUint { digits: vec![2] };
|
||||
let b = BigUint { digits: vec![1, 2] };
|
||||
let c = BigUint { digits: vec![1, 1, 2] };
|
||||
let d = BigUint { digits: vec![0, 2] };
|
||||
let e = BigUint { digits: vec![0, 1, 1, 2] };
|
||||
let f = BigUint { digits: vec![7, 8] };
|
||||
|
||||
assert!(a.clone().div(&b, true).is_none());
|
||||
assert!(c.clone().div(&a, true).is_none());
|
||||
assert!(c.clone().div(&d, true).is_none());
|
||||
assert!(e.clone().div(&a, true).is_none());
|
||||
|
||||
assert!(f.clone().div(&b, true).is_none());
|
||||
assert!(c.clone().div(&b, true).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_unit_works() {
|
||||
let a = BigUint { digits: vec![100] };
|
||||
let b = BigUint { digits: vec![1, 100] };
|
||||
let c = BigUint { digits: vec![14, 28, 100] };
|
||||
|
||||
assert_eq!(a.clone().div_unit(1), a);
|
||||
assert_eq!(a.clone().div_unit(0), a);
|
||||
assert_eq!(a.clone().div_unit(2), BigUint::from(50 as Single));
|
||||
assert_eq!(a.clone().div_unit(7), BigUint::from(14 as Single));
|
||||
|
||||
assert_eq!(b.clone().div_unit(1), b);
|
||||
assert_eq!(b.clone().div_unit(0), b);
|
||||
assert_eq!(b.clone().div_unit(2), BigUint::from(((B + 100) / 2) as Single));
|
||||
assert_eq!(b.clone().div_unit(7), BigUint::from(((B + 100) / 7) as Single));
|
||||
|
||||
assert_eq!(c.clone().div_unit(1), c);
|
||||
assert_eq!(c.clone().div_unit(0), c);
|
||||
assert_eq!(c.clone().div_unit(2), BigUint { digits: vec![7, 14, 50] });
|
||||
assert_eq!(c.clone().div_unit(7), BigUint { digits: vec![2, 4, 14] });
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user