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:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
+100
View File
@@ -0,0 +1,100 @@
[package]
name = "pezsp-runtime"
version = "31.0.1"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Runtime Modules shared primitive types."
documentation = "https://docs.rs/pezsp-runtime"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
binary-merkle-tree = { workspace = true }
bytes = { workspace = true }
codec = { features = ["derive", "max-encoded-len"], workspace = true }
docify = { workspace = true }
either = { workspace = true }
hash256-std-hasher = { workspace = true }
impl-trait-for-tuples = { workspace = true }
log = { workspace = true }
num-traits = { workspace = true }
paste = { workspace = true, default-features = true }
rand = { optional = true, workspace = true, default-features = true }
scale-info = { features = ["derive"], workspace = true }
serde = { features = ["alloc", "derive"], optional = true, workspace = true }
pezsp-application-crypto = { workspace = true }
pezsp-arithmetic = { workspace = true }
pezsp-core = { workspace = true }
pezsp-io = { workspace = true }
pezsp-std = { workspace = true }
pezsp-trie = { workspace = true }
pezsp-weights = { workspace = true }
tracing = { workspace = true, features = ["log"], default-features = false }
simple-mermaid = { workspace = true, optional = true }
tuplex = { workspace = true, default-features = false }
[dev-dependencies]
rand = { workspace = true, default-features = true }
serde_json = { workspace = true, default-features = true }
pezsp-api = { workspace = true, default-features = true }
pezsp-state-machine = { workspace = true, default-features = true }
pezsp-tracing = { workspace = true, default-features = true }
bizinikiwi-test-runtime-client = { workspace = true }
zstd = { workspace = true }
[features]
runtime-benchmarks = [
"binary-merkle-tree/runtime-benchmarks",
"pezsp-api/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-state-machine/runtime-benchmarks",
"pezsp-trie/runtime-benchmarks",
"bizinikiwi-test-runtime-client/runtime-benchmarks",
]
try-runtime = []
default = ["std"]
std = [
"binary-merkle-tree/std",
"bytes/std",
"codec/std",
"either/std",
"either/use_std",
"hash256-std-hasher/std",
"log/std",
"num-traits/std",
"rand",
"scale-info/std",
"serde/std",
"simple-mermaid",
"pezsp-api/std",
"pezsp-application-crypto/std",
"pezsp-arithmetic/std",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-state-machine/std",
"pezsp-std/std",
"pezsp-tracing/std",
"pezsp-trie/std",
"pezsp-weights/std",
"tracing/std",
"tuplex/std",
]
# Serde support without relying on std features.
serde = [
"dep:serde",
"scale-info/serde",
"pezsp-application-crypto/serde",
"pezsp-arithmetic/serde",
"pezsp-core/serde",
"pezsp-weights/serde",
]
+3
View File
@@ -0,0 +1,3 @@
Runtime Modules shared primitive types.
License: Apache-2.0
+1
View File
@@ -0,0 +1 @@
../../../docs
+163
View File
@@ -0,0 +1,163 @@
// 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.
//! Provides some utilities to define a piecewise linear function.
use crate::{
traits::{AtLeast32BitUnsigned, SaturatedConversion},
Perbill,
};
use core::ops::Sub;
use scale_info::TypeInfo;
/// Piecewise Linear function in [0, 1] -> [0, 1].
#[derive(PartialEq, Eq, pezsp_core::RuntimeDebug, TypeInfo)]
pub struct PiecewiseLinear<'a> {
/// Array of points. Must be in order from the lowest abscissas to the highest.
pub points: &'a [(Perbill, Perbill)],
/// The maximum value that can be returned.
pub maximum: Perbill,
}
fn abs_sub<N: Ord + Sub<Output = N> + Clone>(a: N, b: N) -> N where {
a.clone().max(b.clone()) - a.min(b)
}
impl<'a> PiecewiseLinear<'a> {
/// Compute `f(n/d)*d` with `n <= d`. This is useful to avoid loss of precision.
pub fn calculate_for_fraction_times_denominator<N>(&self, n: N, d: N) -> N
where
N: AtLeast32BitUnsigned + Clone,
{
let n = n.min(d.clone());
if self.points.is_empty() {
return N::zero();
}
let next_point_index = self.points.iter().position(|p| n < p.0 * d.clone());
let (prev, next) = if let Some(next_point_index) = next_point_index {
if let Some(previous_point_index) = next_point_index.checked_sub(1) {
(self.points[previous_point_index], self.points[next_point_index])
} else {
// There is no previous points, take first point ordinate
return self.points.first().map(|p| p.1).unwrap_or_else(Perbill::zero) * d;
}
} else {
// There is no next points, take last point ordinate
return self.points.last().map(|p| p.1).unwrap_or_else(Perbill::zero) * d;
};
let delta_y = multiply_by_rational_saturating(
abs_sub(n.clone(), prev.0 * d.clone()),
abs_sub(next.1.deconstruct(), prev.1.deconstruct()),
// Must not saturate as prev abscissa > next abscissa
next.0.deconstruct().saturating_sub(prev.0.deconstruct()),
);
// If both subtractions are same sign then result is positive
if (n > prev.0 * d.clone()) == (next.1.deconstruct() > prev.1.deconstruct()) {
(prev.1 * d).saturating_add(delta_y)
// Otherwise result is negative
} else {
(prev.1 * d).saturating_sub(delta_y)
}
}
}
// Compute value * p / q.
// This is guaranteed not to overflow on whatever values nor lose precision.
// `q` must be superior to zero.
fn multiply_by_rational_saturating<N>(value: N, p: u32, q: u32) -> N
where
N: AtLeast32BitUnsigned + Clone,
{
let q = q.max(1);
// Mul can saturate if p > q
let result_divisor_part = (value.clone() / q.into()).saturating_mul(p.into());
let result_remainder_part = {
let rem = value % q.into();
// Fits into u32 because q is u32 and remainder < q
let rem_u32 = rem.saturated_into::<u32>();
// Multiplication fits into u64 as both term are u32
let rem_part = rem_u32 as u64 * p as u64 / q as u64;
// Can saturate if p > q
rem_part.saturated_into::<N>()
};
// Can saturate if p > q
result_divisor_part.saturating_add(result_remainder_part)
}
#[test]
fn test_multiply_by_rational_saturating() {
let div = 100u32;
for value in 0..=div {
for p in 0..=div {
for q in 1..=div {
let value: u64 =
(value as u128 * u64::MAX as u128 / div as u128).try_into().unwrap();
let p = (p as u64 * u32::MAX as u64 / div as u64).try_into().unwrap();
let q = (q as u64 * u32::MAX as u64 / div as u64).try_into().unwrap();
assert_eq!(
multiply_by_rational_saturating(value, p, q),
(value as u128 * p as u128 / q as u128).try_into().unwrap_or(u64::MAX)
);
}
}
}
}
#[test]
fn test_calculate_for_fraction_times_denominator() {
let curve = PiecewiseLinear {
points: &[
(Perbill::from_parts(0_000_000_000), Perbill::from_parts(0_500_000_000)),
(Perbill::from_parts(0_500_000_000), Perbill::from_parts(1_000_000_000)),
(Perbill::from_parts(1_000_000_000), Perbill::from_parts(0_000_000_000)),
],
maximum: Perbill::from_parts(1_000_000_000),
};
pub fn formal_calculate_for_fraction_times_denominator(n: u64, d: u64) -> u64 {
if n <= Perbill::from_parts(0_500_000_000) * d {
n + d / 2
} else {
(d as u128 * 2 - n as u128 * 2).try_into().unwrap()
}
}
let div = 100u32;
for d in 0..=div {
for n in 0..=d {
let d: u64 = (d as u128 * u64::MAX as u128 / div as u128).try_into().unwrap();
let n: u64 = (n as u128 * u64::MAX as u128 / div as u128).try_into().unwrap();
let res = curve.calculate_for_fraction_times_denominator(n, d);
let expected = formal_calculate_for_fraction_times_denominator(n, d);
assert!(abs_sub(res, expected) <= 1);
}
}
}
@@ -0,0 +1,206 @@
// 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.
//! Generic implementation of a block and associated items.
#[cfg(feature = "std")]
use std::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
codec::{Codec, Decode, DecodeWithMemTracking, Encode, EncodeLike},
traits::{
self, Block as BlockT, Header as HeaderT, LazyExtrinsic, MaybeSerialize,
MaybeSerializeDeserialize, Member, NumberFor,
},
Justifications, OpaqueExtrinsic,
};
use alloc::vec::Vec;
use core::marker::PhantomData;
use pezsp_core::RuntimeDebug;
/// Something to identify a block.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
pub enum BlockId<Block: BlockT> {
/// Identify by block header hash.
Hash(Block::Hash),
/// Identify by block number.
Number(NumberFor<Block>),
}
impl<Block: BlockT> BlockId<Block> {
/// Create a block ID from a hash.
pub const fn hash(hash: Block::Hash) -> Self {
BlockId::Hash(hash)
}
/// Create a block ID from a number.
pub const fn number(number: NumberFor<Block>) -> Self {
BlockId::Number(number)
}
/// Check if this block ID refers to the pre-genesis state.
pub fn is_pre_genesis(&self) -> bool {
match self {
BlockId::Hash(hash) => hash == &Default::default(),
BlockId::Number(_) => false,
}
}
/// Create a block ID for a pre-genesis state.
pub fn pre_genesis() -> Self {
BlockId::Hash(Default::default())
}
}
impl<Block: BlockT> Copy for BlockId<Block> {}
#[cfg(feature = "std")]
impl<Block: BlockT> fmt::Display for BlockId<Block> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
/// Abstraction over a bizinikiwi block that allows us to lazily decode its extrinsics.
#[derive(RuntimeDebug, Encode, Decode, scale_info::TypeInfo)]
pub struct LazyBlock<Header, Extrinsic> {
/// The block header.
pub header: Header,
/// The accompanying extrinsics.
pub extrinsics: Vec<OpaqueExtrinsic>,
_phantom: PhantomData<Extrinsic>,
}
impl<Header, Extrinsic: Into<OpaqueExtrinsic>> LazyBlock<Header, Extrinsic> {
/// Creates a new instance of `LazyBlock` from its parts.
pub fn new(header: Header, extrinsics: Vec<Extrinsic>) -> Self {
Self {
header,
extrinsics: extrinsics.into_iter().map(|xt| xt.into()).collect(),
_phantom: Default::default(),
}
}
}
impl<Header, Extrinsic: Into<OpaqueExtrinsic>> From<Block<Header, Extrinsic>>
for LazyBlock<Header, Extrinsic>
{
fn from(block: Block<Header, Extrinsic>) -> Self {
LazyBlock::new(block.header, block.extrinsics)
}
}
impl<Header, Extrinsic> EncodeLike<LazyBlock<Header, Extrinsic>> for Block<Header, Extrinsic>
where
Block<Header, Extrinsic>: Encode,
LazyBlock<Header, Extrinsic>: Encode,
{
}
impl<Header, Extrinsic> EncodeLike<Block<Header, Extrinsic>> for LazyBlock<Header, Extrinsic>
where
Block<Header, Extrinsic>: Encode,
LazyBlock<Header, Extrinsic>: Encode,
{
}
impl<Header, Extrinsic> traits::LazyBlock for LazyBlock<Header, Extrinsic>
where
Header: HeaderT,
Extrinsic: core::fmt::Debug + LazyExtrinsic,
{
type Extrinsic = Extrinsic;
type Header = Header;
fn header(&self) -> &Self::Header {
&self.header
}
fn header_mut(&mut self) -> &mut Self::Header {
&mut self.header
}
fn extrinsics(&self) -> impl Iterator<Item = Result<Self::Extrinsic, codec::Error>> {
self.extrinsics.iter().map(|xt| Self::Extrinsic::decode_unprefixed(&xt.0))
}
}
/// Abstraction over a bizinikiwi block.
#[derive(
PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, scale_info::TypeInfo,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Block<Header, Extrinsic> {
/// The block header.
pub header: Header,
/// The accompanying extrinsics.
pub extrinsics: Vec<Extrinsic>,
}
impl<Header, Extrinsic> traits::HeaderProvider for Block<Header, Extrinsic>
where
Header: HeaderT,
{
type HeaderT = Header;
}
impl<Header, Extrinsic: MaybeSerialize> traits::Block for Block<Header, Extrinsic>
where
Header: HeaderT + MaybeSerializeDeserialize,
Extrinsic: Member
+ Codec
+ DecodeWithMemTracking
+ traits::ExtrinsicLike
+ Into<OpaqueExtrinsic>
+ LazyExtrinsic,
{
type Extrinsic = Extrinsic;
type Header = Header;
type Hash = <Self::Header as traits::Header>::Hash;
type LazyBlock = LazyBlock<Header, Extrinsic>;
fn header(&self) -> &Self::Header {
&self.header
}
fn extrinsics(&self) -> &[Self::Extrinsic] {
&self.extrinsics[..]
}
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
(self.header, self.extrinsics)
}
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
Block { header, extrinsics }
}
}
/// Abstraction over a bizinikiwi block and justification.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SignedBlock<Block> {
/// Full block.
pub block: Block,
/// Block justification.
pub justifications: Option<Justifications>,
}
@@ -0,0 +1,154 @@
// 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.
//! Generic implementation of an extrinsic that has passed the verification
//! stage.
use codec::Encode;
use pezsp_weights::Weight;
use crate::{
traits::{
self, transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin,
DispatchInfoOf, DispatchTransaction, Dispatchable, MaybeDisplay, Member,
PostDispatchInfoOf, ValidateUnsigned,
},
transaction_validity::{TransactionSource, TransactionValidity},
};
use super::unchecked_extrinsic::ExtensionVersion;
/// Default version of the [Extension](TransactionExtension) used to construct the inherited
/// implication for legacy transactions.
const DEFAULT_EXTENSION_VERSION: ExtensionVersion = 0;
/// The kind of extrinsic this is, including any fields required of that kind. This is basically
/// the full extrinsic except the `Call`.
#[derive(PartialEq, Eq, Clone, pezsp_core::RuntimeDebug)]
pub enum ExtrinsicFormat<AccountId, Extension> {
/// Extrinsic is bare; it must pass either the bare forms of `TransactionExtension` or
/// `ValidateUnsigned`, both deprecated, or alternatively a `ProvideInherent`.
Bare,
/// Extrinsic has a default `Origin` of `Signed(AccountId)` and must pass all
/// `TransactionExtension`s regular checks and includes all extension data.
Signed(AccountId, Extension),
/// Extrinsic has a default `Origin` of `None` and must pass all `TransactionExtension`s.
/// regular checks and includes all extension data.
General(ExtensionVersion, Extension),
}
/// Definition of something that the external world might want to say; its existence implies that it
/// has been checked and is good, particularly with regards to the signature.
///
/// This is typically passed into [`traits::Applyable::apply`], which should execute
/// [`CheckedExtrinsic::function`], alongside all other bits and bobs.
#[derive(PartialEq, Eq, Clone, pezsp_core::RuntimeDebug)]
pub struct CheckedExtrinsic<AccountId, Call, Extension> {
/// Who this purports to be from and the number of extrinsics have come before
/// from the same signer, if anyone (note this is not a signature).
pub format: ExtrinsicFormat<AccountId, Extension>,
/// The function that should be called.
pub function: Call,
}
impl<AccountId, Call, Extension, RuntimeOrigin> traits::Applyable
for CheckedExtrinsic<AccountId, Call, Extension>
where
AccountId: Member + MaybeDisplay,
Call: Member + Dispatchable<RuntimeOrigin = RuntimeOrigin> + Encode,
Extension: TransactionExtension<Call>,
RuntimeOrigin: From<Option<AccountId>> + AsTransactionAuthorizedOrigin,
{
type Call = Call;
fn validate<I: ValidateUnsigned<Call = Self::Call>>(
&self,
source: TransactionSource,
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> TransactionValidity {
match self.format {
ExtrinsicFormat::Bare => {
let inherent_validation = I::validate_unsigned(source, &self.function)?;
let legacy_validation = Extension::bare_validate(&self.function, info, len)?;
Ok(legacy_validation.combine_with(inherent_validation))
},
ExtrinsicFormat::Signed(ref signer, ref extension) => {
let origin = Some(signer.clone()).into();
extension
.validate_only(
origin,
&self.function,
info,
len,
source,
DEFAULT_EXTENSION_VERSION,
)
.map(|x| x.0)
},
ExtrinsicFormat::General(extension_version, ref extension) => extension
.validate_only(None.into(), &self.function, info, len, source, extension_version)
.map(|x| x.0),
}
}
fn apply<I: ValidateUnsigned<Call = Self::Call>>(
self,
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Self::Call>> {
match self.format {
ExtrinsicFormat::Bare => {
I::pre_dispatch(&self.function)?;
// TODO: Separate logic from `TransactionExtension` into a new `InherentExtension`
// interface.
Extension::bare_validate_and_prepare(&self.function, info, len)?;
let res = self.function.dispatch(None.into());
let mut post_info = res.unwrap_or_else(|err| err.post_info);
let pd_res = res.map(|_| ()).map_err(|e| e.error);
// TODO: Separate logic from `TransactionExtension` into a new `InherentExtension`
// interface.
Extension::bare_post_dispatch(info, &mut post_info, len, &pd_res)?;
Ok(res)
},
ExtrinsicFormat::Signed(signer, extension) => extension.dispatch_transaction(
Some(signer).into(),
self.function,
info,
len,
DEFAULT_EXTENSION_VERSION,
),
ExtrinsicFormat::General(extension_version, extension) => extension
.dispatch_transaction(None.into(), self.function, info, len, extension_version),
}
}
}
impl<AccountId, Call: Dispatchable, Extension: TransactionExtension<Call>>
CheckedExtrinsic<AccountId, Call, Extension>
{
/// Returns the weight of the extension of this transaction, if present. If the transaction
/// doesn't use any extension, the weight returned is equal to zero.
pub fn extension_weight(&self) -> Weight {
match &self.format {
ExtrinsicFormat::Bare => Weight::zero(),
ExtrinsicFormat::Signed(_, ext) | ExtrinsicFormat::General(_, ext) =>
ext.weight(&self.function),
}
}
}
@@ -0,0 +1,472 @@
// 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.
//! Generic implementation of a digest.
#[cfg(all(not(feature = "std"), feature = "serde"))]
use alloc::format;
use alloc::vec::Vec;
use codec::DecodeAll;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
codec::{Decode, DecodeWithMemTracking, Encode, Error, Input},
scale_info::{
build::{Fields, Variants},
Path, Type, TypeInfo,
},
ConsensusEngineId,
};
use pezsp_core::RuntimeDebug;
/// Generic header digest.
#[derive(
PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, TypeInfo, Default,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Digest {
/// A list of logs in the digest.
pub logs: Vec<DigestItem>,
}
impl Digest {
/// Get reference to all digest items.
pub fn logs(&self) -> &[DigestItem] {
&self.logs
}
/// Push new digest item.
pub fn push(&mut self, item: DigestItem) {
self.logs.push(item);
}
/// Pop a digest item.
pub fn pop(&mut self) -> Option<DigestItem> {
self.logs.pop()
}
/// Get reference to the first digest item that matches the passed predicate.
pub fn log<T: ?Sized, F: Fn(&DigestItem) -> Option<&T>>(&self, predicate: F) -> Option<&T> {
self.logs().iter().find_map(predicate)
}
/// Get a conversion of the first digest item that successfully converts using the function.
pub fn convert_first<T, F: Fn(&DigestItem) -> Option<T>>(&self, predicate: F) -> Option<T> {
self.logs().iter().find_map(predicate)
}
}
/// Digest item that is able to encode/decode 'system' digest items and
/// provide opaque access to other items.
#[derive(PartialEq, Eq, Clone, DecodeWithMemTracking, RuntimeDebug)]
pub enum DigestItem {
/// A pre-runtime digest.
///
/// These are messages from the consensus engine to the runtime, although
/// the consensus engine can (and should) read them itself to avoid
/// code and state duplication. It is erroneous for a runtime to produce
/// these, but this is not (yet) checked.
///
/// NOTE: the runtime is not allowed to panic or fail in an `on_initialize`
/// call if an expected `PreRuntime` digest is not present. It is the
/// responsibility of a external block verifier to check this. Runtime API calls
/// will initialize the block without pre-runtime digests, so initialization
/// cannot fail when they are missing.
PreRuntime(ConsensusEngineId, Vec<u8>),
/// A message from the runtime to the consensus engine. This should *never*
/// be generated by the native code of any consensus engine, but this is not
/// checked (yet).
Consensus(ConsensusEngineId, Vec<u8>),
/// Put a Seal on it. This is only used by native code, and is never seen
/// by runtimes.
Seal(ConsensusEngineId, Vec<u8>),
/// Some other thing. Unsupported and experimental.
Other(Vec<u8>),
/// An indication for the light clients that the runtime execution
/// environment is updated.
///
/// Currently this is triggered when:
/// 1. Runtime code blob is changed or
/// 2. `heap_pages` value is changed.
RuntimeEnvironmentUpdated,
}
#[cfg(feature = "serde")]
impl serde::Serialize for DigestItem {
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.using_encoded(|bytes| pezsp_core::bytes::serialize(bytes, seq))
}
}
#[cfg(feature = "serde")]
impl<'a> serde::Deserialize<'a> for DigestItem {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
let r = pezsp_core::bytes::deserialize(de)?;
Decode::decode(&mut &r[..])
.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
}
}
impl TypeInfo for DigestItem {
type Identity = Self;
fn type_info() -> Type {
Type::builder().path(Path::new("DigestItem", module_path!())).variant(
Variants::new()
.variant("PreRuntime", |v| {
v.index(DigestItemType::PreRuntime as u8).fields(
Fields::unnamed()
.field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Consensus", |v| {
v.index(DigestItemType::Consensus as u8).fields(
Fields::unnamed()
.field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Seal", |v| {
v.index(DigestItemType::Seal as u8).fields(
Fields::unnamed()
.field(|f| f.ty::<ConsensusEngineId>().type_name("ConsensusEngineId"))
.field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")),
)
})
.variant("Other", |v| {
v.index(DigestItemType::Other as u8)
.fields(Fields::unnamed().field(|f| f.ty::<Vec<u8>>().type_name("Vec<u8>")))
})
.variant("RuntimeEnvironmentUpdated", |v| {
v.index(DigestItemType::RuntimeEnvironmentUpdated as u8).fields(Fields::unit())
}),
)
}
}
/// A 'referencing view' for digest item. Does not own its contents. Used by
/// final runtime implementations for encoding/decoding its log items.
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
pub enum DigestItemRef<'a> {
/// A pre-runtime digest.
///
/// These are messages from the consensus engine to the runtime, although
/// the consensus engine can (and should) read them itself to avoid
/// code and state duplication. It is erroneous for a runtime to produce
/// these, but this is not (yet) checked.
PreRuntime(&'a ConsensusEngineId, &'a [u8]),
/// A message from the runtime to the consensus engine. This should *never*
/// be generated by the native code of any consensus engine, but this is not
/// checked (yet).
Consensus(&'a ConsensusEngineId, &'a [u8]),
/// Put a Seal on it. This is only used by native code, and is never seen
/// by runtimes.
Seal(&'a ConsensusEngineId, &'a [u8]),
/// Any 'non-system' digest item, opaque to the native code.
Other(&'a [u8]),
/// Runtime code or heap pages updated.
RuntimeEnvironmentUpdated,
}
/// Type of the digest item. Used to gain explicit control over `DigestItem` encoding
/// process. We need an explicit control, because final runtimes are encoding their own
/// digest items using `DigestItemRef` type and we can't auto-derive `Decode`
/// trait for `DigestItemRef`.
#[repr(u32)]
#[derive(Encode, Decode)]
pub enum DigestItemType {
Other = 0,
Consensus = 4,
Seal = 5,
PreRuntime = 6,
RuntimeEnvironmentUpdated = 8,
}
/// Type of a digest item that contains raw data; this also names the consensus engine ID where
/// applicable. Used to identify one or more digest items of interest.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum OpaqueDigestItemId<'a> {
/// Type corresponding to DigestItem::PreRuntime.
PreRuntime(&'a ConsensusEngineId),
/// Type corresponding to DigestItem::Consensus.
Consensus(&'a ConsensusEngineId),
/// Type corresponding to DigestItem::Seal.
Seal(&'a ConsensusEngineId),
/// Some other (non-prescribed) type.
Other,
}
impl DigestItem {
/// Returns a 'referencing view' for this digest item.
pub fn dref(&self) -> DigestItemRef<'_> {
match *self {
Self::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s),
Self::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s),
Self::Seal(ref v, ref s) => DigestItemRef::Seal(v, s),
Self::Other(ref v) => DigestItemRef::Other(v),
Self::RuntimeEnvironmentUpdated => DigestItemRef::RuntimeEnvironmentUpdated,
}
}
/// Returns `Some` if this entry is the `PreRuntime` entry.
pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> {
self.dref().as_pre_runtime()
}
/// Returns `Some` if this entry is the `Consensus` entry.
pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &[u8])> {
self.dref().as_consensus()
}
/// Returns `Some` if this entry is the `Seal` entry.
pub fn as_seal(&self) -> Option<(ConsensusEngineId, &[u8])> {
self.dref().as_seal()
}
/// Returns Some if `self` is a `DigestItem::Other`.
pub fn as_other(&self) -> Option<&[u8]> {
self.dref().as_other()
}
/// Returns the opaque data contained in the item if `Some` if this entry has the id given.
pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&[u8]> {
self.dref().try_as_raw(id)
}
/// Returns the data decoded as `T`, if the `id` is matching.
pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
self.dref().try_to::<T>(id)
}
/// Try to match this to a `Self::Seal`, check `id` matches and decode it.
///
/// Returns `None` if this isn't a seal item, the `id` doesn't match or when the decoding fails.
pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
self.dref().seal_try_to(id)
}
/// Try to match this to a `Self::Consensus`, check `id` matches and decode it.
///
/// Returns `None` if this isn't a consensus item, the `id` doesn't match or
/// when the decoding fails.
pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
self.dref().consensus_try_to(id)
}
/// Try to match this to a `Self::PreRuntime`, check `id` matches and decode it.
///
/// Returns `None` if this isn't a pre-runtime item, the `id` doesn't match or
/// when the decoding fails.
pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
self.dref().pre_runtime_try_to(id)
}
}
impl Encode for DigestItem {
fn encode(&self) -> Vec<u8> {
self.dref().encode()
}
}
impl codec::EncodeLike for DigestItem {}
impl Decode for DigestItem {
#[allow(deprecated)]
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let item_type: DigestItemType = Decode::decode(input)?;
match item_type {
DigestItemType::PreRuntime => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::PreRuntime(vals.0, vals.1))
},
DigestItemType::Consensus => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::Consensus(vals.0, vals.1))
},
DigestItemType::Seal => {
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
Ok(Self::Seal(vals.0, vals.1))
},
DigestItemType::Other => Ok(Self::Other(Decode::decode(input)?)),
DigestItemType::RuntimeEnvironmentUpdated => Ok(Self::RuntimeEnvironmentUpdated),
}
}
}
impl<'a> DigestItemRef<'a> {
/// Cast this digest item into `PreRuntime`
pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
match *self {
Self::PreRuntime(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
_ => None,
}
}
/// Cast this digest item into `Consensus`
pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
match *self {
Self::Consensus(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
_ => None,
}
}
/// Cast this digest item into `Seal`
pub fn as_seal(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
match *self {
Self::Seal(consensus_engine_id, data) => Some((*consensus_engine_id, data)),
_ => None,
}
}
/// Cast this digest item into `PreRuntime`
pub fn as_other(&self) -> Option<&'a [u8]> {
match *self {
Self::Other(data) => Some(data),
_ => None,
}
}
/// Try to match this digest item to the given opaque item identifier; if it matches, then
/// return the opaque data it contains.
pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&'a [u8]> {
match (id, self) {
(OpaqueDigestItemId::Consensus(w), &Self::Consensus(v, s)) |
(OpaqueDigestItemId::Seal(w), &Self::Seal(v, s)) |
(OpaqueDigestItemId::PreRuntime(w), &Self::PreRuntime(v, s))
if v == w =>
Some(s),
(OpaqueDigestItemId::Other, &Self::Other(s)) => Some(s),
_ => None,
}
}
/// Try to match this digest item to the given opaque item identifier; if it matches, then
/// try to cast to the given data type; if that works, return it.
pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
self.try_as_raw(id).and_then(|mut x| DecodeAll::decode_all(&mut x).ok())
}
/// Try to match this to a `Self::Seal`, check `id` matches and decode it.
///
/// Returns `None` if this isn't a seal item, the `id` doesn't match or when the decoding fails.
pub fn seal_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
self.as_seal()
.filter(|s| s.0 == *id)
.and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
}
/// Try to match this to a `Self::Consensus`, check `id` matches and decode it.
///
/// Returns `None` if this isn't a consensus item, the `id` doesn't match or
/// when the decoding fails.
pub fn consensus_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
self.as_consensus()
.filter(|s| s.0 == *id)
.and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
}
/// Try to match this to a `Self::PreRuntime`, check `id` matches and decode it.
///
/// Returns `None` if this isn't a pre-runtime item, the `id` doesn't match or
/// when the decoding fails.
pub fn pre_runtime_try_to<T: Decode>(&self, id: &ConsensusEngineId) -> Option<T> {
self.as_pre_runtime()
.filter(|s| s.0 == *id)
.and_then(|mut d| DecodeAll::decode_all(&mut d.1).ok())
}
}
impl<'a> Encode for DigestItemRef<'a> {
fn encode(&self) -> Vec<u8> {
match *self {
Self::Consensus(val, data) => (DigestItemType::Consensus, val, data).encode(),
Self::Seal(val, sig) => (DigestItemType::Seal, val, sig).encode(),
Self::PreRuntime(val, data) => (DigestItemType::PreRuntime, val, data).encode(),
Self::Other(val) => (DigestItemType::Other, val).encode(),
Self::RuntimeEnvironmentUpdated => DigestItemType::RuntimeEnvironmentUpdated.encode(),
}
}
}
impl<'a> codec::EncodeLike for DigestItemRef<'a> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_serialize_digest() {
let digest = Digest {
logs: vec![DigestItem::Other(vec![1, 2, 3]), DigestItem::Seal(*b"test", vec![1, 2, 3])],
};
assert_eq!(
serde_json::to_string(&digest).unwrap(),
r#"{"logs":["0x000c010203","0x05746573740c010203"]}"#
);
}
#[test]
fn digest_item_type_info() {
let type_info = DigestItem::type_info();
let variants = if let scale_info::TypeDef::Variant(variant) = type_info.type_def {
variant.variants
} else {
panic!("Should be a TypeDef::TypeDefVariant")
};
// ensure that all variants are covered by manual TypeInfo impl
let check = |digest_item_type: DigestItemType| {
let (variant_name, digest_item) = match digest_item_type {
DigestItemType::Other => ("Other", DigestItem::Other(Default::default())),
DigestItemType::Consensus =>
("Consensus", DigestItem::Consensus(Default::default(), Default::default())),
DigestItemType::Seal =>
("Seal", DigestItem::Seal(Default::default(), Default::default())),
DigestItemType::PreRuntime =>
("PreRuntime", DigestItem::PreRuntime(Default::default(), Default::default())),
DigestItemType::RuntimeEnvironmentUpdated =>
("RuntimeEnvironmentUpdated", DigestItem::RuntimeEnvironmentUpdated),
};
let encoded = digest_item.encode();
let variant = variants
.iter()
.find(|v| v.name == variant_name)
.expect(&format!("Variant {} not found", variant_name));
assert_eq!(encoded[0], variant.index)
};
check(DigestItemType::Other);
check(DigestItemType::Consensus);
check(DigestItemType::Seal);
check(DigestItemType::PreRuntime);
check(DigestItemType::RuntimeEnvironmentUpdated);
}
}
@@ -0,0 +1,252 @@
// 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.
//! Generic implementation of an unchecked (pre-verification) extrinsic.
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::codec::{Decode, DecodeWithMemTracking, Encode, Error, Input, Output};
/// Era period
pub type Period = u64;
/// Era phase
pub type Phase = u64;
/// An era to describe the longevity of a transaction.
#[derive(DecodeWithMemTracking, PartialEq, Eq, Clone, Copy, pezsp_core::RuntimeDebug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Era {
/// The transaction is valid forever. The genesis hash must be present in the signed content.
Immortal,
/// Period and phase are encoded:
/// - The period of validity from the block hash found in the signing material.
/// - The phase in the period that this transaction's lifetime begins (and, importantly,
/// implies which block hash is included in the signature material). If the `period` is
/// greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that
/// `period` is.
///
/// When used on `FRAME`-based runtimes, `period` cannot exceed `BlockHashCount` parameter
/// of `system` module.
Mortal(Period, Phase),
}
// E.g. with period == 4:
// 0 10 20 30 40
// 0123456789012345678901234567890123456789012
// |...|
// authored -/ \- expiry
// phase = 1
// n = Q(current - phase, period) + phase
impl Era {
/// Create a new era based on a period (which should be a power of two between 4 and 65536
/// inclusive) and a block number on which it should start (or, for long periods, be shortly
/// after the start).
///
/// If using `Era` in the context of `FRAME` runtime, make sure that `period`
/// does not exceed `BlockHashCount` parameter passed to `system` module, since that
/// prunes old blocks and renders transactions immediately invalid.
pub fn mortal(period: u64, current: u64) -> Self {
let period = period.checked_next_power_of_two().unwrap_or(1 << 16).clamp(4, 1 << 16);
let phase = current % period;
let quantize_factor = (period >> 12).max(1);
let quantized_phase = phase / quantize_factor * quantize_factor;
Self::Mortal(period, quantized_phase)
}
/// Create an "immortal" transaction.
pub fn immortal() -> Self {
Self::Immortal
}
/// `true` if this is an immortal transaction.
pub fn is_immortal(&self) -> bool {
matches!(self, Self::Immortal)
}
/// Get the block number of the start of the era whose properties this object
/// describes that `current` belongs to.
pub fn birth(self, current: u64) -> u64 {
match self {
Self::Immortal => 0,
Self::Mortal(period, phase) => (current.max(phase) - phase) / period * period + phase,
}
}
/// Get the block number of the first block at which the era has ended.
pub fn death(self, current: u64) -> u64 {
match self {
Self::Immortal => u64::MAX,
Self::Mortal(period, _) => self.birth(current) + period,
}
}
}
impl Encode for Era {
fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
match self {
Self::Immortal => output.push_byte(0),
Self::Mortal(period, phase) => {
let quantize_factor = (*period as u64 >> 12).max(1);
let encoded = (period.trailing_zeros() - 1).clamp(1, 15) as u16 |
((phase / quantize_factor) << 4) as u16;
encoded.encode_to(output);
},
}
}
}
impl codec::EncodeLike for Era {}
impl Decode for Era {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let first = input.read_byte()?;
if first == 0 {
Ok(Self::Immortal)
} else {
let encoded = first as u64 + ((input.read_byte()? as u64) << 8);
let period = 2 << (encoded % (1 << 4));
let quantize_factor = (period >> 12).max(1);
let phase = (encoded >> 4) * quantize_factor;
if period >= 4 && phase < period {
Ok(Self::Mortal(period, phase))
} else {
Err("Invalid period and phase".into())
}
}
}
}
/// Add Mortal{N}(u8) variants with the given indices, to describe custom encoding.
macro_rules! mortal_variants {
($variants:ident, $($index:literal),* ) => {
$variants
$(
.variant(concat!(stringify!(Mortal), stringify!($index)), |v| v
.index($index)
.fields(scale_info::build::Fields::unnamed().field(|f| f.ty::<u8>()))
)
)*
}
}
impl scale_info::TypeInfo for Era {
type Identity = Self;
fn type_info() -> scale_info::Type {
let variants = scale_info::build::Variants::new().variant("Immortal", |v| v.index(0));
// this is necessary since the size of the encoded Mortal variant is `u16`, conditional on
// the value of the first byte being > 0.
let variants = mortal_variants!(
variants, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
);
scale_info::Type::builder()
.path(scale_info::Path::new("Era", module_path!()))
.variant(variants)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn immortal_works() {
let e = Era::immortal();
assert_eq!(e.birth(0), 0);
assert_eq!(e.death(0), u64::MAX);
assert_eq!(e.birth(1), 0);
assert_eq!(e.death(1), u64::MAX);
assert_eq!(e.birth(u64::MAX), 0);
assert_eq!(e.death(u64::MAX), u64::MAX);
assert!(e.is_immortal());
assert_eq!(e.encode(), vec![0u8]);
assert_eq!(e, Era::decode(&mut &[0u8][..]).unwrap());
}
#[test]
fn mortal_codec_works() {
let e = Era::mortal(64, 42);
assert!(!e.is_immortal());
let expected = vec![5 + 42 % 16 * 16, 42 / 16];
assert_eq!(e.encode(), expected);
assert_eq!(e, Era::decode(&mut &expected[..]).unwrap());
}
#[test]
fn long_period_mortal_codec_works() {
let e = Era::mortal(32768, 20000);
let expected = vec![(14 + 2500 % 16 * 16) as u8, (2500 / 16) as u8];
assert_eq!(e.encode(), expected);
assert_eq!(e, Era::decode(&mut &expected[..]).unwrap());
}
#[test]
fn era_initialization_works() {
assert_eq!(Era::mortal(64, 42), Era::Mortal(64, 42));
assert_eq!(Era::mortal(32768, 20000), Era::Mortal(32768, 20000));
assert_eq!(Era::mortal(200, 513), Era::Mortal(256, 1));
assert_eq!(Era::mortal(2, 1), Era::Mortal(4, 1));
assert_eq!(Era::mortal(4, 5), Era::Mortal(4, 1));
}
#[test]
fn quantized_clamped_era_initialization_works() {
// clamp 1000000 to 65536, quantize 1000001 % 65536 to the nearest 4
assert_eq!(Era::mortal(1000000, 1000001), Era::Mortal(65536, 1000001 % 65536 / 4 * 4));
}
#[test]
fn mortal_birth_death_works() {
let e = Era::mortal(4, 6);
for i in 6..10 {
assert_eq!(e.birth(i), 6);
assert_eq!(e.death(i), 10);
}
// wrong because it's outside of the (current...current + period) range
assert_ne!(e.birth(10), 6);
assert_ne!(e.birth(5), 6);
}
#[test]
fn current_less_than_phase() {
// should not panic
Era::mortal(4, 3).birth(1);
}
}
@@ -0,0 +1,239 @@
// 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.
//! Generic implementation of a block header.
use crate::{
codec::{Codec, Decode, DecodeWithMemTracking, Encode},
generic::Digest,
scale_info::TypeInfo,
traits::{self, AtLeast32BitUnsigned, BlockNumber, Hash as HashT, MaybeDisplay, Member},
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use pezsp_core::U256;
/// Abstraction over a block header for a bizinikiwi chain.
#[derive(
Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Clone, pezsp_core::RuntimeDebug, TypeInfo,
)]
#[scale_info(skip_type_params(Hash))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Header<Number: Copy + Into<U256> + TryFrom<U256>, Hash: HashT> {
/// The parent hash.
pub parent_hash: Hash::Output,
/// The block number.
#[cfg_attr(
feature = "serde",
serde(serialize_with = "serialize_number", deserialize_with = "deserialize_number")
)]
#[codec(compact)]
pub number: Number,
/// The state trie merkle root
pub state_root: Hash::Output,
/// The merkle root of the extrinsics.
pub extrinsics_root: Hash::Output,
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
pub digest: Digest,
}
#[cfg(feature = "serde")]
pub fn serialize_number<S, T: Copy + Into<U256> + TryFrom<U256>>(
val: &T,
s: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let u256: U256 = (*val).into();
serde::Serialize::serialize(&u256, s)
}
#[cfg(feature = "serde")]
pub fn deserialize_number<'a, D, T: Copy + Into<U256> + TryFrom<U256>>(d: D) -> Result<T, D::Error>
where
D: serde::Deserializer<'a>,
{
let u256: U256 = serde::Deserialize::deserialize(d)?;
TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
}
impl<Number, Hash> traits::Header for Header<Number, Hash>
where
Number: BlockNumber,
Hash: HashT,
{
type Number = Number;
type Hash = <Hash as HashT>::Output;
type Hashing = Hash;
fn new(
number: Self::Number,
extrinsics_root: Self::Hash,
state_root: Self::Hash,
parent_hash: Self::Hash,
digest: Digest,
) -> Self {
Self { number, extrinsics_root, state_root, parent_hash, digest }
}
fn number(&self) -> &Self::Number {
&self.number
}
fn set_number(&mut self, num: Self::Number) {
self.number = num
}
fn extrinsics_root(&self) -> &Self::Hash {
&self.extrinsics_root
}
fn set_extrinsics_root(&mut self, root: Self::Hash) {
self.extrinsics_root = root
}
fn state_root(&self) -> &Self::Hash {
&self.state_root
}
fn set_state_root(&mut self, root: Self::Hash) {
self.state_root = root
}
fn parent_hash(&self) -> &Self::Hash {
&self.parent_hash
}
fn set_parent_hash(&mut self, hash: Self::Hash) {
self.parent_hash = hash
}
fn digest(&self) -> &Digest {
&self.digest
}
fn digest_mut(&mut self) -> &mut Digest {
#[cfg(feature = "std")]
log::debug!(target: "header", "Retrieving mutable reference to digest");
&mut self.digest
}
}
impl<Number, Hash> Header<Number, Hash>
where
Number: Member
+ core::hash::Hash
+ Copy
+ MaybeDisplay
+ AtLeast32BitUnsigned
+ Codec
+ Into<U256>
+ TryFrom<U256>,
Hash: HashT,
{
/// Convenience helper for computing the hash of the header without having
/// to import the trait.
pub fn hash(&self) -> Hash::Output {
Hash::hash_of(self)
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::traits::BlakeTwo256;
#[test]
fn should_serialize_numbers() {
fn serialize(num: u128) -> String {
let mut v = vec![];
{
let mut ser = serde_json::Serializer::new(std::io::Cursor::new(&mut v));
serialize_number(&num, &mut ser).unwrap();
}
String::from_utf8(v).unwrap()
}
assert_eq!(serialize(0), "\"0x0\"".to_owned());
assert_eq!(serialize(1), "\"0x1\"".to_owned());
assert_eq!(serialize(u64::MAX as u128), "\"0xffffffffffffffff\"".to_owned());
assert_eq!(serialize(u64::MAX as u128 + 1), "\"0x10000000000000000\"".to_owned());
}
#[test]
fn should_deserialize_number() {
fn deserialize(num: &str) -> u128 {
let mut der = serde_json::Deserializer::new(serde_json::de::StrRead::new(num));
deserialize_number(&mut der).unwrap()
}
assert_eq!(deserialize("\"0x0\""), 0);
assert_eq!(deserialize("\"0x1\""), 1);
assert_eq!(deserialize("\"0xffffffffffffffff\""), u64::MAX as u128);
assert_eq!(deserialize("\"0x10000000000000000\""), u64::MAX as u128 + 1);
}
#[test]
fn ensure_format_is_unchanged() {
let header = Header::<u32, BlakeTwo256> {
parent_hash: BlakeTwo256::hash(b"1"),
number: 2,
state_root: BlakeTwo256::hash(b"3"),
extrinsics_root: BlakeTwo256::hash(b"4"),
digest: crate::generic::Digest {
logs: vec![crate::generic::DigestItem::Other(b"6".to_vec())],
},
};
let header_encoded = header.encode();
assert_eq!(
header_encoded,
vec![
146, 205, 245, 120, 196, 112, 133, 165, 153, 34, 86, 240, 220, 249, 125, 11, 25,
241, 241, 201, 222, 77, 95, 227, 12, 58, 206, 97, 145, 182, 229, 219, 8, 88, 19,
72, 51, 123, 15, 62, 20, 134, 32, 23, 61, 170, 165, 249, 77, 0, 216, 129, 112, 93,
203, 240, 170, 131, 239, 218, 186, 97, 210, 237, 225, 235, 134, 73, 33, 73, 151,
87, 78, 32, 196, 100, 56, 138, 23, 36, 32, 210, 84, 3, 104, 43, 187, 184, 12, 73,
104, 49, 200, 204, 31, 143, 13, 4, 0, 4, 54
],
);
assert_eq!(header, Header::<u32, BlakeTwo256>::decode(&mut &header_encoded[..]).unwrap());
let header = Header::<u32, BlakeTwo256> {
parent_hash: BlakeTwo256::hash(b"1000"),
number: 2000,
state_root: BlakeTwo256::hash(b"3000"),
extrinsics_root: BlakeTwo256::hash(b"4000"),
digest: crate::generic::Digest {
logs: vec![crate::generic::DigestItem::Other(b"5000".to_vec())],
},
};
let header_encoded = header.encode();
assert_eq!(
header_encoded,
vec![
197, 243, 254, 225, 31, 117, 21, 218, 179, 213, 92, 6, 247, 164, 230, 25, 47, 166,
140, 117, 142, 159, 195, 202, 67, 196, 238, 26, 44, 18, 33, 92, 65, 31, 219, 225,
47, 12, 107, 88, 153, 146, 55, 21, 226, 186, 110, 48, 167, 187, 67, 183, 228, 232,
118, 136, 30, 254, 11, 87, 48, 112, 7, 97, 31, 82, 146, 110, 96, 87, 152, 68, 98,
162, 227, 222, 78, 14, 244, 194, 120, 154, 112, 97, 222, 144, 174, 101, 220, 44,
111, 126, 54, 34, 155, 220, 253, 124, 4, 0, 16, 53, 48, 48, 48
],
);
assert_eq!(header, Header::<u32, BlakeTwo256>::decode(&mut &header_encoded[..]).unwrap());
}
}
@@ -0,0 +1,40 @@
// 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.
//! Generic implementations of [`crate::traits::Header`], [`crate::traits::Block`] and
//! [`crate::traits::ExtrinsicLike`].
mod block;
mod checked_extrinsic;
mod digest;
mod era;
mod header;
#[cfg(test)]
mod tests;
mod unchecked_extrinsic;
pub use self::{
block::{Block, BlockId, LazyBlock, SignedBlock},
checked_extrinsic::{CheckedExtrinsic, ExtrinsicFormat},
digest::{Digest, DigestItem, DigestItemRef, OpaqueDigestItemId},
era::{Era, Phase},
header::Header,
unchecked_extrinsic::{
ExtensionVersion, Preamble, SignedPayload, UncheckedExtrinsic, EXTRINSIC_FORMAT_VERSION,
},
};
pub use unchecked_extrinsic::UncheckedSignaturePayload;
@@ -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.
//! Tests for the generic implementations of Extrinsic/Header/Block.
use super::DigestItem;
use crate::codec::{Decode, Encode};
#[test]
fn system_digest_item_encoding() {
let item = DigestItem::Consensus([1, 2, 3, 4], vec![5, 6, 7, 8]);
let encoded = item.encode();
assert_eq!(
encoded,
vec![
4, // type = DigestItemType::Consensus
1, 2, 3, 4, 16, 5, 6, 7, 8,
]
);
let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap();
assert_eq!(item, decoded);
}
#[test]
fn non_system_digest_item_encoding() {
let item = DigestItem::Other(vec![10, 20, 30]);
let encoded = item.encode();
assert_eq!(
encoded,
vec![
// type = DigestItemType::Other
0, // length of other data
12, // authorities
10, 20, 30,
]
);
let decoded: DigestItem = Decode::decode(&mut &encoded[..]).unwrap();
assert_eq!(item, decoded);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,20 @@
// 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.
//! Runtime types that existed in old API versions.
pub mod byte_sized_error;
@@ -0,0 +1,100 @@
// 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.
//! Runtime types that existed prior to BlockBuilder API version 6.
use crate::{ArithmeticError, TokenError};
use codec::{Decode, Encode};
use scale_info::TypeInfo;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// [`ModuleError`] type definition before BlockBuilder API version 6.
#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModuleError {
/// Module index, matching the metadata module index.
pub index: u8,
/// Module specific error value.
pub error: u8,
/// Optional error message.
#[codec(skip)]
#[cfg_attr(feature = "serde", serde(skip_deserializing))]
pub message: Option<&'static str>,
}
impl PartialEq for ModuleError {
fn eq(&self, other: &Self) -> bool {
(self.index == other.index) && (self.error == other.error)
}
}
/// [`DispatchError`] type definition before BlockBuilder API version 6.
#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DispatchError {
/// Some error occurred.
Other(
#[codec(skip)]
#[cfg_attr(feature = "serde", serde(skip_deserializing))]
&'static str,
),
/// Failed to lookup some data.
CannotLookup,
/// A bad origin.
BadOrigin,
/// A custom error in a module.
Module(ModuleError),
/// At least one consumer is remaining so the account cannot be destroyed.
ConsumerRemaining,
/// There are no providers so the account cannot be created.
NoProviders,
/// There are too many consumers so the account cannot be created.
TooManyConsumers,
/// An error to do with tokens.
Token(TokenError),
/// An arithmetic error.
Arithmetic(ArithmeticError),
}
/// [`DispatchOutcome`] type definition before BlockBuilder API version 6.
pub type DispatchOutcome = Result<(), DispatchError>;
/// [`ApplyExtrinsicResult`] type definition before BlockBuilder API version 6.
pub type ApplyExtrinsicResult =
Result<DispatchOutcome, crate::transaction_validity::TransactionValidityError>;
/// Convert the legacy `ApplyExtrinsicResult` type to the latest version.
pub fn convert_to_latest(old: ApplyExtrinsicResult) -> crate::ApplyExtrinsicResult {
old.map(|outcome| {
outcome.map_err(|e| match e {
DispatchError::Other(s) => crate::DispatchError::Other(s),
DispatchError::CannotLookup => crate::DispatchError::CannotLookup,
DispatchError::BadOrigin => crate::DispatchError::BadOrigin,
DispatchError::Module(err) => crate::DispatchError::Module(crate::ModuleError {
index: err.index,
error: [err.error, 0, 0, 0],
message: err.message,
}),
DispatchError::ConsumerRemaining => crate::DispatchError::ConsumerRemaining,
DispatchError::NoProviders => crate::DispatchError::NoProviders,
DispatchError::TooManyConsumers => crate::DispatchError::TooManyConsumers,
DispatchError::Token(err) => crate::DispatchError::Token(err),
DispatchError::Arithmetic(err) => crate::DispatchError::Arithmetic(err),
})
})
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
// 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.
//! MultiAddress type is a wrapper for multiple downstream account formats.
use alloc::vec::Vec;
use codec::{Decode, DecodeWithMemTracking, Encode};
/// A multi-format address wrapper for on-chain accounts.
#[derive(
Encode,
Decode,
DecodeWithMemTracking,
PartialEq,
Eq,
Clone,
crate::RuntimeDebug,
scale_info::TypeInfo,
)]
#[cfg_attr(feature = "std", derive(Hash))]
pub enum MultiAddress<AccountId, AccountIndex> {
/// It's an account ID (pubkey).
Id(AccountId),
/// It's an account index.
Index(#[codec(compact)] AccountIndex),
/// It's some arbitrary raw bytes.
Raw(Vec<u8>),
/// It's a 32 byte representation.
Address32([u8; 32]),
/// It's a 20 byte representation.
Address20([u8; 20]),
}
#[cfg(feature = "std")]
impl<AccountId, AccountIndex> std::fmt::Display for MultiAddress<AccountId, AccountIndex>
where
AccountId: std::fmt::Debug,
AccountIndex: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use pezsp_core::hexdisplay::HexDisplay;
match self {
Self::Raw(inner) => write!(f, "MultiAddress::Raw({})", HexDisplay::from(inner)),
Self::Address32(inner) => {
write!(f, "MultiAddress::Address32({})", HexDisplay::from(inner))
},
Self::Address20(inner) => {
write!(f, "MultiAddress::Address20({})", HexDisplay::from(inner))
},
_ => write!(f, "{:?}", self),
}
}
}
impl<AccountId, AccountIndex> From<AccountId> for MultiAddress<AccountId, AccountIndex> {
fn from(a: AccountId) -> Self {
Self::Id(a)
}
}
@@ -0,0 +1,609 @@
// 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.
//! A high-level helpers for making HTTP requests from Offchain Workers.
//!
//! `sp-io` crate exposes a low level methods to make and control HTTP requests
//! available only for Offchain Workers. Those might be hard to use
//! and usually that level of control is not really necessary.
//! This module aims to provide high-level wrappers for those APIs
//! to simplify making HTTP requests.
//!
//!
//! Example:
//! ```rust,no_run
//! use pezsp_runtime::offchain::http::Request;
//!
//! // initiate a GET request to localhost:1234
//! let request: Request = Request::get("http://localhost:1234");
//! let pending = request
//! .add_header("X-Auth", "hunter2")
//! .send()
//! .unwrap();
//!
//! // wait for the response indefinitely
//! let mut response = pending.wait().unwrap();
//!
//! // then check the headers
//! let mut headers = response.headers().into_iter();
//! assert_eq!(headers.current(), None);
//!
//! // and collect the body
//! let body = response.body();
//! assert_eq!(body.clone().collect::<Vec<_>>(), b"1234".to_vec());
//! assert_eq!(body.error(), &None);
//! ```
use alloc::{str, vec, vec::Vec};
use pezsp_core::{
offchain::{
HttpError, HttpRequestId as RequestId, HttpRequestStatus as RequestStatus, Timestamp,
},
RuntimeDebug,
};
/// Request method (HTTP verb)
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub enum Method {
/// GET request
Get,
/// POST request
Post,
/// PUT request
Put,
/// PATCH request
Patch,
/// DELETE request
Delete,
/// Custom verb
Other(&'static str),
}
impl AsRef<str> for Method {
fn as_ref(&self) -> &str {
match *self {
Method::Get => "GET",
Method::Post => "POST",
Method::Put => "PUT",
Method::Patch => "PATCH",
Method::Delete => "DELETE",
Method::Other(m) => m,
}
}
}
mod header {
use super::*;
/// A header type.
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub struct Header {
name: Vec<u8>,
value: Vec<u8>,
}
impl Header {
/// Creates new header given it's name and value.
pub fn new(name: &str, value: &str) -> Self {
Header { name: name.as_bytes().to_vec(), value: value.as_bytes().to_vec() }
}
/// Returns the name of this header.
pub fn name(&self) -> &str {
// Header keys are always produced from `&str` so this is safe.
// we don't store them as `Strings` to avoid bringing `alloc::String` to sp-std
// or here.
unsafe { str::from_utf8_unchecked(&self.name) }
}
/// Returns the value of this header.
pub fn value(&self) -> &str {
// Header values are always produced from `&str` so this is safe.
// we don't store them as `Strings` to avoid bringing `alloc::String` to sp-std
// or here.
unsafe { str::from_utf8_unchecked(&self.value) }
}
}
}
/// An HTTP request builder.
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub struct Request<'a, T = Vec<&'static [u8]>> {
/// Request method
pub method: Method,
/// Request URL
pub url: &'a str,
/// Body of the request
pub body: T,
/// Deadline to finish sending the request
pub deadline: Option<Timestamp>,
/// Request list of headers.
headers: Vec<header::Header>,
}
impl<T: Default> Default for Request<'static, T> {
fn default() -> Self {
Request {
method: Method::Get,
url: "http://localhost",
headers: Vec::new(),
body: Default::default(),
deadline: None,
}
}
}
impl<'a> Request<'a> {
/// Start a simple GET request
pub fn get(url: &'a str) -> Self {
Self::new(url)
}
}
impl<'a, T> Request<'a, T> {
/// Create new POST request with given body.
pub fn post(url: &'a str, body: T) -> Self {
let req: Request = Request::default();
Request { url, body, method: Method::Post, headers: req.headers, deadline: req.deadline }
}
}
impl<'a, T: Default> Request<'a, T> {
/// Create a new Request builder with the given URL.
pub fn new(url: &'a str) -> Self {
Request::default().url(url)
}
/// Change the method of the request
pub fn method(mut self, method: Method) -> Self {
self.method = method;
self
}
/// Change the URL of the request.
pub fn url(mut self, url: &'a str) -> Self {
self.url = url;
self
}
/// Set the body of the request.
pub fn body(mut self, body: T) -> Self {
self.body = body;
self
}
/// Add a header.
pub fn add_header(mut self, name: &str, value: &str) -> Self {
self.headers.push(header::Header::new(name, value));
self
}
/// Set the deadline of the request.
pub fn deadline(mut self, deadline: Timestamp) -> Self {
self.deadline = Some(deadline);
self
}
}
impl<'a, I: AsRef<[u8]>, T: IntoIterator<Item = I>> Request<'a, T> {
/// Send the request and return a handle.
///
/// Err is returned in case the deadline is reached
/// or the request timeouts.
pub fn send(self) -> Result<PendingRequest, HttpError> {
let meta = &[];
// start an http request.
let id = pezsp_io::offchain::http_request_start(self.method.as_ref(), self.url, meta)
.map_err(|_| HttpError::IoError)?;
// add custom headers
for header in &self.headers {
pezsp_io::offchain::http_request_add_header(id, header.name(), header.value())
.map_err(|_| HttpError::IoError)?
}
// write body
for chunk in self.body {
pezsp_io::offchain::http_request_write_body(id, chunk.as_ref(), self.deadline)?;
}
// finalize the request
pezsp_io::offchain::http_request_write_body(id, &[], self.deadline)?;
Ok(PendingRequest { id })
}
}
/// A request error
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub enum Error {
/// Deadline has been reached.
DeadlineReached,
/// Request had timed out.
IoError,
/// Unknown error has been encountered.
Unknown,
}
/// A struct representing an uncompleted http request.
#[derive(PartialEq, Eq, RuntimeDebug)]
pub struct PendingRequest {
/// Request ID
pub id: RequestId,
}
/// A result of waiting for a pending request.
pub type HttpResult = Result<Response, Error>;
impl PendingRequest {
/// Wait for the request to complete.
///
/// NOTE this waits for the request indefinitely.
pub fn wait(self) -> HttpResult {
match self.try_wait(None) {
Ok(res) => res,
Err(_) => panic!("Since `None` is passed we will never get a deadline error; qed"),
}
}
/// Attempts to wait for the request to finish,
/// but will return `Err` in case the deadline is reached.
pub fn try_wait(
self,
deadline: impl Into<Option<Timestamp>>,
) -> Result<HttpResult, PendingRequest> {
Self::try_wait_all(vec![self], deadline)
.pop()
.expect("One request passed, one status received; qed")
}
/// Wait for all provided requests.
pub fn wait_all(requests: Vec<PendingRequest>) -> Vec<HttpResult> {
Self::try_wait_all(requests, None)
.into_iter()
.map(|r| match r {
Ok(r) => r,
Err(_) => panic!("Since `None` is passed we will never get a deadline error; qed"),
})
.collect()
}
/// Attempt to wait for all provided requests, but up to given deadline.
///
/// Requests that are complete will resolve to an `Ok` others will return a `DeadlineReached`
/// error.
pub fn try_wait_all(
requests: Vec<PendingRequest>,
deadline: impl Into<Option<Timestamp>>,
) -> Vec<Result<HttpResult, PendingRequest>> {
let ids = requests.iter().map(|r| r.id).collect::<Vec<_>>();
let statuses = pezsp_io::offchain::http_response_wait(&ids, deadline.into());
statuses
.into_iter()
.zip(requests.into_iter())
.map(|(status, req)| match status {
RequestStatus::DeadlineReached => Err(req),
RequestStatus::IoError => Ok(Err(Error::IoError)),
RequestStatus::Invalid => Ok(Err(Error::Unknown)),
RequestStatus::Finished(code) => Ok(Ok(Response::new(req.id, code))),
})
.collect()
}
}
/// A HTTP response.
#[derive(RuntimeDebug)]
pub struct Response {
/// Request id
pub id: RequestId,
/// Response status code
pub code: u16,
/// A collection of headers.
headers: Option<Headers>,
}
impl Response {
fn new(id: RequestId, code: u16) -> Self {
Self { id, code, headers: None }
}
/// Retrieve the headers for this response.
pub fn headers(&mut self) -> &Headers {
if self.headers.is_none() {
self.headers = Some(Headers { raw: pezsp_io::offchain::http_response_headers(self.id) });
}
self.headers.as_ref().expect("Headers were just set; qed")
}
/// Retrieve the body of this response.
pub fn body(&self) -> ResponseBody {
ResponseBody::new(self.id)
}
}
/// A buffered byte iterator over response body.
///
/// Note that reading the body may return `None` in following cases:
/// 1. Either the deadline you've set is reached (check via `#error`; In such case you can resume
/// the reader by setting a new deadline)
/// 2. Or because of IOError. In such case the reader is not resumable and will keep returning
/// `None`.
/// 3. The body has been returned. The reader will keep returning `None`.
#[derive(Clone)]
pub struct ResponseBody {
id: RequestId,
error: Option<HttpError>,
buffer: [u8; 4096],
filled_up_to: Option<usize>,
position: usize,
deadline: Option<Timestamp>,
}
#[cfg(feature = "std")]
impl std::fmt::Debug for ResponseBody {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("ResponseBody")
.field("id", &self.id)
.field("error", &self.error)
.field("buffer", &self.buffer.len())
.field("filled_up_to", &self.filled_up_to)
.field("position", &self.position)
.field("deadline", &self.deadline)
.finish()
}
}
impl ResponseBody {
fn new(id: RequestId) -> Self {
ResponseBody {
id,
error: None,
buffer: [0_u8; 4096],
filled_up_to: None,
position: 0,
deadline: None,
}
}
/// Set the deadline for reading the body.
pub fn deadline(&mut self, deadline: impl Into<Option<Timestamp>>) {
self.deadline = deadline.into();
self.error = None;
}
/// Return an error that caused the iterator to return `None`.
///
/// If the error is `DeadlineReached` you can resume the iterator by setting
/// a new deadline.
pub fn error(&self) -> &Option<HttpError> {
&self.error
}
}
impl Iterator for ResponseBody {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
if self.error.is_some() {
return None;
}
if self.filled_up_to.is_none() {
let result =
pezsp_io::offchain::http_response_read_body(self.id, &mut self.buffer, self.deadline);
match result {
Err(e) => {
self.error = Some(e);
return None;
},
Ok(0) => return None,
Ok(size) => {
self.position = 0;
self.filled_up_to = Some(size as usize);
},
}
}
if Some(self.position) == self.filled_up_to {
self.filled_up_to = None;
return self.next();
}
let result = self.buffer[self.position];
self.position += 1;
Some(result)
}
}
/// A collection of Headers in the response.
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
pub struct Headers {
/// Raw headers
pub raw: Vec<(Vec<u8>, Vec<u8>)>,
}
impl Headers {
/// Retrieve a single header from the list of headers.
///
/// Note this method is linearly looking from all the headers
/// comparing them with the needle byte-by-byte.
/// If you want to consume multiple headers it's better to iterate
/// and collect them on your own.
pub fn find(&self, name: &str) -> Option<&str> {
let raw = name.as_bytes();
for (key, val) in &self.raw {
if &**key == raw {
return str::from_utf8(val).ok();
}
}
None
}
/// Convert this headers into an iterator.
pub fn into_iter(&self) -> HeadersIterator<'_> {
HeadersIterator { collection: &self.raw, index: None }
}
}
/// A custom iterator traversing all the headers.
#[derive(Clone, RuntimeDebug)]
pub struct HeadersIterator<'a> {
collection: &'a [(Vec<u8>, Vec<u8>)],
index: Option<usize>,
}
impl<'a> HeadersIterator<'a> {
/// Move the iterator to the next position.
///
/// Returns `true` is `current` has been set by this call.
pub fn next(&mut self) -> bool {
let index = self.index.map(|x| x + 1).unwrap_or(0);
self.index = Some(index);
index < self.collection.len()
}
/// Returns current element (if any).
///
/// Note that you have to call `next` prior to calling this
pub fn current(&self) -> Option<(&str, &str)> {
self.collection
.get(self.index?)
.map(|val| (str::from_utf8(&val.0).unwrap_or(""), str::from_utf8(&val.1).unwrap_or("")))
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezsp_core::offchain::{testing, OffchainWorkerExt};
use pezsp_io::TestExternalities;
#[test]
fn should_send_a_basic_request_and_get_response() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let request: Request = Request::get("http://localhost:1234");
let pending = request.add_header("X-Auth", "hunter2").send().unwrap();
// make sure it's sent correctly
state.write().fulfill_pending_request(
0,
testing::PendingRequest {
method: "GET".into(),
uri: "http://localhost:1234".into(),
headers: vec![("X-Auth".into(), "hunter2".into())],
sent: true,
..Default::default()
},
b"1234".to_vec(),
None,
);
// wait
let mut response = pending.wait().unwrap();
// then check the response
let mut headers = response.headers().into_iter();
assert_eq!(headers.current(), None);
assert_eq!(headers.next(), false);
assert_eq!(headers.current(), None);
let body = response.body();
assert_eq!(body.clone().collect::<Vec<_>>(), b"1234".to_vec());
assert_eq!(body.error(), &None);
})
}
#[test]
fn should_send_huge_response() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let request: Request = Request::get("http://localhost:1234");
let pending = request.add_header("X-Auth", "hunter2").send().unwrap();
// make sure it's sent correctly
state.write().fulfill_pending_request(
0,
testing::PendingRequest {
method: "GET".into(),
uri: "http://localhost:1234".into(),
headers: vec![("X-Auth".into(), "hunter2".into())],
sent: true,
..Default::default()
},
vec![0; 5923],
None,
);
// wait
let response = pending.wait().unwrap();
let body = response.body();
assert_eq!(body.clone().collect::<Vec<_>>(), vec![0; 5923]);
assert_eq!(body.error(), &None);
})
}
#[test]
fn should_send_a_post_request() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let pending = Request::default()
.method(Method::Post)
.url("http://localhost:1234")
.body(vec![b"1234"])
.send()
.unwrap();
// make sure it's sent correctly
state.write().fulfill_pending_request(
0,
testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:1234".into(),
body: b"1234".to_vec(),
sent: true,
..Default::default()
},
b"1234".to_vec(),
Some(("Test".to_owned(), "Header".to_owned())),
);
// wait
let mut response = pending.wait().unwrap();
// then check the response
let mut headers = response.headers().into_iter();
assert_eq!(headers.current(), None);
assert_eq!(headers.next(), true);
assert_eq!(headers.current(), Some(("Test", "Header")));
let body = response.body();
assert_eq!(body.clone().collect::<Vec<_>>(), b"1234".to_vec());
assert_eq!(body.error(), &None);
})
}
}
@@ -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.
//! A collection of higher lever helpers for offchain calls.
pub mod http;
pub mod storage;
pub mod storage_lock;
pub use pezsp_core::offchain::*;
@@ -0,0 +1,174 @@
// 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.
//! A set of storage helpers for offchain workers.
use pezsp_core::offchain::StorageKind;
/// A storage value with a static key.
pub type StorageValue = StorageValueRef<'static>;
/// An abstraction over local storage value.
pub struct StorageValueRef<'a> {
key: &'a [u8],
kind: StorageKind,
}
/// Reason for not being able to provide the stored value
#[derive(Debug, PartialEq, Eq)]
pub enum StorageRetrievalError {
/// Value found but undecodable
Undecodable,
}
/// Possible errors when mutating a storage value.
#[derive(Debug, PartialEq, Eq)]
pub enum MutateStorageError<T, E> {
/// The underlying db failed to update due to a concurrent modification.
/// Contains the new value that was not stored.
ConcurrentModification(T),
/// The function given to us to create the value to be stored failed.
/// May be used to signal that having looked at the existing value,
/// they don't want to mutate it.
ValueFunctionFailed(E),
}
impl<'a> StorageValueRef<'a> {
/// Create a new reference to a value in the persistent local storage.
pub fn persistent(key: &'a [u8]) -> Self {
Self { key, kind: StorageKind::PERSISTENT }
}
/// Create a new reference to a value in the fork-aware local storage.
pub fn local(key: &'a [u8]) -> Self {
Self { key, kind: StorageKind::LOCAL }
}
/// Set the value of the storage to encoding of given parameter.
///
/// Note that the storage may be accessed by workers running concurrently,
/// if you happen to write a `get-check-set` pattern you should most likely
/// be using `mutate` instead.
pub fn set(&self, value: &impl codec::Encode) {
value.using_encoded(|val| pezsp_io::offchain::local_storage_set(self.kind, self.key, val))
}
/// Remove the associated value from the storage.
pub fn clear(&mut self) {
pezsp_io::offchain::local_storage_clear(self.kind, self.key)
}
/// Retrieve & decode the value from storage.
///
/// Note that if you want to do some checks based on the value
/// and write changes after that, you should rather be using `mutate`.
///
/// Returns the value if stored.
/// Returns an error if the value could not be decoded.
pub fn get<T: codec::Decode>(&self) -> Result<Option<T>, StorageRetrievalError> {
pezsp_io::offchain::local_storage_get(self.kind, self.key)
.map(|val| T::decode(&mut &*val).map_err(|_| StorageRetrievalError::Undecodable))
.transpose()
}
/// Retrieve & decode the current value and set it to a new value atomically.
///
/// Function `mutate_val` takes as input the current value and should
/// return a new value that is attempted to be written to storage.
///
/// This function returns:
/// 1. `Ok(T)` in case the value has been successfully set.
/// 2. `Err(MutateStorageError::ConcurrentModification(T))` in case the value was calculated
/// by the passed closure `mutate_val`, but it could not be stored.
/// 3. `Err(MutateStorageError::ValueFunctionFailed(_))` in case `mutate_val` returns an error.
pub fn mutate<T, E, F>(&self, mutate_val: F) -> Result<T, MutateStorageError<T, E>>
where
T: codec::Codec,
F: FnOnce(Result<Option<T>, StorageRetrievalError>) -> Result<T, E>,
{
let value = pezsp_io::offchain::local_storage_get(self.kind, self.key);
let decoded = value
.as_deref()
.map(|mut bytes| T::decode(&mut bytes).map_err(|_| StorageRetrievalError::Undecodable))
.transpose();
let val =
mutate_val(decoded).map_err(|err| MutateStorageError::ValueFunctionFailed(err))?;
let set = val.using_encoded(|new_val| {
pezsp_io::offchain::local_storage_compare_and_set(self.kind, self.key, value, new_val)
});
if set {
Ok(val)
} else {
Err(MutateStorageError::ConcurrentModification(val))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezsp_core::offchain::{testing, OffchainDbExt};
use pezsp_io::TestExternalities;
#[test]
fn should_set_and_get() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainDbExt::new(offchain));
t.execute_with(|| {
let val = StorageValue::persistent(b"testval");
assert_eq!(val.get::<u32>(), Ok(None));
val.set(&15_u32);
assert_eq!(val.get::<u32>(), Ok(Some(15_u32)));
assert_eq!(val.get::<Vec<u8>>(), Err(StorageRetrievalError::Undecodable));
assert_eq!(state.read().persistent_storage.get(b"testval"), Some(vec![15_u8, 0, 0, 0]));
})
}
#[test]
fn should_mutate() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainDbExt::new(offchain));
t.execute_with(|| {
let val = StorageValue::persistent(b"testval");
let result = val.mutate::<u32, (), _>(|val| {
assert_eq!(val, Ok(None));
Ok(16_u32)
});
assert_eq!(result, Ok(16_u32));
assert_eq!(val.get::<u32>(), Ok(Some(16_u32)));
assert_eq!(state.read().persistent_storage.get(b"testval"), Some(vec![16_u8, 0, 0, 0]));
// mutate again, but this time early-exit.
let res = val.mutate::<u32, (), _>(|val| {
assert_eq!(val, Ok(Some(16_u32)));
Err(())
});
assert_eq!(res, Err(MutateStorageError::ValueFunctionFailed(())));
})
}
}
@@ -0,0 +1,579 @@
// 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.
//! # Off-chain Storage Lock
//!
//! A storage-based lock with a defined expiry time.
//!
//! The lock is using Local Storage and allows synchronizing access to critical
//! section of your code for concurrently running Off-chain Workers. Usage of
//! `PERSISTENT` variant of the storage persists the lock value across a full node
//! restart or re-orgs.
//!
//! A use case for the lock is to make sure that a particular section of the
//! code is only run by one Off-chain Worker at a time. This may include
//! performing a side-effect (i.e. an HTTP call) or alteration of single or
//! multiple Local Storage entries.
//!
//! One use case would be collective updates of multiple data items or append /
//! remove of i.e. sets, vectors which are stored in the off-chain storage DB.
//!
//! ## Example:
//!
//! ```rust
//! # use codec::{Decode, Encode, Codec};
//! // in your off-chain worker code
//! use pezsp_runtime::offchain::{
//! storage::StorageValueRef,
//! storage_lock::{StorageLock, Time},
//! };
//!
//! fn append_to_in_storage_vec<'a, T>(key: &'a [u8], _: T) where T: Codec {
//! // `access::lock` defines the storage entry which is used for
//! // persisting the lock in the underlying database.
//! // The entry name _must_ be unique and can be interpreted as a
//! // unique mutex instance reference tag.
//! let mut lock = StorageLock::<Time>::new(b"access::lock");
//! {
//! let _guard = lock.lock();
//! let acc = StorageValueRef::persistent(key);
//! let v: Vec<T> = acc.get::<Vec<T>>().unwrap().unwrap();
//! // modify `v` as desired
//! // i.e. perform some heavy computation with
//! // side effects that should only be done once.
//! acc.set(&v);
//! // drop `_guard` implicitly at end of scope
//! }
//! }
//! ```
use crate::{
offchain::storage::{MutateStorageError, StorageRetrievalError, StorageValueRef},
traits::BlockNumberProvider,
};
use codec::{Codec, Decode, Encode};
use core::fmt;
use pezsp_core::offchain::{Duration, Timestamp};
use pezsp_io::offchain;
/// Default expiry duration for time based locks in milliseconds.
const STORAGE_LOCK_DEFAULT_EXPIRY_DURATION: Duration = Duration::from_millis(20_000);
/// Default expiry duration for block based locks in blocks.
const STORAGE_LOCK_DEFAULT_EXPIRY_BLOCKS: u32 = 4;
/// Time between checks if the lock is still being held in milliseconds.
const STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MIN: Duration = Duration::from_millis(10);
const STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MAX: Duration = Duration::from_millis(100);
/// Lockable item for use with a persisted storage lock.
///
/// Bound for an item that has a stateful ordered meaning
/// without explicitly requiring `Ord` trait in general.
pub trait Lockable: Sized {
/// An instant type specifying i.e. a point in time.
type Deadline: Sized + Codec + Clone;
/// Calculate the deadline based on a current state
/// such as time or block number and derives the deadline.
fn deadline(&self) -> Self::Deadline;
/// Verify the deadline has not expired compared to the
/// current state, i.e. time or block number.
fn has_expired(deadline: &Self::Deadline) -> bool;
/// Snooze at least until `deadline` is reached.
///
/// Note that `deadline` is only passed to allow optimizations
/// for `Lockables` which have a time based component.
fn snooze(_deadline: &Self::Deadline) {
pezsp_io::offchain::sleep_until(
offchain::timestamp().add(STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MAX),
);
}
}
/// Lockable based on the current timestamp with a configurable expiration time.
#[derive(Encode, Decode)]
pub struct Time {
/// How long the lock will stay valid once `fn lock(..)` or
/// `fn try_lock(..)` successfully acquire a lock.
expiration_duration: Duration,
}
impl Default for Time {
fn default() -> Self {
Self { expiration_duration: STORAGE_LOCK_DEFAULT_EXPIRY_DURATION }
}
}
impl Lockable for Time {
type Deadline = Timestamp;
fn deadline(&self) -> Self::Deadline {
offchain::timestamp().add(self.expiration_duration)
}
fn has_expired(deadline: &Self::Deadline) -> bool {
offchain::timestamp() > *deadline
}
fn snooze(deadline: &Self::Deadline) {
let now = offchain::timestamp();
let remainder: Duration = now.diff(deadline);
// do not snooze the full duration, but instead snooze max 100ms
// it might get unlocked in another thread
let snooze = remainder.clamp(
STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MIN,
STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MAX,
);
pezsp_io::offchain::sleep_until(now.add(snooze));
}
}
/// A deadline based on block number and time.
#[derive(Encode, Decode, Eq, PartialEq)]
pub struct BlockAndTimeDeadline<B: BlockNumberProvider> {
/// The block number until which the lock is still valid _at least_.
pub block_number: <B as BlockNumberProvider>::BlockNumber,
/// The timestamp until which the lock is still valid _at least_.
pub timestamp: Timestamp,
}
impl<B: BlockNumberProvider> Clone for BlockAndTimeDeadline<B> {
fn clone(&self) -> Self {
Self { block_number: self.block_number, timestamp: self.timestamp }
}
}
impl<B: BlockNumberProvider> Default for BlockAndTimeDeadline<B> {
/// Provide the current state of block number and time.
fn default() -> Self {
Self {
block_number: B::current_block_number() + STORAGE_LOCK_DEFAULT_EXPIRY_BLOCKS.into(),
timestamp: offchain::timestamp().add(STORAGE_LOCK_DEFAULT_EXPIRY_DURATION),
}
}
}
impl<B: BlockNumberProvider> fmt::Debug for BlockAndTimeDeadline<B>
where
<B as BlockNumberProvider>::BlockNumber: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BlockAndTimeDeadline")
.field("block_number", &self.block_number)
.field("timestamp", &self.timestamp)
.finish()
}
}
/// Lockable based on block number and timestamp.
///
/// Expiration is defined if both, block number _and_ timestamp
/// expire.
pub struct BlockAndTime<B: BlockNumberProvider> {
/// Relative block number offset, which is used to determine
/// the block number part of the deadline.
expiration_block_number_offset: u32,
/// Relative duration, used to derive the time based part of
/// the deadline.
expiration_duration: Duration,
_phantom: core::marker::PhantomData<B>,
}
impl<B: BlockNumberProvider> Default for BlockAndTime<B> {
fn default() -> Self {
Self {
expiration_block_number_offset: STORAGE_LOCK_DEFAULT_EXPIRY_BLOCKS,
expiration_duration: STORAGE_LOCK_DEFAULT_EXPIRY_DURATION,
_phantom: core::marker::PhantomData::<B>,
}
}
}
// derive not possible, since `B` does not necessarily implement `trait Clone`
impl<B: BlockNumberProvider> Clone for BlockAndTime<B> {
fn clone(&self) -> Self {
Self {
expiration_block_number_offset: self.expiration_block_number_offset,
expiration_duration: self.expiration_duration,
_phantom: core::marker::PhantomData::<B>,
}
}
}
impl<B: BlockNumberProvider> Lockable for BlockAndTime<B> {
type Deadline = BlockAndTimeDeadline<B>;
fn deadline(&self) -> Self::Deadline {
let block_number = <B as BlockNumberProvider>::current_block_number() +
self.expiration_block_number_offset.into();
BlockAndTimeDeadline {
timestamp: offchain::timestamp().add(self.expiration_duration),
block_number,
}
}
fn has_expired(deadline: &Self::Deadline) -> bool {
offchain::timestamp() > deadline.timestamp &&
<B as BlockNumberProvider>::current_block_number() > deadline.block_number
}
fn snooze(deadline: &Self::Deadline) {
let now = offchain::timestamp();
let remainder: Duration = now.diff(&(deadline.timestamp));
let snooze = remainder.clamp(
STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MIN,
STORAGE_LOCK_PER_CHECK_ITERATION_SNOOZE_MAX,
);
pezsp_io::offchain::sleep_until(now.add(snooze));
}
}
/// Storage based lock.
///
/// A lock that is persisted in the DB and provides the ability to guard against
/// concurrent access in an off-chain worker, with a defined expiry deadline
/// based on the concrete [`Lockable`] implementation.
pub struct StorageLock<'a, L = Time> {
// A storage value ref which defines the DB entry representing the lock.
value_ref: StorageValueRef<'a>,
lockable: L,
}
impl<'a, L: Lockable + Default> StorageLock<'a, L> {
/// Create a new storage lock with a `default()` instance of type `L`.
pub fn new(key: &'a [u8]) -> Self {
Self::with_lockable(key, Default::default())
}
}
impl<'a, L: Lockable> StorageLock<'a, L> {
/// Create a new storage lock with an explicit instance of a lockable `L`.
pub fn with_lockable(key: &'a [u8], lockable: L) -> Self {
Self { value_ref: StorageValueRef::<'a>::persistent(key), lockable }
}
/// Extend active lock's deadline
fn extend_active_lock(&mut self) -> Result<<L as Lockable>::Deadline, ()> {
let res = self.value_ref.mutate(
|s: Result<Option<L::Deadline>, StorageRetrievalError>| -> Result<<L as Lockable>::Deadline, ()> {
match s {
// lock is present and is still active, extend the lock.
Ok(Some(deadline)) if !<L as Lockable>::has_expired(&deadline) =>
Ok(self.lockable.deadline()),
// other cases
_ => Err(()),
}
});
match res {
Ok(deadline) => Ok(deadline),
Err(MutateStorageError::ConcurrentModification(_)) => Err(()),
Err(MutateStorageError::ValueFunctionFailed(e)) => Err(e),
}
}
/// Internal lock helper to avoid lifetime conflicts.
fn try_lock_inner(
&mut self,
new_deadline: L::Deadline,
) -> Result<(), <L as Lockable>::Deadline> {
let res = self.value_ref.mutate(
|s: Result<Option<L::Deadline>, StorageRetrievalError>|
-> Result<<L as Lockable>::Deadline, <L as Lockable>::Deadline> {
match s {
// no lock set, we can safely acquire it
Ok(None) => Ok(new_deadline),
// write was good, but read failed
Err(_) => Ok(new_deadline),
// lock is set, but it is expired. We can re-acquire it.
Ok(Some(deadline)) if <L as Lockable>::has_expired(&deadline) =>
Ok(new_deadline),
// lock is present and is still active
Ok(Some(deadline)) => Err(deadline),
}
},
);
match res {
Ok(_) => Ok(()),
Err(MutateStorageError::ConcurrentModification(deadline)) => Err(deadline),
Err(MutateStorageError::ValueFunctionFailed(e)) => Err(e),
}
}
/// A single attempt to lock using the storage entry.
///
/// Returns a lock guard on success, otherwise an error containing the
/// `<Self::Lockable>::Deadline` in for the currently active lock
/// by another task `Err(<L as Lockable>::Deadline)`.
pub fn try_lock(&mut self) -> Result<StorageLockGuard<'a, '_, L>, <L as Lockable>::Deadline> {
self.try_lock_inner(self.lockable.deadline())?;
Ok(StorageLockGuard::<'a, '_> { lock: Some(self) })
}
/// Repeated lock attempts until the lock is successfully acquired.
///
/// If one uses `fn forget(..)`, it is highly likely `fn try_lock(..)`
/// is the correct API to use instead of `fn lock(..)`, since that might
/// never unlock in the anticipated span i.e. when used with `BlockAndTime`
/// during a certain block number span.
pub fn lock(&mut self) -> StorageLockGuard<'a, '_, L> {
while let Err(deadline) = self.try_lock_inner(self.lockable.deadline()) {
L::snooze(&deadline);
}
StorageLockGuard::<'a, '_, L> { lock: Some(self) }
}
/// Explicitly unlock the lock.
fn unlock(&mut self) {
self.value_ref.clear();
}
}
/// RAII style guard for a lock.
pub struct StorageLockGuard<'a, 'b, L: Lockable> {
lock: Option<&'b mut StorageLock<'a, L>>,
}
impl<'a, 'b, L: Lockable> StorageLockGuard<'a, 'b, L> {
/// Consume the guard but **do not** unlock the underlying lock.
///
/// Can be used to implement a grace period after doing some
/// heavy computations and sending a transaction to be included
/// on-chain. By forgetting the lock, it will stay locked until
/// its expiration deadline is reached while the off-chain worker
/// can already exit.
pub fn forget(mut self) {
let _ = self.lock.take();
}
/// Extend the lock by guard deadline if it already exists.
///
/// i.e. large sets of items for which it is hard to calculate a
/// meaning full conservative deadline which does not block for a
/// very long time on node termination.
pub fn extend_lock(&mut self) -> Result<<L as Lockable>::Deadline, ()> {
if let Some(ref mut lock) = self.lock {
lock.extend_active_lock()
} else {
Err(())
}
}
}
impl<'a, 'b, L: Lockable> Drop for StorageLockGuard<'a, 'b, L> {
fn drop(&mut self) {
if let Some(lock) = self.lock.take() {
lock.unlock();
}
}
}
impl<'a> StorageLock<'a, Time> {
/// Explicitly create a time based storage lock with a non-default
/// expiration timeout.
pub fn with_deadline(key: &'a [u8], expiration_duration: Duration) -> Self {
Self {
value_ref: StorageValueRef::<'a>::persistent(key),
lockable: Time { expiration_duration },
}
}
}
impl<'a, B> StorageLock<'a, BlockAndTime<B>>
where
B: BlockNumberProvider,
{
/// Explicitly create a time and block number based storage lock with
/// a non-default expiration duration and block number offset.
pub fn with_block_and_time_deadline(
key: &'a [u8],
expiration_block_number_offset: u32,
expiration_duration: Duration,
) -> Self {
Self {
value_ref: StorageValueRef::<'a>::persistent(key),
lockable: BlockAndTime::<B> {
expiration_block_number_offset,
expiration_duration,
_phantom: core::marker::PhantomData,
},
}
}
/// Explicitly create a time and block number based storage lock with
/// the default expiration duration and a non-default block number offset.
pub fn with_block_deadline(key: &'a [u8], expiration_block_number_offset: u32) -> Self {
Self {
value_ref: StorageValueRef::<'a>::persistent(key),
lockable: BlockAndTime::<B> {
expiration_block_number_offset,
expiration_duration: STORAGE_LOCK_DEFAULT_EXPIRY_DURATION,
_phantom: core::marker::PhantomData,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezsp_core::offchain::{testing, OffchainDbExt, OffchainWorkerExt};
use pezsp_io::TestExternalities;
const VAL_1: u32 = 0u32;
const VAL_2: u32 = 0xFFFF_FFFFu32;
#[test]
fn storage_lock_write_unlock_lock_read_unlock() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let mut lock = StorageLock::<'_, Time>::new(b"lock_1");
let val = StorageValueRef::persistent(b"protected_value");
{
let _guard = lock.lock();
val.set(&VAL_1);
assert_eq!(val.get::<u32>(), Ok(Some(VAL_1)));
}
{
let _guard = lock.lock();
val.set(&VAL_2);
assert_eq!(val.get::<u32>(), Ok(Some(VAL_2)));
}
});
// lock must have been cleared at this point
assert_eq!(state.read().persistent_storage.get(b"lock_1"), None);
}
#[test]
fn storage_lock_and_forget() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let mut lock = StorageLock::<'_, Time>::new(b"lock_2");
let val = StorageValueRef::persistent(b"protected_value");
let guard = lock.lock();
val.set(&VAL_1);
assert_eq!(val.get::<u32>(), Ok(Some(VAL_1)));
guard.forget();
});
// lock must have been cleared at this point
let opt = state.read().persistent_storage.get(b"lock_2");
assert!(opt.is_some());
}
#[test]
fn storage_lock_and_let_expire_and_lock_again() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let sleep_until = offchain::timestamp().add(Duration::from_millis(500));
let lock_expiration = Duration::from_millis(200);
let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_3", lock_expiration);
{
let guard = lock.lock();
guard.forget();
}
// assure the lock expires
offchain::sleep_until(sleep_until);
let mut lock = StorageLock::<'_, Time>::new(b"lock_3");
let res = lock.try_lock();
assert!(res.is_ok());
let guard = res.unwrap();
guard.forget();
});
// lock must have been cleared at this point
let opt = state.read().persistent_storage.get(b"lock_3");
assert!(opt.is_some());
}
#[test]
fn extend_active_lock() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let lock_expiration = Duration::from_millis(300);
let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration);
let mut guard = lock.lock();
// sleep_until < lock_expiration
offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200)));
// the lock is still active, extend it successfully
assert_eq!(guard.extend_lock().is_ok(), true);
// sleep_until < deadline
offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200)));
// the lock is still active, try_lock will fail
let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration);
let res = lock.try_lock();
assert_eq!(res.is_ok(), false);
// sleep again until sleep_until > deadline
offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200)));
// the lock has expired, failed to extend it
assert_eq!(guard.extend_lock().is_ok(), false);
guard.forget();
// try_lock will succeed
let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration);
let res = lock.try_lock();
assert!(res.is_ok());
let guard = res.unwrap();
guard.forget();
});
// lock must have been cleared at this point
let opt = state.read().persistent_storage.get(b"lock_4");
assert_eq!(opt.unwrap(), vec![132_u8, 3u8, 0, 0, 0, 0, 0, 0]); // 132 + 256 * 3 = 900
}
}
@@ -0,0 +1,327 @@
// 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.
//! Types for a compact base-16 merkle trie used for checking and generating proofs within the
//! runtime. The `sp-trie` crate exposes all of these same functionality (and more), but this
//! library is designed to work more easily with runtime native types, which simply need to
//! implement `Encode`/`Decode`. It also exposes a runtime friendly `TrieError` type which can be
//! use inside of a FRAME Pallet.
//!
//! Proofs are created with latest bizinikiwi trie format (`LayoutV1`), and are not compatible with
//! proofs using `LayoutV0`.
use super::{ProofToHashes, ProvingTrie, TrieError};
use crate::{Decode, DispatchError, Encode};
use alloc::vec::Vec;
use codec::MaxEncodedLen;
use pezsp_trie::{
trie_types::{TrieDBBuilder, TrieDBMutBuilderV1},
LayoutV1, MemoryDB, RandomState, Trie, TrieMut,
};
/// A helper structure for building a basic base-16 merkle trie and creating compact proofs for that
/// trie. Proofs are created with latest bizinikiwi trie format (`LayoutV1`), and are not compatible
/// with proofs using `LayoutV0`.
pub struct BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
{
db: MemoryDB<Hashing>,
root: Hashing::Out,
_phantom: core::marker::PhantomData<(Key, Value)>,
}
impl<Hashing, Key, Value> BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
Key: Encode,
{
/// Create a compact merkle proof needed to prove all `keys` and their values are in the trie.
///
/// When verifying the proof created by this function, you must include all of the keys and
/// values of the proof, else the verifier will complain that extra nodes are provided in the
/// proof that are not needed.
pub fn create_multi_proof(&self, keys: &[Key]) -> Result<Vec<u8>, DispatchError> {
pezsp_trie::generate_trie_proof::<LayoutV1<Hashing>, _, _, _>(
&self.db,
self.root,
&keys.into_iter().map(|k| k.encode()).collect::<Vec<Vec<u8>>>(),
)
.map_err(|err| TrieError::from(*err).into())
.map(|structured_proof| structured_proof.encode())
}
}
impl<Hashing, Key, Value> ProvingTrie<Hashing, Key, Value> for BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
Key: Encode,
Value: Encode + Decode,
{
/// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs.
fn generate_for<I>(items: I) -> Result<Self, DispatchError>
where
I: IntoIterator<Item = (Key, Value)>,
{
let mut db = MemoryDB::with_hasher(RandomState::default());
let mut root = Default::default();
{
let mut trie = TrieDBMutBuilderV1::new(&mut db, &mut root).build();
for (key, value) in items.into_iter() {
key.using_encoded(|k| value.using_encoded(|v| trie.insert(k, v)))
.map_err(|_| "failed to insert into trie")?;
}
}
Ok(Self { db, root, _phantom: Default::default() })
}
/// Access the underlying trie root.
fn root(&self) -> &Hashing::Out {
&self.root
}
/// Query a value contained within the current trie. Returns `None` if the
/// nodes within the current `MemoryDB` are insufficient to query the item.
fn query(&self, key: &Key) -> Option<Value> {
let trie = TrieDBBuilder::new(&self.db, &self.root).build();
key.using_encoded(|s| trie.get(s))
.ok()?
.and_then(|raw| Value::decode(&mut &*raw).ok())
}
/// Create a compact merkle proof needed to prove a single key and its value are in the trie.
fn create_proof(&self, key: &Key) -> Result<Vec<u8>, DispatchError> {
pezsp_trie::generate_trie_proof::<LayoutV1<Hashing>, _, _, _>(
&self.db,
self.root,
&[key.encode()],
)
.map_err(|err| TrieError::from(*err).into())
.map(|structured_proof| structured_proof.encode())
}
/// Verify the existence of `key` and `value` in a given trie root and proof.
fn verify_proof(
root: &Hashing::Out,
proof: &[u8],
key: &Key,
value: &Value,
) -> Result<(), DispatchError> {
verify_proof::<Hashing, Key, Value>(root, proof, key, value)
}
}
impl<Hashing, Key, Value> ProofToHashes for BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
Hashing::Out: MaxEncodedLen,
{
// Our proof is just raw bytes.
type Proof = [u8];
// This base 16 trie uses a raw proof of `Vec<Vec<u8>`, where the length of the first `Vec`
// is the depth of the trie. We can use this to predict the number of hashes.
fn proof_to_hashes(proof: &[u8]) -> Result<u32, DispatchError> {
use codec::DecodeLength;
let depth =
<Vec<Vec<u8>> as DecodeLength>::len(proof).map_err(|_| TrieError::DecodeError)?;
Ok(depth as u32)
}
}
/// Verify the existence of `key` and `value` in a given trie root and proof.
pub fn verify_proof<Hashing, Key, Value>(
root: &Hashing::Out,
proof: &[u8],
key: &Key,
value: &Value,
) -> Result<(), DispatchError>
where
Hashing: pezsp_core::Hasher,
Key: Encode,
Value: Encode,
{
let structured_proof: Vec<Vec<u8>> =
Decode::decode(&mut &proof[..]).map_err(|_| TrieError::DecodeError)?;
pezsp_trie::verify_trie_proof::<LayoutV1<Hashing>, _, _, _>(
&root,
&structured_proof,
&[(key.encode(), Some(value.encode()))],
)
.map_err(|err| TrieError::from(err).into())
}
/// Verify the existence of multiple `items` in a given trie root and proof.
pub fn verify_multi_proof<Hashing, Key, Value>(
root: &Hashing::Out,
proof: &[u8],
items: &[(Key, Value)],
) -> Result<(), DispatchError>
where
Hashing: pezsp_core::Hasher,
Key: Encode,
Value: Encode,
{
let structured_proof: Vec<Vec<u8>> =
Decode::decode(&mut &proof[..]).map_err(|_| TrieError::DecodeError)?;
let items_encoded = items
.into_iter()
.map(|(key, value)| (key.encode(), Some(value.encode())))
.collect::<Vec<(Vec<u8>, Option<Vec<u8>>)>>();
pezsp_trie::verify_trie_proof::<LayoutV1<Hashing>, _, _, _>(
&root,
&structured_proof,
&items_encoded,
)
.map_err(|err| TrieError::from(err).into())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::BlakeTwo256;
use pezsp_core::H256;
use std::collections::BTreeMap;
// A trie which simulates a trie of accounts (u32) and balances (u128).
type BalanceTrie = BasicProvingTrie<BlakeTwo256, u32, u128>;
// The expected root hash for an empty trie.
fn empty_root() -> H256 {
pezsp_trie::empty_trie_root::<LayoutV1<BlakeTwo256>>()
}
fn create_balance_trie() -> BalanceTrie {
// Create a map of users and their balances.
let mut map = BTreeMap::<u32, u128>::new();
for i in 0..100u32 {
map.insert(i, i.into());
}
// Put items into the trie.
let balance_trie = BalanceTrie::generate_for(map).unwrap();
// Root is changed.
let root = *balance_trie.root();
assert!(root != empty_root());
// Assert valid keys are queryable.
assert_eq!(balance_trie.query(&6u32), Some(6u128));
assert_eq!(balance_trie.query(&9u32), Some(9u128));
assert_eq!(balance_trie.query(&69u32), Some(69u128));
// Invalid key returns none.
assert_eq!(balance_trie.query(&6969u32), None);
balance_trie
}
#[test]
fn empty_trie_works() {
let empty_trie = BalanceTrie::generate_for(Vec::new()).unwrap();
assert_eq!(*empty_trie.root(), empty_root());
}
#[test]
fn basic_end_to_end_single_value() {
let balance_trie = create_balance_trie();
let root = *balance_trie.root();
// Create a proof for a valid key.
let proof = balance_trie.create_proof(&6u32).unwrap();
// Assert key is provable, all other keys are invalid.
for i in 0..200u32 {
if i == 6 {
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&root, &proof, &i, &u128::from(i)),
Ok(())
);
// Wrong value is invalid.
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&root, &proof, &i, &u128::from(i + 1)),
Err(TrieError::RootMismatch.into())
);
} else {
assert!(
verify_proof::<BlakeTwo256, _, _>(&root, &proof, &i, &u128::from(i)).is_err()
);
}
}
}
#[test]
fn basic_end_to_end_multi() {
let balance_trie = create_balance_trie();
let root = *balance_trie.root();
// Create a proof for a valid and invalid key.
let proof = balance_trie.create_multi_proof(&[6u32, 9u32, 69u32]).unwrap();
let items = [(6u32, 6u128), (9u32, 9u128), (69u32, 69u128)];
assert_eq!(verify_multi_proof::<BlakeTwo256, _, _>(&root, &proof, &items), Ok(()));
}
#[test]
fn proof_fails_with_bad_data() {
let balance_trie = create_balance_trie();
let root = *balance_trie.root();
// Create a proof for a valid key.
let proof = balance_trie.create_proof(&6u32).unwrap();
// Correct data verifies successfully
assert_eq!(verify_proof::<BlakeTwo256, _, _>(&root, &proof, &6u32, &6u128), Ok(()));
// Fail to verify proof with wrong root
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&Default::default(), &proof, &6u32, &6u128),
Err(TrieError::RootMismatch.into())
);
// Crete a bad proof.
let bad_proof = balance_trie.create_proof(&99u32).unwrap();
// Fail to verify data with the wrong proof
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&root, &bad_proof, &6u32, &6u128),
Err(TrieError::ExtraneousHashReference.into())
);
}
#[test]
fn proof_to_hashes() {
let mut i: u32 = 1;
// Compute log base 16 and round up
let log16 = |x: u32| -> u32 {
let x_f64 = x as f64;
let log16_x = (x_f64.ln() / 16_f64.ln()).ceil();
log16_x as u32
};
while i < 10_000_000 {
let trie = BalanceTrie::generate_for((0..i).map(|i| (i, u128::from(i)))).unwrap();
let proof = trie.create_proof(&0).unwrap();
let hashes = BalanceTrie::proof_to_hashes(&proof).unwrap();
let log16 = log16(i).max(1);
assert_eq!(hashes, log16);
i = i * 10;
}
}
}
@@ -0,0 +1,288 @@
// 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.
//! Types for a base-2 merkle tree used for checking and generating proofs within the
//! runtime. The `binary-merkle-tree` crate exposes all of these same functionality (and more), but
//! this library is designed to work more easily with runtime native types, which simply need to
//! implement `Encode`/`Decode`.
use super::{ProofToHashes, ProvingTrie, TrieError};
use crate::{Decode, DispatchError, Encode};
use alloc::{collections::BTreeMap, vec::Vec};
use binary_merkle_tree::{merkle_proof, merkle_root, MerkleProof};
use codec::MaxEncodedLen;
/// A helper structure for building a basic base-2 merkle trie and creating compact proofs for that
/// trie.
pub struct BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
{
db: BTreeMap<Key, Value>,
root: Hashing::Out,
_phantom: core::marker::PhantomData<(Key, Value)>,
}
impl<Hashing, Key, Value> ProvingTrie<Hashing, Key, Value> for BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
Hashing::Out: Encode + Decode,
Key: Encode + Decode + Ord,
Value: Encode + Decode + Clone,
{
/// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs.
fn generate_for<I>(items: I) -> Result<Self, DispatchError>
where
I: IntoIterator<Item = (Key, Value)>,
{
let mut db = BTreeMap::default();
for (key, value) in items.into_iter() {
db.insert(key, value);
}
let root = merkle_root::<Hashing, _>(db.iter().map(|item| item.encode()));
Ok(Self { db, root, _phantom: Default::default() })
}
/// Access the underlying trie root.
fn root(&self) -> &Hashing::Out {
&self.root
}
/// Query a value contained within the current trie. Returns `None` if the
/// nodes within the current `db` are insufficient to query the item.
fn query(&self, key: &Key) -> Option<Value> {
self.db.get(&key).cloned()
}
/// Create a compact merkle proof needed to prove a single key and its value are in the trie.
/// Returns an error if the nodes within the current `db` are insufficient to create a proof.
fn create_proof(&self, key: &Key) -> Result<Vec<u8>, DispatchError> {
let mut encoded = Vec::with_capacity(self.db.len());
let mut found_index = None;
// Find the index of our key, and encode the (key, value) pair.
for (i, (k, v)) in self.db.iter().enumerate() {
// If we found the key we are looking for, save it.
if k == key {
found_index = Some(i);
}
encoded.push((k, v).encode());
}
let index = found_index.ok_or(TrieError::IncompleteDatabase)?;
let proof = merkle_proof::<Hashing, Vec<Vec<u8>>, Vec<u8>>(encoded, index as u32);
Ok(proof.encode())
}
/// Verify the existence of `key` and `value` in a given trie root and proof.
fn verify_proof(
root: &Hashing::Out,
proof: &[u8],
key: &Key,
value: &Value,
) -> Result<(), DispatchError> {
verify_proof::<Hashing, Key, Value>(root, proof, key, value)
}
}
impl<Hashing, Key, Value> ProofToHashes for BasicProvingTrie<Hashing, Key, Value>
where
Hashing: pezsp_core::Hasher,
Hashing::Out: MaxEncodedLen + Decode,
Key: Decode,
Value: Decode,
{
// Our proof is just raw bytes.
type Proof = [u8];
// This base 2 merkle trie includes a `proof` field which is a `Vec<Hash>`.
// The length of this vector tells us the depth of the proof, and how many
// hashes we need to calculate.
fn proof_to_hashes(proof: &[u8]) -> Result<u32, DispatchError> {
let decoded_proof: MerkleProof<Hashing::Out, Vec<u8>> =
Decode::decode(&mut &proof[..]).map_err(|_| TrieError::IncompleteProof)?;
let depth = decoded_proof.proof.len();
Ok(depth as u32)
}
}
/// Verify the existence of `key` and `value` in a given trie root and proof.
pub fn verify_proof<Hashing, Key, Value>(
root: &Hashing::Out,
proof: &[u8],
key: &Key,
value: &Value,
) -> Result<(), DispatchError>
where
Hashing: pezsp_core::Hasher,
Hashing::Out: Decode,
Key: Encode + Decode,
Value: Encode + Decode,
{
let decoded_proof: MerkleProof<Hashing::Out, Vec<u8>> =
Decode::decode(&mut &proof[..]).map_err(|_| TrieError::IncompleteProof)?;
if *root != decoded_proof.root {
return Err(TrieError::RootMismatch.into());
}
if (key, value).encode() != decoded_proof.leaf {
return Err(TrieError::ValueMismatch.into());
}
if binary_merkle_tree::verify_proof::<Hashing, _, _>(
&decoded_proof.root,
decoded_proof.proof,
decoded_proof.number_of_leaves,
decoded_proof.leaf_index,
&decoded_proof.leaf,
) {
Ok(())
} else {
Err(TrieError::IncompleteProof.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::BlakeTwo256;
use pezsp_core::H256;
use std::collections::BTreeMap;
// A trie which simulates a trie of accounts (u32) and balances (u128).
type BalanceTrie = BasicProvingTrie<BlakeTwo256, u32, u128>;
// The expected root hash for an empty trie.
fn empty_root() -> H256 {
let tree = BalanceTrie::generate_for(Vec::new()).unwrap();
*tree.root()
}
fn create_balance_trie() -> BalanceTrie {
// Create a map of users and their balances.
let mut map = BTreeMap::<u32, u128>::new();
for i in 0..100u32 {
map.insert(i, i.into());
}
// Put items into the trie.
let balance_trie = BalanceTrie::generate_for(map).unwrap();
// Root is changed.
let root = *balance_trie.root();
assert!(root != empty_root());
// Assert valid keys are queryable.
assert_eq!(balance_trie.query(&6u32), Some(6u128));
assert_eq!(balance_trie.query(&9u32), Some(9u128));
assert_eq!(balance_trie.query(&69u32), Some(69u128));
balance_trie
}
#[test]
fn empty_trie_works() {
let empty_trie = BalanceTrie::generate_for(Vec::new()).unwrap();
assert_eq!(*empty_trie.root(), empty_root());
}
#[test]
fn basic_end_to_end_single_value() {
let balance_trie = create_balance_trie();
let root = *balance_trie.root();
// Create a proof for a valid key.
let proof = balance_trie.create_proof(&6u32).unwrap();
// Assert key is provable, all other keys are invalid.
for i in 0..200u32 {
if i == 6 {
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&root, &proof, &i, &u128::from(i)),
Ok(())
);
// Wrong value is invalid.
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&root, &proof, &i, &u128::from(i + 1)),
Err(TrieError::ValueMismatch.into())
);
} else {
assert!(
verify_proof::<BlakeTwo256, _, _>(&root, &proof, &i, &u128::from(i)).is_err()
);
}
}
}
#[test]
fn proof_fails_with_bad_data() {
let balance_trie = create_balance_trie();
let root = *balance_trie.root();
// Create a proof for a valid key.
let proof = balance_trie.create_proof(&6u32).unwrap();
// Correct data verifies successfully
assert_eq!(verify_proof::<BlakeTwo256, _, _>(&root, &proof, &6u32, &6u128), Ok(()));
// Fail to verify proof with wrong root
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&Default::default(), &proof, &6u32, &6u128),
Err(TrieError::RootMismatch.into())
);
// Fail to verify proof with wrong data
assert_eq!(
verify_proof::<BlakeTwo256, _, _>(&root, &[], &6u32, &6u128),
Err(TrieError::IncompleteProof.into())
);
}
// We make assumptions about the structure of the merkle proof in order to provide the
// `proof_to_hashes` function. This test keeps those assumptions checked.
#[test]
fn assert_structure_of_merkle_proof() {
let balance_trie = create_balance_trie();
let root = *balance_trie.root();
// Create a proof for a valid key.
let proof = balance_trie.create_proof(&6u32).unwrap();
let decoded_proof: MerkleProof<H256, Vec<u8>> = Decode::decode(&mut &proof[..]).unwrap();
let constructed_proof = MerkleProof::<H256, Vec<u8>> {
root,
proof: decoded_proof.proof.clone(),
number_of_leaves: 100,
leaf_index: 6,
leaf: (6u32, 6u128).encode(),
};
assert_eq!(constructed_proof, decoded_proof);
}
#[test]
fn proof_to_hashes() {
let mut i: u32 = 1;
while i < 10_000_000 {
let trie = BalanceTrie::generate_for((0..i).map(|i| (i, u128::from(i)))).unwrap();
let proof = trie.create_proof(&0).unwrap();
let hashes = BalanceTrie::proof_to_hashes(&proof).unwrap();
let log2 = (i as f64).log2().ceil() as u32;
assert_eq!(hashes, log2);
i = i * 10;
}
}
}
@@ -0,0 +1,198 @@
// 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.
//! Types for merkle tries compatible with the runtime.
pub mod base16;
pub mod base2;
use crate::{Decode, DecodeWithMemTracking, DispatchError, Encode, MaxEncodedLen, TypeInfo};
#[cfg(feature = "serde")]
use crate::{Deserialize, Serialize};
use alloc::vec::Vec;
use pezsp_trie::{trie_types::TrieError as SpTrieError, VerifyError};
/// A runtime friendly error type for tries.
#[derive(
Eq,
PartialEq,
Clone,
Copy,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TrieError {
/* From TrieError */
/// Attempted to create a trie with a state root not in the DB.
InvalidStateRoot,
/// Trie item not found in the database,
IncompleteDatabase,
/// A value was found in the trie with a nibble key that was not byte-aligned.
ValueAtIncompleteKey,
/// Corrupt Trie item.
DecoderError,
/// Hash is not value.
InvalidHash,
/* From VerifyError */
/// The statement being verified contains multiple key-value pairs with the same key.
DuplicateKey,
/// The proof contains at least one extraneous node.
ExtraneousNode,
/// The proof contains at least one extraneous value which should have been omitted from the
/// proof.
ExtraneousValue,
/// The proof contains at least one extraneous hash reference the should have been omitted.
ExtraneousHashReference,
/// The proof contains an invalid child reference that exceeds the hash length.
InvalidChildReference,
/// The proof indicates that an expected value was not found in the trie.
ValueMismatch,
/// The proof is missing trie nodes required to verify.
IncompleteProof,
/// The root hash computed from the proof is incorrect.
RootMismatch,
/// One of the proof nodes could not be decoded.
DecodeError,
}
impl<T> From<SpTrieError<T>> for TrieError {
fn from(error: SpTrieError<T>) -> Self {
match error {
SpTrieError::InvalidStateRoot(..) => Self::InvalidStateRoot,
SpTrieError::IncompleteDatabase(..) => Self::IncompleteDatabase,
SpTrieError::ValueAtIncompleteKey(..) => Self::ValueAtIncompleteKey,
SpTrieError::DecoderError(..) => Self::DecoderError,
SpTrieError::InvalidHash(..) => Self::InvalidHash,
}
}
}
impl<T, U> From<VerifyError<T, U>> for TrieError {
fn from(error: VerifyError<T, U>) -> Self {
match error {
VerifyError::DuplicateKey(..) => Self::DuplicateKey,
VerifyError::ExtraneousNode => Self::ExtraneousNode,
VerifyError::ExtraneousValue(..) => Self::ExtraneousValue,
VerifyError::ExtraneousHashReference(..) => Self::ExtraneousHashReference,
VerifyError::InvalidChildReference(..) => Self::InvalidChildReference,
VerifyError::ValueMismatch(..) => Self::ValueMismatch,
VerifyError::IncompleteProof => Self::IncompleteProof,
VerifyError::RootMismatch(..) => Self::RootMismatch,
VerifyError::DecodeError(..) => Self::DecodeError,
}
}
}
impl From<TrieError> for &'static str {
fn from(e: TrieError) -> &'static str {
match e {
TrieError::InvalidStateRoot => "The state root is not in the database.",
TrieError::IncompleteDatabase => "A trie item was not found in the database.",
TrieError::ValueAtIncompleteKey =>
"A value was found with a key that is not byte-aligned.",
TrieError::DecoderError => "A corrupt trie item was encountered.",
TrieError::InvalidHash => "The hash does not match the expected value.",
TrieError::DuplicateKey => "The proof contains duplicate keys.",
TrieError::ExtraneousNode => "The proof contains extraneous nodes.",
TrieError::ExtraneousValue => "The proof contains extraneous values.",
TrieError::ExtraneousHashReference => "The proof contains extraneous hash references.",
TrieError::InvalidChildReference => "The proof contains an invalid child reference.",
TrieError::ValueMismatch => "The proof indicates a value mismatch.",
TrieError::IncompleteProof => "The proof is incomplete.",
TrieError::RootMismatch => "The root hash computed from the proof is incorrect.",
TrieError::DecodeError => "One of the proof nodes could not be decoded.",
}
}
}
/// An interface for creating, interacting with, and creating proofs in a merkle trie.
pub trait ProvingTrie<Hashing, Key, Value>
where
Self: Sized,
Hashing: pezsp_core::Hasher,
{
/// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs.
fn generate_for<I>(items: I) -> Result<Self, DispatchError>
where
I: IntoIterator<Item = (Key, Value)>;
/// Access the underlying trie root.
fn root(&self) -> &Hashing::Out;
/// Query a value contained within the current trie. Returns `None` if the
/// the value does not exist in the trie.
fn query(&self, key: &Key) -> Option<Value>;
/// Create a proof that can be used to verify a key and its value are in the trie.
fn create_proof(&self, key: &Key) -> Result<Vec<u8>, DispatchError>;
/// Verify the existence of `key` and `value` in a given trie root and proof.
fn verify_proof(
root: &Hashing::Out,
proof: &[u8],
key: &Key,
value: &Value,
) -> Result<(), DispatchError>;
}
/// This trait is one strategy that can be used to benchmark a trie proof verification for the
/// runtime. This strategy assumes that the majority complexity of verifying a merkle proof comes
/// from computing hashes to recreate the merkle root. This trait converts the the proof, some
/// bytes, to the number of hashes we expect to execute to verify that proof.
pub trait ProofToHashes {
/// The Proof type we will use to determine the number of hashes.
type Proof: ?Sized;
/// This function returns the number of hashes we expect to calculate based on the
/// size of the proof. This is used for benchmarking, so for worst case scenario, we should
/// round up.
///
/// The major complexity of doing a `verify_proof` is computing the hashes needed
/// to calculate the merkle root. For tries, it should be easy to predict the depth
/// of the trie (which is equivalent to the hashes), by looking at the length of the proof.
fn proof_to_hashes(proof: &Self::Proof) -> Result<u32, DispatchError>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::BlakeTwo256;
// A trie which simulates a trie of accounts (u32) and balances (u128).
type BalanceTrie2 = base2::BasicProvingTrie<BlakeTwo256, u32, u128>;
type BalanceTrie16 = base16::BasicProvingTrie<BlakeTwo256, u32, u128>;
#[test]
fn basic_api_usage_base_2() {
let balance_trie = BalanceTrie2::generate_for((0..100u32).map(|i| (i, i.into()))).unwrap();
let root = *balance_trie.root();
assert_eq!(balance_trie.query(&69), Some(69));
assert_eq!(balance_trie.query(&6969), None);
let proof = balance_trie.create_proof(&69u32).unwrap();
assert_eq!(BalanceTrie2::verify_proof(&root, &proof, &69u32, &69u128), Ok(()));
}
#[test]
fn basic_api_usage_base_16() {
let balance_trie = BalanceTrie16::generate_for((0..100u32).map(|i| (i, i.into()))).unwrap();
let root = *balance_trie.root();
assert_eq!(balance_trie.query(&69), Some(69));
assert_eq!(balance_trie.query(&6969), None);
let proof = balance_trie.create_proof(&69u32).unwrap();
assert_eq!(BalanceTrie16::verify_proof(&root, &proof, &69u32, &69u128), Ok(()));
}
}
@@ -0,0 +1,101 @@
// 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.
//! A logger that can be used to log from the runtime.
//!
//! See [`RuntimeLogger`] for more docs.
/// Runtime logger implementation - `log` crate backend.
///
/// The logger should be initialized if you want to display
/// logs inside the runtime that is not necessarily running natively.
pub struct RuntimeLogger;
impl RuntimeLogger {
/// Initialize the logger.
///
/// This is a no-op when running natively (`std`).
#[cfg(feature = "std")]
pub fn init() {}
/// Initialize the logger.
///
/// This is a no-op when running natively (`std`).
#[cfg(not(feature = "std"))]
pub fn init() {
static LOGGER: RuntimeLogger = RuntimeLogger;
let _ = log::set_logger(&LOGGER);
// Use the same max log level as used by the host.
log::set_max_level(pezsp_io::logging::max_level().into());
}
}
impl log::Log for RuntimeLogger {
fn enabled(&self, _: &log::Metadata) -> bool {
// The final filtering is done by the host. This is not perfect, as we would still call into
// the host for log lines that will be thrown away.
true
}
fn log(&self, record: &log::Record) {
use core::fmt::Write;
let mut msg = alloc::string::String::default();
let _ = ::core::write!(&mut msg, "{}", record.args());
pezsp_io::logging::log(record.level().into(), record.target(), msg.as_bytes());
}
fn flush(&self) {}
}
#[cfg(test)]
mod tests {
use pezsp_api::ProvideRuntimeApi;
use std::env;
use bizinikiwi_test_runtime_client::{
runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
};
#[test]
fn ensure_runtime_logger_works() {
if env::var("RUN_TEST").is_ok() {
pezsp_tracing::try_init_simple();
let client = TestClientBuilder::new().build();
let runtime_api = client.runtime_api();
runtime_api
.do_trace_log(client.chain_info().genesis_hash)
.expect("Logging should not fail");
} else {
for (level, should_print) in &[("test=trace", true), ("info", false)] {
let executable = std::env::current_exe().unwrap();
let output = std::process::Command::new(executable)
.env("RUN_TEST", "1")
.env("RUST_LOG", level)
.args(&["--nocapture", "ensure_runtime_logger_works"])
.output()
.unwrap();
let output = String::from_utf8(output.stderr).unwrap();
assert!(output.contains("Hey I'm runtime") == *should_print);
assert!(output.contains("THIS IS TRACING") == *should_print);
assert!(output.contains("Hey, I'm tracing") == *should_print);
}
}
}
}
@@ -0,0 +1,325 @@
// 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.
//! Testing utilities.
use crate::{
codec::{Codec, Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen},
generic::{self, LazyBlock, UncheckedExtrinsic},
scale_info::TypeInfo,
traits::{self, BlakeTwo256, Dispatchable, LazyExtrinsic, OpaqueKeys},
DispatchResultWithInfo, KeyTypeId, OpaqueExtrinsic,
};
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
use pezsp_core::crypto::{key_types, ByteArray, CryptoType, Dummy};
pub use pezsp_core::{sr25519, H256};
use std::{cell::RefCell, fmt::Debug};
/// A dummy type which can be used instead of regular cryptographic primitives.
///
/// 1. Wraps a `u64` `AccountId` and is able to `IdentifyAccount`.
/// 2. Can be converted to any `Public` key.
/// 3. Implements `RuntimeAppPublic` so it can be used instead of regular application-specific
/// crypto.
#[derive(
Default,
PartialEq,
Eq,
Clone,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
Hash,
Serialize,
Deserialize,
PartialOrd,
Ord,
MaxEncodedLen,
TypeInfo,
)]
pub struct UintAuthorityId(pub u64);
impl From<u64> for UintAuthorityId {
fn from(id: u64) -> Self {
UintAuthorityId(id)
}
}
impl From<UintAuthorityId> for u64 {
fn from(id: UintAuthorityId) -> u64 {
id.0
}
}
impl UintAuthorityId {
/// Convert this authority ID into a public key.
pub fn to_public_key<T: ByteArray>(&self) -> T {
let mut bytes = [0u8; 32];
bytes[0..8].copy_from_slice(&self.0.to_le_bytes());
T::from_slice(&bytes).unwrap()
}
/// Set the list of keys returned by the runtime call for all keys of that type.
pub fn set_all_keys<T: Into<UintAuthorityId>>(keys: impl IntoIterator<Item = T>) {
ALL_KEYS.with(|l| *l.borrow_mut() = keys.into_iter().map(Into::into).collect())
}
}
impl CryptoType for UintAuthorityId {
type Pair = Dummy;
}
impl AsRef<[u8]> for UintAuthorityId {
fn as_ref(&self) -> &[u8] {
// Unsafe, i know, but it's test code and it's just there because it's really convenient to
// keep `UintAuthorityId` as a u64 under the hood.
unsafe {
std::slice::from_raw_parts(
&self.0 as *const u64 as *const _,
std::mem::size_of::<u64>(),
)
}
}
}
thread_local! {
/// A list of all UintAuthorityId keys returned to the runtime.
static ALL_KEYS: RefCell<Vec<UintAuthorityId>> = RefCell::new(vec![]);
}
impl pezsp_application_crypto::RuntimeAppPublic for UintAuthorityId {
const ID: KeyTypeId = key_types::DUMMY;
type Signature = TestSignature;
type ProofOfPossession = TestSignature;
fn all() -> Vec<Self> {
ALL_KEYS.with(|l| l.borrow().clone())
}
fn generate_pair(_: Option<Vec<u8>>) -> Self {
use rand::RngCore;
UintAuthorityId(rand::thread_rng().next_u64())
}
fn sign<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature> {
Some(TestSignature(self.0, msg.as_ref().to_vec()))
}
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
traits::Verify::verify(signature, msg.as_ref(), &self.0)
}
fn generate_proof_of_possession(&mut self, _owner: &[u8]) -> Option<Self::Signature> {
None
}
fn verify_proof_of_possession(&self, _owner: &[u8], _pop: &Self::Signature) -> bool {
false
}
fn to_raw_vec(&self) -> Vec<u8> {
AsRef::<[u8]>::as_ref(self).to_vec()
}
}
impl OpaqueKeys for UintAuthorityId {
type KeyTypeIdProviders = ();
fn key_ids() -> &'static [KeyTypeId] {
&[key_types::DUMMY]
}
fn get_raw(&self, _: KeyTypeId) -> &[u8] {
self.as_ref()
}
fn get<T: Decode>(&self, _: KeyTypeId) -> Option<T> {
self.using_encoded(|mut x| T::decode(&mut x)).ok()
}
}
impl traits::IdentifyAccount for UintAuthorityId {
type AccountId = u64;
fn into_account(self) -> Self::AccountId {
self.0
}
}
impl traits::Verify for UintAuthorityId {
type Signer = Self;
fn verify<L: traits::Lazy<[u8]>>(
&self,
_msg: L,
signer: &<Self::Signer as traits::IdentifyAccount>::AccountId,
) -> bool {
self.0 == *signer
}
}
/// A dummy signature type, to match `UintAuthorityId`.
#[derive(
Eq,
PartialEq,
Clone,
Debug,
Hash,
Serialize,
Deserialize,
Encode,
Decode,
DecodeWithMemTracking,
TypeInfo,
)]
pub struct TestSignature(pub u64, pub Vec<u8>);
impl traits::Verify for TestSignature {
type Signer = UintAuthorityId;
fn verify<L: traits::Lazy<[u8]>>(&self, mut msg: L, signer: &u64) -> bool {
signer == &self.0 && msg.get() == &self.1[..]
}
}
/// Digest item
pub type DigestItem = generic::DigestItem;
/// Header Digest
pub type Digest = generic::Digest;
/// Block Header
pub type Header = generic::Header<u64, BlakeTwo256>;
impl Header {
/// A new header with the given number and default hash for all other fields.
pub fn new_from_number(number: <Self as traits::Header>::Number) -> Self {
Self {
number,
extrinsics_root: Default::default(),
state_root: Default::default(),
parent_hash: Default::default(),
digest: Default::default(),
}
}
}
/// Testing block
#[derive(
PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo,
)]
pub struct Block<Xt> {
/// Block header
pub header: Header,
/// List of extrinsics
pub extrinsics: Vec<Xt>,
}
impl<Xt> traits::HeaderProvider for Block<Xt> {
type HeaderT = Header;
}
impl<Xt: Into<OpaqueExtrinsic>> From<Block<Xt>> for LazyBlock<Header, Xt> {
fn from(block: Block<Xt>) -> Self {
LazyBlock::new(block.header, block.extrinsics)
}
}
impl<Xt> EncodeLike<LazyBlock<Header, Xt>> for Block<Xt>
where
Block<Xt>: Encode,
LazyBlock<Header, Xt>: Encode,
{
}
impl<Xt> EncodeLike<Block<Xt>> for LazyBlock<Header, Xt>
where
Block<Xt>: Encode,
LazyBlock<Header, Xt>: Encode,
{
}
impl<
Xt: 'static
+ Codec
+ DecodeWithMemTracking
+ Sized
+ Send
+ Sync
+ Serialize
+ Clone
+ Eq
+ Debug
+ traits::ExtrinsicLike
+ Into<OpaqueExtrinsic>
+ LazyExtrinsic,
> traits::Block for Block<Xt>
{
type Extrinsic = Xt;
type Header = Header;
type Hash = <Header as traits::Header>::Hash;
type LazyBlock = LazyBlock<Header, Xt>;
fn header(&self) -> &Self::Header {
&self.header
}
fn extrinsics(&self) -> &[Self::Extrinsic] {
&self.extrinsics[..]
}
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
(self.header, self.extrinsics)
}
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
Block { header, extrinsics }
}
}
impl<'a, Xt> Deserialize<'a> for Block<Xt>
where
Block<Xt>: Decode,
{
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
let r = <Vec<u8>>::deserialize(de)?;
Decode::decode(&mut &r[..])
.map_err(|e| DeError::custom(format!("Invalid value passed into decode: {}", e)))
}
}
/// Extrinsic type with `u64` accounts and mocked signatures, used in testing.
pub type TestXt<Call, Extra> = UncheckedExtrinsic<u64, Call, (), Extra>;
/// Wrapper over a `u64` that can be used as a `RuntimeCall`.
#[derive(PartialEq, Eq, Debug, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
pub struct MockCallU64(pub u64);
impl Dispatchable for MockCallU64 {
type RuntimeOrigin = u64;
type Config = ();
type Info = ();
type PostInfo = ();
fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo<Self::PostInfo> {
Ok(())
}
}
impl From<u64> for MockCallU64 {
fn from(value: u64) -> Self {
Self(value)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,131 @@
// 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 [AsTransactionExtension] adapter struct for adapting [SignedExtension]s to
//! [TransactionExtension]s.
#![allow(deprecated)]
use scale_info::TypeInfo;
use pezsp_core::RuntimeDebug;
use crate::{
traits::{AsSystemOriginSigner, SignedExtension, ValidateResult},
transaction_validity::{InvalidTransaction, TransactionSource},
};
use super::*;
/// Adapter to use a `SignedExtension` in the place of a `TransactionExtension`.
#[derive(TypeInfo, Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, RuntimeDebug)]
#[deprecated = "Convert your SignedExtension to a TransactionExtension."]
pub struct AsTransactionExtension<SE: SignedExtension>(pub SE);
impl<SE: SignedExtension + Default> Default for AsTransactionExtension<SE> {
fn default() -> Self {
Self(SE::default())
}
}
impl<SE: SignedExtension> From<SE> for AsTransactionExtension<SE> {
fn from(value: SE) -> Self {
Self(value)
}
}
impl<SE: SignedExtension> TransactionExtension<SE::Call> for AsTransactionExtension<SE>
where
<SE::Call as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<SE::AccountId> + Clone,
{
const IDENTIFIER: &'static str = SE::IDENTIFIER;
type Implicit = SE::AdditionalSigned;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
self.0.additional_signed()
}
fn metadata() -> Vec<TransactionExtensionMetadata> {
SE::metadata()
}
fn weight(&self, _call: &SE::Call) -> Weight {
Weight::zero()
}
type Val = ();
type Pre = SE::Pre;
fn validate(
&self,
origin: <SE::Call as Dispatchable>::RuntimeOrigin,
call: &SE::Call,
info: &DispatchInfoOf<SE::Call>,
len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Encode,
_source: TransactionSource,
) -> ValidateResult<Self::Val, SE::Call> {
let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?;
let r = self.0.validate(who, call, info, len)?;
Ok((r, (), origin))
}
fn prepare(
self,
_: (),
origin: &<SE::Call as Dispatchable>::RuntimeOrigin,
call: &SE::Call,
info: &DispatchInfoOf<SE::Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?;
self.0.pre_dispatch(who, call, info, len)
}
fn post_dispatch_details(
pre: Self::Pre,
info: &DispatchInfoOf<SE::Call>,
post_info: &PostDispatchInfoOf<SE::Call>,
len: usize,
result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
SE::post_dispatch(Some(pre), info, post_info, len, result)?;
Ok(Weight::zero())
}
fn bare_validate(
call: &SE::Call,
info: &DispatchInfoOf<SE::Call>,
len: usize,
) -> TransactionValidity {
SE::validate_unsigned(call, info, len)
}
fn bare_validate_and_prepare(
call: &SE::Call,
info: &DispatchInfoOf<SE::Call>,
len: usize,
) -> Result<(), TransactionValidityError> {
SE::pre_dispatch_unsigned(call, info, len)
}
fn bare_post_dispatch(
info: &DispatchInfoOf<SE::Call>,
post_info: &mut PostDispatchInfoOf<SE::Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
SE::post_dispatch(None, info, post_info, len, result)
}
}
@@ -0,0 +1,185 @@
// 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 [DispatchTransaction] trait.
use crate::{
generic::ExtensionVersion,
traits::AsTransactionAuthorizedOrigin,
transaction_validity::{InvalidTransaction, TransactionSource},
};
use super::*;
/// Single-function utility trait with a blanket impl over [`TransactionExtension`] in order to
/// provide transaction dispatching functionality. We avoid implementing this directly on the trait
/// since we never want it to be overriden by the trait implementation.
pub trait DispatchTransaction<Call: Dispatchable> {
/// The origin type of the transaction.
type Origin;
/// The info type.
type Info;
/// The resultant type.
type Result;
/// The `Val` of the extension.
type Val;
/// The `Pre` of the extension.
type Pre;
/// Just validate a transaction.
///
/// The is basically the same as [validate](TransactionExtension::validate), except that there
/// is no need to supply the bond data.
fn validate_only(
&self,
origin: Self::Origin,
call: &Call,
info: &Self::Info,
len: usize,
source: TransactionSource,
extension_version: ExtensionVersion,
) -> Result<(ValidTransaction, Self::Val, Self::Origin), TransactionValidityError>;
/// Validate and prepare a transaction, ready for dispatch.
fn validate_and_prepare(
self,
origin: Self::Origin,
call: &Call,
info: &Self::Info,
len: usize,
extension_version: ExtensionVersion,
) -> Result<(Self::Pre, Self::Origin), TransactionValidityError>;
/// Dispatch a transaction with the given base origin and call.
fn dispatch_transaction(
self,
origin: Self::Origin,
call: Call,
info: &Self::Info,
len: usize,
extension_version: ExtensionVersion,
) -> Self::Result;
/// Do everything which would be done in a [dispatch_transaction](Self::dispatch_transaction),
/// but instead of executing the call, execute `substitute` instead. Since this doesn't actually
/// dispatch the call, it doesn't need to consume it and so `call` can be passed as a reference.
fn test_run(
self,
origin: Self::Origin,
call: &Call,
info: &Self::Info,
len: usize,
extension_version: ExtensionVersion,
substitute: impl FnOnce(
Self::Origin,
) -> crate::DispatchResultWithInfo<<Call as Dispatchable>::PostInfo>,
) -> Self::Result;
}
impl<T: TransactionExtension<Call>, Call: Dispatchable + Encode> DispatchTransaction<Call> for T
where
<Call as Dispatchable>::RuntimeOrigin: AsTransactionAuthorizedOrigin,
{
type Origin = <Call as Dispatchable>::RuntimeOrigin;
type Info = DispatchInfoOf<Call>;
type Result = crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Call>>;
type Val = T::Val;
type Pre = T::Pre;
fn validate_only(
&self,
origin: Self::Origin,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
source: TransactionSource,
extension_version: ExtensionVersion,
) -> Result<(ValidTransaction, T::Val, Self::Origin), TransactionValidityError> {
match self.validate(
origin,
call,
info,
len,
self.implicit()?,
&TxBaseImplication((extension_version, call)),
source,
) {
// After validation, some origin must have been authorized.
Ok((_, _, origin)) if !origin.is_transaction_authorized() =>
Err(InvalidTransaction::UnknownOrigin.into()),
res => res,
}
}
fn validate_and_prepare(
self,
origin: Self::Origin,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
extension_version: ExtensionVersion,
) -> Result<(T::Pre, Self::Origin), TransactionValidityError> {
let (_, val, origin) = self.validate_only(
origin,
call,
info,
len,
TransactionSource::InBlock,
extension_version,
)?;
let pre = self.prepare(val, &origin, &call, info, len)?;
Ok((pre, origin))
}
fn dispatch_transaction(
self,
origin: <Call as Dispatchable>::RuntimeOrigin,
call: Call,
info: &DispatchInfoOf<Call>,
len: usize,
extension_version: ExtensionVersion,
) -> Self::Result {
let (pre, origin) =
self.validate_and_prepare(origin, &call, info, len, extension_version)?;
let mut res = call.dispatch(origin);
let pd_res = res.map(|_| ()).map_err(|e| e.error);
let post_info = match &mut res {
Ok(info) => info,
Err(err) => &mut err.post_info,
};
post_info.set_extension_weight(info);
T::post_dispatch(pre, info, post_info, len, &pd_res)?;
Ok(res)
}
fn test_run(
self,
origin: Self::Origin,
call: &Call,
info: &Self::Info,
len: usize,
extension_version: ExtensionVersion,
substitute: impl FnOnce(
Self::Origin,
) -> crate::DispatchResultWithInfo<<Call as Dispatchable>::PostInfo>,
) -> Self::Result {
let (pre, origin) =
self.validate_and_prepare(origin, &call, info, len, extension_version)?;
let mut res = substitute(origin);
let pd_res = res.map(|_| ()).map_err(|e| e.error);
let post_info = match &mut res {
Ok(info) => info,
Err(err) => &mut err.post_info,
};
post_info.set_extension_weight(info);
T::post_dispatch(pre, info, post_info, len, &pd_res)?;
Ok(res)
}
}
@@ -0,0 +1,870 @@
// 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 transaction extension trait.
use crate::{
scale_info::{MetaType, StaticTypeInfo},
transaction_validity::{
TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction,
},
DispatchResult,
};
use alloc::vec::Vec;
use codec::{Codec, Decode, DecodeWithMemTracking, Encode};
use core::fmt::Debug;
#[doc(hidden)]
pub use core::marker::PhantomData;
use impl_trait_for_tuples::impl_for_tuples;
use pezsp_weights::Weight;
use tuplex::{PopFront, PushBack};
use super::{
DispatchInfoOf, DispatchOriginOf, Dispatchable, ExtensionPostDispatchWeightHandler,
PostDispatchInfoOf, RefundWeight,
};
mod as_transaction_extension;
mod dispatch_transaction;
#[allow(deprecated)]
pub use as_transaction_extension::AsTransactionExtension;
pub use dispatch_transaction::DispatchTransaction;
/// Provides `Sealed` trait.
mod private {
/// Special trait that prevents the implementation of some traits outside of this crate.
pub trait Sealed {}
}
/// The base implication in a transaction.
///
/// This struct is used to represent the base implication in the transaction, that is
/// the implication not part of any transaction extensions. It usually comprises of the call and
/// the transaction extension version.
///
/// The concept of implication in the transaction extension pipeline is explained in the trait
/// documentation: [`TransactionExtension`].
#[derive(Encode)]
pub struct TxBaseImplication<T>(pub T);
impl<T: Encode> Implication for TxBaseImplication<T> {
fn parts(&self) -> ImplicationParts<&impl Encode, &impl Encode, &impl Encode> {
ImplicationParts { base: self, explicit: &(), implicit: &() }
}
}
impl<T> private::Sealed for TxBaseImplication<T> {}
/// The implication in a transaction.
///
/// The concept of implication in the transaction extension pipeline is explained in the trait
/// documentation: [`TransactionExtension`].
#[derive(Encode)]
pub struct ImplicationParts<Base, Explicit, Implicit> {
/// The base implication, that is implication not part of any transaction extension, usually
/// the call and the transaction extension version.
pub base: Base,
/// The explicit implication in transaction extensions.
pub explicit: Explicit,
/// The implicit implication in transaction extensions.
pub implicit: Implicit,
}
impl<Base: Encode, Explicit: Encode, Implicit: Encode> Implication
for ImplicationParts<Base, Explicit, Implicit>
{
fn parts(&self) -> ImplicationParts<&impl Encode, &impl Encode, &impl Encode> {
ImplicationParts { base: &self.base, explicit: &self.explicit, implicit: &self.implicit }
}
}
impl<Base, Explicit, Implicit> private::Sealed for ImplicationParts<Base, Explicit, Implicit> {}
/// Interface of implications in the transaction extension pipeline.
///
/// Implications can be encoded, this is useful for checking signature on the implications.
/// Implications can be split into parts, this allow to destructure and restructure the
/// implications, this is useful for nested pipeline.
///
/// This trait is sealed, consider using [`TxBaseImplication`] and [`ImplicationParts`]
/// implementations.
///
/// The concept of implication in the transaction extension pipeline is explained in the trait
/// documentation: [`TransactionExtension`].
pub trait Implication: Encode + private::Sealed {
/// Destructure the implication into its parts.
fn parts(&self) -> ImplicationParts<&impl Encode, &impl Encode, &impl Encode>;
}
/// Shortcut for the result value of the `validate` function.
pub type ValidateResult<Val, Call> =
Result<(ValidTransaction, Val, DispatchOriginOf<Call>), TransactionValidityError>;
/// Means by which a transaction may be extended. This type embodies both the data and the logic
/// that should be additionally associated with the transaction. It should be plain old data.
///
/// The simplest transaction extension would be the Unit type (and empty pipeline) `()`. This
/// executes no additional logic and implies a dispatch of the transaction's call using the
/// inherited origin (either `None` or `Signed`, depending on whether this is a signed or general
/// transaction).
///
/// Transaction extensions are capable of altering certain associated semantics:
///
/// - They may define the origin with which the transaction's call should be dispatched.
/// - They may define various parameters used by the transaction queue to determine under what
/// conditions the transaction should be retained and introduced on-chain.
/// - They may define whether this transaction is acceptable for introduction on-chain at all.
///
/// Each of these semantics are defined by the `validate` function.
///
/// **NOTE: Transaction extensions cannot under any circumstances alter the call itself.**
///
/// Transaction extensions are capable of defining logic which is executed additionally to the
/// dispatch of the call:
///
/// - They may define logic which must be executed prior to the dispatch of the call.
/// - They may also define logic which must be executed after the dispatch of the call.
///
/// Each of these semantics are defined by the `prepare` and `post_dispatch_details` functions
/// respectively.
///
/// Finally, transaction extensions may define additional data to help define the implications of
/// the logic they introduce. This additional data may be explicitly defined by the transaction
/// author (in which case it is included as part of the transaction body), or it may be implicitly
/// defined by the transaction extension based around the on-chain state (which the transaction
/// author is assumed to know). This data may be utilized by the above logic to alter how a node's
/// transaction queue treats this transaction.
///
/// ## Default implementations
///
/// Of the 6 functions in this trait along with `TransactionExtension`, 2 of them must return a
/// value of an associated type on success, with only `implicit` having a default implementation.
/// This means that default implementations cannot be provided for `validate` and `prepare`.
/// However, a macro is provided [impl_tx_ext_default](crate::impl_tx_ext_default) which is capable
/// of generating default implementations for both of these functions. If you do not wish to
/// introduce additional logic into the transaction pipeline, then it is recommended that you use
/// this macro to implement these functions. Additionally, [weight](TransactionExtension::weight)
/// can return a default value, which would mean the extension is weightless, but it is not
/// implemented by default. Instead, implementers can explicitly choose to implement this default
/// behavior through the same [impl_tx_ext_default](crate::impl_tx_ext_default) macro.
///
/// If your extension does any post-flight logic, then the functionality must be implemented in
/// [post_dispatch_details](TransactionExtension::post_dispatch_details). This function can return
/// the actual weight used by the extension during an entire dispatch cycle by wrapping said weight
/// value in a `Some`. This is useful in computing fee refunds, similar to how post dispatch
/// information is used to refund fees for calls. Alternatively, a `None` can be returned, which
/// means that the worst case scenario weight, namely the value returned by
/// [weight](TransactionExtension::weight), is the actual weight. This particular piece of logic
/// is embedded in the default implementation of
/// [post_dispatch](TransactionExtension::post_dispatch) so that the weight is assumed to be worst
/// case scenario, but implementers of this trait can correct it with extra effort. Therefore, all
/// users of an extension should use [post_dispatch](TransactionExtension::post_dispatch), with
/// [post_dispatch_details](TransactionExtension::post_dispatch_details) considered an internal
/// function.
///
/// ## Pipelines, Inherited Implications, and Authorized Origins
///
/// Requiring a single transaction extension to define all of the above semantics would be
/// cumbersome and would lead to a lot of boilerplate. Instead, transaction extensions are
/// aggregated into pipelines, which are tuples of transaction extensions. Each extension in the
/// pipeline is executed in order, and the output of each extension is aggregated and/or relayed as
/// the input to the next extension in the pipeline.
///
/// This ordered composition happens with all data types ([Val](TransactionExtension::Val),
/// [Pre](TransactionExtension::Pre) and [Implicit](TransactionExtension::Implicit)) as well as
/// all functions. There are important consequences stemming from how the composition affects the
/// meaning of the `origin` and `implication` parameters as well as the results. Whereas the
/// [prepare](TransactionExtension::prepare) and
/// [post_dispatch](TransactionExtension::post_dispatch) functions are clear in their meaning, the
/// [validate](TransactionExtension::validate) function is fairly sophisticated and warrants further
/// explanation.
///
/// Firstly, the `origin` parameter. The `origin` passed into the first item in a pipeline is simply
/// that passed into the tuple itself. It represents an authority who has authorized the implication
/// of the transaction, as of the extension it has been passed into *and any further extensions it
/// may pass though, all the way to, and including, the transaction's dispatch call itself. Each
/// following item in the pipeline is passed the origin which the previous item returned. The origin
/// returned from the final item in the pipeline is the origin which is returned by the tuple
/// itself.
///
/// This means that if a constituent extension returns a different origin to the one it was called
/// with, then (assuming no other extension changes it further) *this new origin will be used for
/// all extensions following it in the pipeline, and will be returned from the pipeline to be used
/// as the origin for the call's dispatch*. The call itself as well as all these extensions
/// following may each imply consequence for this origin. We call this the *inherited implication*.
///
/// The *inherited implication* is the cumulated on-chain effects born by whatever origin is
/// returned. It is expressed to the [validate](TransactionExtension::validate) function only as the
/// `implication` argument which implements the [Encode] trait. A transaction extension may define
/// its own implications through its own fields and the
/// [implicit](TransactionExtension::implicit) function. This is only utilized by extensions
/// which precede it in a pipeline or, if the transaction is an old-school signed transaction, the
/// underlying transaction verification logic.
///
/// **The inherited implication passed as the `implication` parameter to
/// [validate](TransactionExtension::validate) does not include the extension's inner data itself
/// nor does it include the result of the extension's `implicit` function.** If you both provide an
/// implication and rely on the implication, then you need to manually aggregate your extensions
/// implication with the aggregated implication passed in.
///
/// In the post dispatch pipeline, the actual weight of each extension is accrued in the
/// [PostDispatchInfo](PostDispatchInfoOf<Call>) of that transaction sequentially with each
/// [post_dispatch](TransactionExtension::post_dispatch) call. This means that an extension handling
/// transaction payment and refunds should be at the end of the pipeline in order to capture the
/// correct amount of weight used during the call. This is because one cannot know the actual weight
/// of an extension after post dispatch without running the post dispatch ahead of time.
pub trait TransactionExtension<Call: Dispatchable>:
Codec + DecodeWithMemTracking + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo
{
/// Unique identifier of this signed extension.
///
/// This will be exposed in the metadata to identify the signed extension used in an extrinsic.
const IDENTIFIER: &'static str;
/// Any additional data which was known at the time of transaction construction and can be
/// useful in authenticating the transaction. This is determined dynamically in part from the
/// on-chain environment using the `implicit` function and not directly contained in the
/// transaction itself and therefore is considered "implicit".
type Implicit: Codec + StaticTypeInfo;
/// Determine any additional data which was known at the time of transaction construction and
/// can be useful in authenticating the transaction. The expected usage of this is to include in
/// any data which is signed and verified as part of transaction validation. Also perform any
/// pre-signature-verification checks and return an error if needed.
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
use crate::transaction_validity::InvalidTransaction::IndeterminateImplicit;
Ok(Self::Implicit::decode(&mut &[][..]).map_err(|_| IndeterminateImplicit)?)
}
/// Returns the metadata for this extension.
///
/// As a [`TransactionExtension`] can be a tuple of [`TransactionExtension`]s we need to return
/// a `Vec` that holds the metadata of each one. Each individual `TransactionExtension` must
/// return *exactly* one [`TransactionExtensionMetadata`].
///
/// This method provides a default implementation that returns a vec containing a single
/// [`TransactionExtensionMetadata`].
fn metadata() -> Vec<TransactionExtensionMetadata> {
alloc::vec![TransactionExtensionMetadata {
identifier: Self::IDENTIFIER,
ty: scale_info::meta_type::<Self>(),
implicit: scale_info::meta_type::<Self::Implicit>()
}]
}
/// The type that encodes information that can be passed from `validate` to `prepare`.
type Val;
/// The type that encodes information that can be passed from `prepare` to `post_dispatch`.
type Pre;
/// The weight consumed by executing this extension instance fully during transaction dispatch.
fn weight(&self, call: &Call) -> Weight;
/// Validate a transaction for the transaction queue.
///
/// This function can be called frequently by the transaction queue to obtain transaction
/// validity against current state. It should perform all checks that determine a valid
/// transaction, that can pay for its execution and quickly eliminate ones that are stale or
/// incorrect.
///
/// Parameters:
/// - `origin`: The origin of the transaction which this extension inherited; coming from an
/// "old-school" *signed transaction*, this will be a system `RawOrigin::Signed` value. If the
/// transaction is a "new-school" *General Transaction*, then this will be a system
/// `RawOrigin::None` value. If this extension is an item in a composite, then it could be
/// anything which was previously returned as an `origin` value in the result of a `validate`
/// call.
/// - `call`: The `Call` wrapped by this extension.
/// - `info`: Information concerning, and inherent to, the transaction's call.
/// - `len`: The total length of the encoded transaction.
/// - `inherited_implication`: The *implication* which this extension inherits. This is a tuple
/// of the transaction's call and some additional opaque-but-encodable data. Coming directly
/// from a transaction, the latter is [()]. However, if this extension is expressed as part of
/// a composite type, then the latter component is equal to any further implications to which
/// the returned `origin` could potentially apply. See Pipelines, Inherited Implications, and
/// Authorized Origins for more information.
///
/// Returns a [ValidateResult], which is a [Result] whose success type is a tuple of
/// [ValidTransaction] (defining useful metadata for the transaction queue), the [Self::Val]
/// token of this transaction, which gets passed into [prepare](TransactionExtension::prepare),
/// and the origin of the transaction, which gets passed into
/// [prepare](TransactionExtension::prepare) and is ultimately used for dispatch.
fn validate(
&self,
origin: DispatchOriginOf<Call>,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
self_implicit: Self::Implicit,
inherited_implication: &impl Implication,
source: TransactionSource,
) -> ValidateResult<Self::Val, Call>;
/// Do any pre-flight stuff for a transaction after validation.
///
/// This is for actions which do not happen in the transaction queue but only immediately prior
/// to the point of dispatch on-chain. This should not return an error, since errors should
/// already have been identified during the [validate](TransactionExtension::validate) call. If
/// an error is returned, the transaction will be considered invalid but no state changes will
/// happen and therefore work done in [validate](TransactionExtension::validate) will not be
/// paid for.
///
/// Unlike `validate`, this function may consume `self`.
///
/// Parameters:
/// - `val`: `Self::Val` returned by the result of the `validate` call.
/// - `origin`: The origin returned by the result of the `validate` call.
/// - `call`: The `Call` wrapped by this extension.
/// - `info`: Information concerning, and inherent to, the transaction's call.
/// - `len`: The total length of the encoded transaction.
///
/// Returns a [Self::Pre] value on success, which gets passed into
/// [post_dispatch](TransactionExtension::post_dispatch) and after the call is dispatched.
///
/// IMPORTANT: **Checks made in validation need not be repeated here.**
fn prepare(
self,
val: Self::Val,
origin: &DispatchOriginOf<Call>,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError>;
/// Do any post-flight stuff for an extrinsic.
///
/// `_pre` contains the output of `prepare`.
///
/// This gets given the `DispatchResult` `_result` from the extrinsic and can, if desired,
/// introduce a `TransactionValidityError`, causing the block to become invalid for including
/// it.
///
/// On success, the caller must return the amount of unspent weight left over by this extension
/// after dispatch. By default, this function returns no unspent weight, which means the entire
/// weight computed for the worst case scenario is consumed.
///
/// WARNING: This function does not automatically keep track of accumulated "actual" weight.
/// Unless this weight is handled at the call site, use
/// [post_dispatch](TransactionExtension::post_dispatch)
/// instead.
///
/// Parameters:
/// - `pre`: `Self::Pre` returned by the result of the `prepare` call prior to dispatch.
/// - `info`: Information concerning, and inherent to, the transaction's call.
/// - `post_info`: Information concerning the dispatch of the transaction's call.
/// - `len`: The total length of the encoded transaction.
/// - `result`: The result of the dispatch.
///
/// WARNING: It is dangerous to return an error here. To do so will fundamentally invalidate the
/// transaction and any block that it is included in, causing the block author to not be
/// compensated for their work in validating the transaction or producing the block so far. It
/// can only be used safely when you *know* that the transaction is one that would only be
/// introduced by the current block author.
fn post_dispatch_details(
_pre: Self::Pre,
_info: &DispatchInfoOf<Call>,
_post_info: &PostDispatchInfoOf<Call>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
Ok(Weight::zero())
}
/// A wrapper for [`post_dispatch_details`](TransactionExtension::post_dispatch_details) that
/// refunds the unspent weight consumed by this extension into the post dispatch information.
///
/// If `post_dispatch_details` returns a non-zero unspent weight, which, by definition, must be
/// less than the worst case weight provided by [weight](TransactionExtension::weight), that
/// is the value refunded in `post_info`.
///
/// If no unspent weight is reported by `post_dispatch_details`, this function assumes the worst
/// case weight and does not refund anything.
///
/// For more information, look into
/// [post_dispatch_details](TransactionExtension::post_dispatch_details).
fn post_dispatch(
pre: Self::Pre,
info: &DispatchInfoOf<Call>,
post_info: &mut PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
let unspent_weight = Self::post_dispatch_details(pre, info, &post_info, len, result)?;
post_info.refund(unspent_weight);
Ok(())
}
/// Validation logic for bare extrinsics.
///
/// NOTE: This function will be migrated to a separate `InherentExtension` interface.
fn bare_validate(
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
) -> TransactionValidity {
Ok(ValidTransaction::default())
}
/// All pre-flight logic run before dispatching bare extrinsics.
///
/// NOTE: This function will be migrated to a separate `InherentExtension` interface.
fn bare_validate_and_prepare(
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
) -> Result<(), TransactionValidityError> {
Ok(())
}
/// Post dispatch logic run after dispatching bare extrinsics.
///
/// NOTE: This function will be migrated to a separate `InherentExtension` interface.
fn bare_post_dispatch(
_info: &DispatchInfoOf<Call>,
_post_info: &mut PostDispatchInfoOf<Call>,
_len: usize,
_result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
Ok(())
}
}
/// Helper macro to be used in a `impl TransactionExtension` block to add default implementations of
/// `weight`, `validate`, `prepare` or any combinations of the them.
///
/// The macro is to be used with 2 parameters, separated by ";":
/// - the `Call` type;
/// - the functions for which a default implementation should be generated, separated by " ";
/// available options are `weight`, `validate` and `prepare`.
///
/// Example usage:
/// ```nocompile
/// impl TransactionExtension<FirstCall> for EmptyExtension {
/// type Val = ();
/// type Pre = ();
///
/// impl_tx_ext_default!(FirstCall; weight validate prepare);
/// }
///
/// impl TransactionExtension<SecondCall> for SimpleExtension {
/// type Val = u32;
/// type Pre = ();
///
/// fn weight(&self, _: &SecondCall) -> Weight {
/// Weight::zero()
/// }
///
/// fn validate(
/// &self,
/// _origin: <T as Config>::RuntimeOrigin,
/// _call: &SecondCall,
/// _info: &DispatchInfoOf<SecondCall>,
/// _len: usize,
/// _self_implicit: Self::Implicit,
/// _inherited_implication: &impl Encode,
/// ) -> ValidateResult<Self::Val, SecondCall> {
/// Ok((Default::default(), 42u32, origin))
/// }
///
/// impl_tx_ext_default!(SecondCall; prepare);
/// }
/// ```
#[macro_export]
macro_rules! impl_tx_ext_default {
($call:ty ; , $( $rest:tt )*) => {
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ; validate $( $rest:tt )*) => {
fn validate(
&self,
origin: $crate::traits::DispatchOriginOf<$call>,
_call: &$call,
_info: &$crate::traits::DispatchInfoOf<$call>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl $crate::codec::Encode,
_source: $crate::transaction_validity::TransactionSource,
) -> $crate::traits::ValidateResult<Self::Val, $call> {
Ok((Default::default(), Default::default(), origin))
}
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ; prepare $( $rest:tt )*) => {
fn prepare(
self,
_val: Self::Val,
_origin: &$crate::traits::DispatchOriginOf<$call>,
_call: &$call,
_info: &$crate::traits::DispatchInfoOf<$call>,
_len: usize,
) -> Result<Self::Pre, $crate::transaction_validity::TransactionValidityError> {
Ok(Default::default())
}
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ; weight $( $rest:tt )*) => {
fn weight(&self, _call: &$call) -> $crate::Weight {
$crate::Weight::zero()
}
$crate::impl_tx_ext_default!{$call ; $( $rest )*}
};
($call:ty ;) => {};
}
/// Information about a [`TransactionExtension`] for the runtime metadata.
pub struct TransactionExtensionMetadata {
/// The unique identifier of the [`TransactionExtension`].
pub identifier: &'static str,
/// The type of the [`TransactionExtension`].
pub ty: MetaType,
/// The type of the [`TransactionExtension`] additional signed data for the payload.
pub implicit: MetaType,
}
#[impl_for_tuples(1, 12)]
impl<Call: Dispatchable> TransactionExtension<Call> for Tuple {
const IDENTIFIER: &'static str = "Use `metadata()`!";
for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); );
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(for_tuples!( ( #( Tuple.implicit()? ),* ) ))
}
fn metadata() -> Vec<TransactionExtensionMetadata> {
let mut ids = Vec::new();
for_tuples!( #( ids.extend(Tuple::metadata()); )* );
ids
}
for_tuples!( type Val = ( #( Tuple::Val ),* ); );
for_tuples!( type Pre = ( #( Tuple::Pre ),* ); );
fn weight(&self, call: &Call) -> Weight {
let mut weight = Weight::zero();
for_tuples!( #( weight = weight.saturating_add(Tuple.weight(call)); )* );
weight
}
fn validate(
&self,
origin: <Call as Dispatchable>::RuntimeOrigin,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
self_implicit: Self::Implicit,
inherited_implication: &impl Implication,
source: TransactionSource,
) -> Result<
(ValidTransaction, Self::Val, <Call as Dispatchable>::RuntimeOrigin),
TransactionValidityError,
> {
let valid = ValidTransaction::default();
let val = ();
let following_explicit_implications = for_tuples!( ( #( &self.Tuple ),* ) );
let following_implicit_implications = self_implicit;
let implication_parts = inherited_implication.parts();
for_tuples!(#(
// Implication of this pipeline element not relevant for later items, so we pop it.
let (_item, following_explicit_implications) = following_explicit_implications.pop_front();
let (item_implicit, following_implicit_implications) = following_implicit_implications.pop_front();
let (item_valid, item_val, origin) = {
Tuple.validate(origin, call, info, len, item_implicit,
&ImplicationParts {
base: implication_parts.base,
explicit: (&following_explicit_implications, implication_parts.explicit),
implicit: (&following_implicit_implications, implication_parts.implicit),
},
source)?
};
let valid = valid.combine_with(item_valid);
let val = val.push_back(item_val);
)* );
Ok((valid, val, origin))
}
fn prepare(
self,
val: Self::Val,
origin: &<Call as Dispatchable>::RuntimeOrigin,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(for_tuples!( ( #(
Tuple::prepare(self.Tuple, val.Tuple, origin, call, info, len)?
),* ) ))
}
fn post_dispatch_details(
pre: Self::Pre,
info: &DispatchInfoOf<Call>,
post_info: &PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
let mut total_unspent_weight = Weight::zero();
for_tuples!( #({
let unspent_weight = Tuple::post_dispatch_details(pre.Tuple, info, post_info, len, result)?;
total_unspent_weight = total_unspent_weight.saturating_add(unspent_weight);
})* );
Ok(total_unspent_weight)
}
fn post_dispatch(
pre: Self::Pre,
info: &DispatchInfoOf<Call>,
post_info: &mut PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info, post_info, len, result)?; )* );
Ok(())
}
fn bare_validate(call: &Call, info: &DispatchInfoOf<Call>, len: usize) -> TransactionValidity {
let valid = ValidTransaction::default();
for_tuples!(#(
let item_valid = Tuple::bare_validate(call, info, len)?;
let valid = valid.combine_with(item_valid);
)* );
Ok(valid)
}
fn bare_validate_and_prepare(
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> Result<(), TransactionValidityError> {
for_tuples!( #( Tuple::bare_validate_and_prepare(call, info, len)?; )* );
Ok(())
}
fn bare_post_dispatch(
info: &DispatchInfoOf<Call>,
post_info: &mut PostDispatchInfoOf<Call>,
len: usize,
result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
for_tuples!( #( Tuple::bare_post_dispatch(info, post_info, len, result)?; )* );
Ok(())
}
}
impl<Call: Dispatchable> TransactionExtension<Call> for () {
const IDENTIFIER: &'static str = "UnitTransactionExtension";
type Implicit = ();
fn implicit(&self) -> core::result::Result<Self::Implicit, TransactionValidityError> {
Ok(())
}
type Val = ();
type Pre = ();
fn weight(&self, _call: &Call) -> Weight {
Weight::zero()
}
fn validate(
&self,
origin: <Call as Dispatchable>::RuntimeOrigin,
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Implication,
_source: TransactionSource,
) -> Result<
(ValidTransaction, (), <Call as Dispatchable>::RuntimeOrigin),
TransactionValidityError,
> {
Ok((ValidTransaction::default(), (), origin))
}
fn prepare(
self,
_val: (),
_origin: &<Call as Dispatchable>::RuntimeOrigin,
_call: &Call,
_info: &DispatchInfoOf<Call>,
_len: usize,
) -> Result<(), TransactionValidityError> {
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_implications_on_nested_structure() {
use scale_info::TypeInfo;
use std::cell::RefCell;
#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
struct MockExtension {
also_implicit: u8,
explicit: u8,
}
const CALL_IMPLICIT: u8 = 23;
thread_local! {
static COUNTER: RefCell<u8> = RefCell::new(1);
}
impl TransactionExtension<()> for MockExtension {
const IDENTIFIER: &'static str = "MockExtension";
type Implicit = u8;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(self.also_implicit)
}
type Val = ();
type Pre = ();
fn weight(&self, _call: &()) -> Weight {
Weight::zero()
}
fn prepare(
self,
_val: Self::Val,
_origin: &DispatchOriginOf<()>,
_call: &(),
_info: &DispatchInfoOf<()>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(())
}
fn validate(
&self,
origin: DispatchOriginOf<()>,
_call: &(),
_info: &DispatchInfoOf<()>,
_len: usize,
self_implicit: Self::Implicit,
inherited_implication: &impl Implication,
_source: TransactionSource,
) -> ValidateResult<Self::Val, ()> {
COUNTER.with(|c| {
let mut counter = c.borrow_mut();
assert_eq!(self_implicit, *counter);
assert_eq!(
self,
&MockExtension { also_implicit: *counter, explicit: *counter + 1 }
);
// Implications must be call then 1 to 22 then 1 to 22 odd.
let mut assert_implications = Vec::new();
assert_implications.push(CALL_IMPLICIT);
for i in *counter + 2..23 {
assert_implications.push(i);
}
for i in *counter + 2..23 {
if i % 2 == 1 {
assert_implications.push(i);
}
}
assert_eq!(inherited_implication.encode(), assert_implications);
*counter += 2;
});
Ok((ValidTransaction::default(), (), origin))
}
fn post_dispatch_details(
_pre: Self::Pre,
_info: &DispatchInfoOf<()>,
_post_info: &PostDispatchInfoOf<()>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
Ok(Weight::zero())
}
}
// Test for one nested structure
let ext = (
MockExtension { also_implicit: 1, explicit: 2 },
MockExtension { also_implicit: 3, explicit: 4 },
(
MockExtension { also_implicit: 5, explicit: 6 },
MockExtension { also_implicit: 7, explicit: 8 },
(
MockExtension { also_implicit: 9, explicit: 10 },
MockExtension { also_implicit: 11, explicit: 12 },
),
MockExtension { also_implicit: 13, explicit: 14 },
MockExtension { also_implicit: 15, explicit: 16 },
),
MockExtension { also_implicit: 17, explicit: 18 },
(MockExtension { also_implicit: 19, explicit: 20 },),
MockExtension { also_implicit: 21, explicit: 22 },
);
let implicit = ext.implicit().unwrap();
let res = ext
.validate(
(),
&(),
&DispatchInfoOf::<()>::default(),
0,
implicit,
&TxBaseImplication(CALL_IMPLICIT),
TransactionSource::Local,
)
.expect("valid");
assert_eq!(res.0, ValidTransaction::default());
// Test for another nested structure
COUNTER.with(|c| {
*c.borrow_mut() = 1;
});
let ext = (
MockExtension { also_implicit: 1, explicit: 2 },
MockExtension { also_implicit: 3, explicit: 4 },
MockExtension { also_implicit: 5, explicit: 6 },
MockExtension { also_implicit: 7, explicit: 8 },
MockExtension { also_implicit: 9, explicit: 10 },
MockExtension { also_implicit: 11, explicit: 12 },
(
MockExtension { also_implicit: 13, explicit: 14 },
MockExtension { also_implicit: 15, explicit: 16 },
MockExtension { also_implicit: 17, explicit: 18 },
MockExtension { also_implicit: 19, explicit: 20 },
MockExtension { also_implicit: 21, explicit: 22 },
),
);
let implicit = ext.implicit().unwrap();
let res = ext
.validate(
(),
&(),
&DispatchInfoOf::<()>::default(),
0,
implicit,
&TxBaseImplication(CALL_IMPLICIT),
TransactionSource::Local,
)
.expect("valid");
assert_eq!(res.0, ValidTransaction::default());
}
}
@@ -0,0 +1,498 @@
// 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.
//! Transaction validity interface.
use crate::{
codec::{Decode, Encode},
RuntimeDebug,
};
use alloc::{vec, vec::Vec};
use scale_info::TypeInfo;
use pezsp_weights::Weight;
/// Priority for a transaction. Additive. Higher is better.
pub type TransactionPriority = u64;
/// Minimum number of blocks a transaction will remain valid for.
/// `TransactionLongevity::max_value()` means "forever".
pub type TransactionLongevity = u64;
/// Tag for a transaction. No two transactions with the same tag should be placed on-chain.
pub type TransactionTag = Vec<u8>;
/// An invalid transaction validity.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum InvalidTransaction {
/// The call of the transaction is not expected.
Call,
/// General error to do with the inability to pay some fees (e.g. account balance too low).
Payment,
/// General error to do with the transaction not yet being valid (e.g. nonce too high).
Future,
/// General error to do with the transaction being outdated (e.g. nonce too low).
Stale,
/// General error to do with the transaction's proofs (e.g. signature).
///
/// # Possible causes
///
/// When using a signed extension that provides additional data for signing, it is required
/// that the signing and the verifying side use the same additional data. Additional
/// data will only be used to generate the signature, but will not be part of the transaction
/// itself. As the verifying side does not know which additional data was used while signing
/// it will only be able to assume a bad signature and cannot express a more meaningful error.
BadProof,
/// The transaction birth block is ancient.
///
/// # Possible causes
///
/// For `FRAME`-based runtimes this would be caused by `current block number
/// - Era::birth block number > BlockHashCount`. (e.g. in Pezkuwi `BlockHashCount` = 2400, so a
/// transaction with birth block number 1337 would be valid up until block number 1337 + 2400,
/// after which point the transaction would be considered to have an ancient birth block.)
AncientBirthBlock,
/// The transaction would exhaust the resources of current block.
///
/// The transaction might be valid, but there are not enough resources
/// left in the current block.
ExhaustsResources,
/// Any other custom invalid validity that is not covered by this enum.
Custom(u8),
/// An extrinsic with a Mandatory dispatch resulted in Error. This is indicative of either a
/// malicious validator or a buggy `provide_inherent`. In any case, it can result in
/// dangerously overweight blocks and therefore if found, invalidates the block.
BadMandatory,
/// An extrinsic with a mandatory dispatch tried to be validated.
/// This is invalid; only inherent extrinsics are allowed to have mandatory dispatches.
MandatoryValidation,
/// The sending address is disabled or known to be invalid.
BadSigner,
/// The implicit data was unable to be calculated.
IndeterminateImplicit,
/// The transaction extension did not authorize any origin.
UnknownOrigin,
}
impl InvalidTransaction {
/// Returns if the reason for the invalidity was block resource exhaustion.
pub fn exhausted_resources(&self) -> bool {
matches!(self, Self::ExhaustsResources)
}
/// Returns if the reason for the invalidity was a mandatory call failing.
pub fn was_mandatory(&self) -> bool {
matches!(self, Self::BadMandatory)
}
}
impl From<InvalidTransaction> for &'static str {
fn from(invalid: InvalidTransaction) -> &'static str {
match invalid {
InvalidTransaction::Call => "Transaction call is not expected",
InvalidTransaction::Future => "Transaction will be valid in the future",
InvalidTransaction::Stale => "Transaction is outdated",
InvalidTransaction::BadProof => "Transaction has a bad signature",
InvalidTransaction::AncientBirthBlock => "Transaction has an ancient birth block",
InvalidTransaction::ExhaustsResources => "Transaction would exhaust the block limits",
InvalidTransaction::Payment =>
"Inability to pay some fees (e.g. account balance too low)",
InvalidTransaction::BadMandatory =>
"A call was labelled as mandatory, but resulted in an Error.",
InvalidTransaction::MandatoryValidation =>
"Transaction dispatch is mandatory; transactions must not be validated.",
InvalidTransaction::Custom(_) => "InvalidTransaction custom error",
InvalidTransaction::BadSigner => "Invalid signing address",
InvalidTransaction::IndeterminateImplicit =>
"The implicit data was unable to be calculated",
InvalidTransaction::UnknownOrigin =>
"The transaction extension did not authorize any origin",
}
}
}
/// An unknown transaction validity.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UnknownTransaction {
/// Could not lookup some information that is required to validate the transaction.
CannotLookup,
/// No validator found for the given unsigned transaction.
NoUnsignedValidator,
/// Any other custom unknown validity that is not covered by this enum.
Custom(u8),
}
impl From<UnknownTransaction> for &'static str {
fn from(unknown: UnknownTransaction) -> &'static str {
match unknown {
UnknownTransaction::CannotLookup =>
"Could not lookup information required to validate the transaction",
UnknownTransaction::NoUnsignedValidator =>
"Could not find an unsigned validator for the unsigned transaction",
UnknownTransaction::Custom(_) => "UnknownTransaction custom error",
}
}
}
/// Errors that can occur while checking the validity of a transaction.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TransactionValidityError {
/// The transaction is invalid.
Invalid(InvalidTransaction),
/// Transaction validity can't be determined.
Unknown(UnknownTransaction),
}
impl TransactionValidityError {
/// Returns `true` if the reason for the error was block resource exhaustion.
pub fn exhausted_resources(&self) -> bool {
match self {
Self::Invalid(e) => e.exhausted_resources(),
Self::Unknown(_) => false,
}
}
/// Returns `true` if the reason for the error was it being a mandatory dispatch that could not
/// be completed successfully.
pub fn was_mandatory(&self) -> bool {
match self {
Self::Invalid(e) => e.was_mandatory(),
Self::Unknown(_) => false,
}
}
}
impl From<TransactionValidityError> for &'static str {
fn from(err: TransactionValidityError) -> &'static str {
match err {
TransactionValidityError::Invalid(invalid) => invalid.into(),
TransactionValidityError::Unknown(unknown) => unknown.into(),
}
}
}
impl From<InvalidTransaction> for TransactionValidityError {
fn from(err: InvalidTransaction) -> Self {
TransactionValidityError::Invalid(err)
}
}
impl From<UnknownTransaction> for TransactionValidityError {
fn from(err: UnknownTransaction) -> Self {
TransactionValidityError::Unknown(err)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TransactionValidityError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for TransactionValidityError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s: &'static str = (*self).into();
write!(f, "{}", s)
}
}
/// Information on a transaction's validity and, if valid, on how it relates to other transactions.
pub type TransactionValidity = Result<ValidTransaction, TransactionValidityError>;
/// Information on a transaction's validity and, if valid, on how it relates to other transactions
/// and some refund for the operation.
pub type TransactionValidityWithRefund =
Result<(ValidTransaction, Weight), TransactionValidityError>;
impl From<InvalidTransaction> for TransactionValidity {
fn from(invalid_transaction: InvalidTransaction) -> Self {
Err(TransactionValidityError::Invalid(invalid_transaction))
}
}
impl From<UnknownTransaction> for TransactionValidity {
fn from(unknown_transaction: UnknownTransaction) -> Self {
Err(TransactionValidityError::Unknown(unknown_transaction))
}
}
/// The source of the transaction.
///
/// Depending on the source we might apply different validation schemes.
/// For instance we can disallow specific kinds of transactions if they were not produced
/// by our local node (for instance off-chain workers).
#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Hash)]
pub enum TransactionSource {
/// Transaction is already included in block.
///
/// This means that we can't really tell where the transaction is coming from,
/// since it's already in the received block. Note that the custom validation logic
/// using either `Local` or `External` should most likely just allow `InBlock`
/// transactions as well.
InBlock,
/// Transaction is coming from a local source.
///
/// This means that the transaction was produced internally by the node
/// (for instance an Off-Chain Worker, or an Off-Chain Call), as opposed
/// to being received over the network.
Local,
/// Transaction has been received externally.
///
/// This means the transaction has been received from (usually) "untrusted" source,
/// for instance received over the network or RPC.
External,
}
/// Information concerning a valid transaction.
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct ValidTransaction {
/// Priority of the transaction.
///
/// Priority determines the ordering of two transactions that have all
/// their dependencies (required tags) satisfied.
pub priority: TransactionPriority,
/// Transaction dependencies
///
/// A non-empty list signifies that some other transactions which provide
/// given tags are required to be included before that one.
pub requires: Vec<TransactionTag>,
/// Provided tags
///
/// A list of tags this transaction provides. Successfully importing the transaction
/// will enable other transactions that depend on (require) those tags to be included as well.
/// Provided and required tags allow Bizinikiwi to build a dependency graph of transactions
/// and import them in the right (linear) order.
///
/// <div class="warning">
///
/// If two different transactions have the same `provides` tags, the transaction pool
/// treats them as conflicting. One of these transactions will be dropped - e.g. depending on
/// submission time, priority of transaction.
///
/// A transaction that has no provided tags, will be dropped by the transaction pool.
///
/// </div>
pub provides: Vec<TransactionTag>,
/// Transaction longevity
///
/// Longevity describes minimum number of blocks the validity is correct.
/// After this period transaction should be removed from the pool or revalidated.
pub longevity: TransactionLongevity,
/// A flag indicating if the transaction should be propagated to other peers.
///
/// By setting `false` here the transaction will still be considered for
/// including in blocks that are authored on the current node, but will
/// never be sent to other peers.
pub propagate: bool,
}
impl Default for ValidTransaction {
fn default() -> Self {
Self {
priority: 0,
requires: vec![],
provides: vec![],
longevity: TransactionLongevity::max_value(),
propagate: true,
}
}
}
impl ValidTransaction {
/// Initiate `ValidTransaction` builder object with a particular prefix for tags.
///
/// To avoid conflicts between different parts in runtime it's recommended to build `requires`
/// and `provides` tags with a unique prefix.
pub fn with_tag_prefix(prefix: &'static str) -> ValidTransactionBuilder {
ValidTransactionBuilder { prefix: Some(prefix), validity: Default::default() }
}
/// Combine two instances into one, as a best effort. This will take the superset of each of the
/// `provides` and `requires` tags, it will sum the priorities, take the minimum longevity and
/// the logic *And* of the propagate flags.
pub fn combine_with(mut self, mut other: ValidTransaction) -> Self {
Self {
priority: self.priority.saturating_add(other.priority),
requires: {
self.requires.append(&mut other.requires);
self.requires
},
provides: {
self.provides.append(&mut other.provides);
self.provides
},
longevity: self.longevity.min(other.longevity),
propagate: self.propagate && other.propagate,
}
}
}
/// `ValidTransaction` builder.
///
///
/// Allows to easily construct `ValidTransaction` and most importantly takes care of
/// prefixing `requires` and `provides` tags to avoid conflicts.
#[derive(Default, Clone, RuntimeDebug)]
pub struct ValidTransactionBuilder {
prefix: Option<&'static str>,
validity: ValidTransaction,
}
impl ValidTransactionBuilder {
/// Set the priority of a transaction.
///
/// Note that the final priority for `FRAME` is combined from all `TransactionExtension`s.
/// Most likely for unsigned transactions you want the priority to be higher
/// than for regular transactions. We recommend exposing a base priority for unsigned
/// transactions as a runtime module parameter, so that the runtime can tune inter-module
/// priorities.
pub fn priority(mut self, priority: TransactionPriority) -> Self {
self.validity.priority = priority;
self
}
/// Set the longevity of a transaction.
///
/// By default the transaction will be considered valid forever and will not be revalidated
/// by the transaction pool. It's recommended though to set the longevity to a finite value
/// though. If unsure, it's also reasonable to expose this parameter via module configuration
/// and let the runtime decide.
pub fn longevity(mut self, longevity: TransactionLongevity) -> Self {
self.validity.longevity = longevity;
self
}
/// Set the propagate flag.
///
/// Set to `false` if the transaction is not meant to be gossiped to peers. Combined with
/// `TransactionSource::Local` validation it can be used to have special kind of
/// transactions that are only produced and included by the validator nodes.
pub fn propagate(mut self, propagate: bool) -> Self {
self.validity.propagate = propagate;
self
}
/// Add a `TransactionTag` to the set of required tags.
///
/// The tag will be encoded and prefixed with module prefix (if any).
/// If you'd rather add a raw `require` tag, consider using `#combine_with` method.
pub fn and_requires(mut self, tag: impl Encode) -> Self {
self.validity.requires.push(match self.prefix.as_ref() {
Some(prefix) => (prefix, tag).encode(),
None => tag.encode(),
});
self
}
/// Add a `TransactionTag` to the set of provided tags.
///
/// The tag will be encoded and prefixed with module prefix (if any).
/// If you'd rather add a raw `require` tag, consider using `#combine_with` method.
pub fn and_provides(mut self, tag: impl Encode) -> Self {
self.validity.provides.push(match self.prefix.as_ref() {
Some(prefix) => (prefix, tag).encode(),
None => tag.encode(),
});
self
}
/// Augment the builder with existing `ValidTransaction`.
///
/// This method does add the prefix to `require` or `provides` tags.
pub fn combine_with(mut self, validity: ValidTransaction) -> Self {
self.validity = core::mem::take(&mut self.validity).combine_with(validity);
self
}
/// Finalize the builder and produce `TransactionValidity`.
///
/// Note the result will always be `Ok`. Use `Into` to produce `ValidTransaction`.
pub fn build(self) -> TransactionValidity {
self.into()
}
}
impl From<ValidTransactionBuilder> for TransactionValidity {
fn from(builder: ValidTransactionBuilder) -> Self {
Ok(builder.into())
}
}
impl From<ValidTransactionBuilder> for ValidTransaction {
fn from(builder: ValidTransactionBuilder) -> Self {
builder.validity
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_encode_and_decode() {
let v: TransactionValidity = Ok(ValidTransaction {
priority: 5,
requires: vec![vec![1, 2, 3, 4]],
provides: vec![vec![4, 5, 6]],
longevity: 42,
propagate: false,
});
let encoded = v.encode();
assert_eq!(
encoded,
vec![
0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0,
0, 0
]
);
// decode back
assert_eq!(TransactionValidity::decode(&mut &*encoded), Ok(v));
}
#[test]
fn builder_should_prefix_the_tags() {
const PREFIX: &str = "test";
let a: ValidTransaction = ValidTransaction::with_tag_prefix(PREFIX)
.and_requires(1)
.and_requires(2)
.and_provides(3)
.and_provides(4)
.propagate(false)
.longevity(5)
.priority(3)
.priority(6)
.into();
assert_eq!(
a,
ValidTransaction {
propagate: false,
longevity: 5,
priority: 6,
requires: vec![(PREFIX, 1).encode(), (PREFIX, 2).encode()],
provides: vec![(PREFIX, 3).encode(), (PREFIX, 4).encode()],
}
);
}
}
@@ -0,0 +1,588 @@
// 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.
//! Provides a type that wraps another type and provides a default value.
use crate::traits::{Bounded, One, Zero};
use codec::{Compact, CompactAs, Decode, DecodeWithMemTracking, Encode, HasCompact, MaxEncodedLen};
use core::{
fmt::Display,
marker::PhantomData,
ops::{
Add, AddAssign, BitAnd, BitOr, BitXor, Deref, Div, DivAssign, Mul, MulAssign, Not, Rem,
RemAssign, Shl, Shr, Sub, SubAssign,
},
};
use num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
Num, NumCast, PrimInt, Saturating, ToPrimitive,
};
use scale_info::{StaticTypeInfo, TypeInfo};
use pezsp_core::Get;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A type that wraps another type and provides a default value.
///
/// Passes through arithmetical and many other operations to the inner value.
/// Type information for metadata is the same as the inner value's type.
#[derive(Encode, Decode, DecodeWithMemTracking, Debug, MaxEncodedLen)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TypeWithDefault<T, D: Get<T>>(T, PhantomData<D>);
impl<T, D: Get<T>> TypeWithDefault<T, D> {
fn new(value: T) -> Self {
Self(value, PhantomData)
}
}
// Hides implementation details from the outside (for metadata type information).
//
// The type info showed in metadata is the one of the inner value's type.
impl<T: StaticTypeInfo, D: Get<T> + 'static> TypeInfo for TypeWithDefault<T, D> {
type Identity = Self;
fn type_info() -> scale_info::Type {
T::type_info()
}
}
impl<T: Clone, D: Get<T>> Clone for TypeWithDefault<T, D> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T: Copy, D: Get<T>> Copy for TypeWithDefault<T, D> {}
impl<T: PartialEq, D: Get<T>> PartialEq for TypeWithDefault<T, D> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Eq, D: Get<T>> Eq for TypeWithDefault<T, D> {}
impl<T: PartialOrd, D: Get<T>> PartialOrd for TypeWithDefault<T, D> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: Ord, D: Get<T>> Ord for TypeWithDefault<T, D> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T, D: Get<T>> Deref for TypeWithDefault<T, D> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T, D: Get<T>> Default for TypeWithDefault<T, D> {
fn default() -> Self {
Self::new(D::get())
}
}
impl<T: CheckedNeg, D: Get<T>> CheckedNeg for TypeWithDefault<T, D> {
fn checked_neg(&self) -> Option<Self> {
self.0.checked_neg().map(Self::new)
}
}
impl<T: CheckedRem, D: Get<T>> CheckedRem for TypeWithDefault<T, D> {
fn checked_rem(&self, rhs: &Self) -> Option<Self> {
self.0.checked_rem(&rhs.0).map(Self::new)
}
}
impl<T: CheckedShr, D: Get<T>> CheckedShr for TypeWithDefault<T, D> {
fn checked_shr(&self, n: u32) -> Option<Self> {
self.0.checked_shr(n).map(Self::new)
}
}
impl<T: CheckedShl, D: Get<T>> CheckedShl for TypeWithDefault<T, D> {
fn checked_shl(&self, n: u32) -> Option<Self> {
self.0.checked_shl(n).map(Self::new)
}
}
impl<T: Rem<Output = T>, D: Get<T>> Rem for TypeWithDefault<T, D> {
type Output = Self;
fn rem(self, rhs: Self) -> Self {
Self::new(self.0 % rhs.0)
}
}
impl<T: Rem<u32, Output = T>, D: Get<T>> Rem<u32> for TypeWithDefault<T, D> {
type Output = Self;
fn rem(self, rhs: u32) -> Self {
Self::new(self.0 % (rhs.into()))
}
}
impl<T: Shr<u32, Output = T>, D: Get<T>> Shr<u32> for TypeWithDefault<T, D> {
type Output = Self;
fn shr(self, rhs: u32) -> Self {
Self::new(self.0 >> rhs)
}
}
impl<T: Shr<usize, Output = T>, D: Get<T>> Shr<usize> for TypeWithDefault<T, D> {
type Output = Self;
fn shr(self, rhs: usize) -> Self {
Self::new(self.0 >> rhs)
}
}
impl<T: Shl<u32, Output = T>, D: Get<T>> Shl<u32> for TypeWithDefault<T, D> {
type Output = Self;
fn shl(self, rhs: u32) -> Self {
Self::new(self.0 << rhs)
}
}
impl<T: Shl<usize, Output = T>, D: Get<T>> Shl<usize> for TypeWithDefault<T, D> {
type Output = Self;
fn shl(self, rhs: usize) -> Self {
Self::new(self.0 << rhs)
}
}
impl<T: RemAssign, D: Get<T>> RemAssign for TypeWithDefault<T, D> {
fn rem_assign(&mut self, rhs: Self) {
self.0 %= rhs.0
}
}
impl<T: DivAssign, D: Get<T>> DivAssign for TypeWithDefault<T, D> {
fn div_assign(&mut self, rhs: Self) {
self.0 /= rhs.0
}
}
impl<T: MulAssign, D: Get<T>> MulAssign for TypeWithDefault<T, D> {
fn mul_assign(&mut self, rhs: Self) {
self.0 *= rhs.0
}
}
impl<T: SubAssign, D: Get<T>> SubAssign for TypeWithDefault<T, D> {
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0
}
}
impl<T: AddAssign, D: Get<T>> AddAssign for TypeWithDefault<T, D> {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0
}
}
impl<T: Display, D: Get<T>> Display for TypeWithDefault<T, D> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
macro_rules! impl_from {
($for_type:ty $(, $from_type:ty)*) => {
$(
impl<D: Get<$for_type>> From<$from_type> for TypeWithDefault<$for_type, D> {
fn from(value: $from_type) -> Self {
Self::new(value.into())
}
}
)*
}
}
impl_from!(u128, u128, u64, u32, u16, u8);
impl_from!(u64, u64, u32, u16, u8);
impl_from!(u32, u32, u16, u8);
impl_from!(u16, u16, u8);
impl_from!(u8, u8);
macro_rules! impl_try_from {
($for_type:ty $(, $try_from_type:ty)*) => {
$(
impl<D: Get<$for_type>> TryFrom<$try_from_type> for TypeWithDefault<$for_type, D> {
type Error = <$for_type as TryFrom<$try_from_type>>::Error;
fn try_from(n: $try_from_type) -> Result<TypeWithDefault<$for_type, D>, Self::Error> {
<$for_type as TryFrom<$try_from_type>>::try_from(n).map(Self::new)
}
}
)*
}
}
impl_try_from!(u8, u16, u32, u64, u128);
impl_try_from!(u16, u32, u64, u128);
impl_try_from!(u32, u64, u128);
impl_try_from!(u64, u128);
impl<T: TryFrom<usize>, D: Get<T>> TryFrom<usize> for TypeWithDefault<T, D> {
type Error = <T as TryFrom<usize>>::Error;
fn try_from(n: usize) -> Result<TypeWithDefault<T, D>, Self::Error> {
T::try_from(n).map(Self::new)
}
}
impl<T: TryInto<u8>, D: Get<T>> TryFrom<TypeWithDefault<T, D>> for u8 {
type Error = <T as TryInto<u8>>::Error;
fn try_from(value: TypeWithDefault<T, D>) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl<T: TryInto<u16>, D: Get<T>> TryFrom<TypeWithDefault<T, D>> for u16 {
type Error = <T as TryInto<u16>>::Error;
fn try_from(value: TypeWithDefault<T, D>) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl<T: TryInto<u32>, D: Get<T>> TryFrom<TypeWithDefault<T, D>> for u32 {
type Error = <T as TryInto<u32>>::Error;
fn try_from(value: TypeWithDefault<T, D>) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl<T: TryInto<u64>, D: Get<T>> TryFrom<TypeWithDefault<T, D>> for u64 {
type Error = <T as TryInto<u64>>::Error;
fn try_from(value: TypeWithDefault<T, D>) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl<T: TryInto<u128>, D: Get<T>> TryFrom<TypeWithDefault<T, D>> for u128 {
type Error = <T as TryInto<u128>>::Error;
fn try_from(value: TypeWithDefault<T, D>) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl<T: TryInto<usize>, D: Get<T>> TryFrom<TypeWithDefault<T, D>> for usize {
type Error = <T as TryInto<usize>>::Error;
fn try_from(value: TypeWithDefault<T, D>) -> Result<Self, Self::Error> {
value.0.try_into()
}
}
impl<T: Zero + PartialEq, D: Get<T>> Zero for TypeWithDefault<T, D> {
fn zero() -> Self {
Self::new(T::zero())
}
fn is_zero(&self) -> bool {
self.0 == T::zero()
}
}
impl<T: Bounded, D: Get<T>> Bounded for TypeWithDefault<T, D> {
fn min_value() -> Self {
Self::new(T::min_value())
}
fn max_value() -> Self {
Self::new(T::max_value())
}
}
impl<T: PrimInt, D: Get<T>> PrimInt for TypeWithDefault<T, D> {
fn count_ones(self) -> u32 {
self.0.count_ones()
}
fn leading_zeros(self) -> u32 {
self.0.leading_zeros()
}
fn trailing_zeros(self) -> u32 {
self.0.trailing_zeros()
}
fn rotate_left(self, n: u32) -> Self {
Self::new(self.0.rotate_left(n))
}
fn rotate_right(self, n: u32) -> Self {
Self::new(self.0.rotate_right(n))
}
fn swap_bytes(self) -> Self {
Self::new(self.0.swap_bytes())
}
fn from_be(x: Self) -> Self {
Self::new(T::from_be(x.0))
}
fn from_le(x: Self) -> Self {
Self::new(T::from_le(x.0))
}
fn to_be(self) -> Self {
Self::new(self.0.to_be())
}
fn to_le(self) -> Self {
Self::new(self.0.to_le())
}
fn count_zeros(self) -> u32 {
self.0.count_zeros()
}
fn signed_shl(self, n: u32) -> Self {
Self::new(self.0.signed_shl(n))
}
fn signed_shr(self, n: u32) -> Self {
Self::new(self.0.signed_shr(n))
}
fn unsigned_shl(self, n: u32) -> Self {
Self::new(self.0.unsigned_shl(n))
}
fn unsigned_shr(self, n: u32) -> Self {
Self::new(self.0.unsigned_shr(n))
}
fn pow(self, exp: u32) -> Self {
Self::new(self.0.pow(exp))
}
}
impl<T: Saturating, D: Get<T>> Saturating for TypeWithDefault<T, D> {
fn saturating_add(self, rhs: Self) -> Self {
Self::new(self.0.saturating_add(rhs.0))
}
fn saturating_sub(self, rhs: Self) -> Self {
Self::new(self.0.saturating_sub(rhs.0))
}
}
impl<T: Div<Output = T>, D: Get<T>> Div for TypeWithDefault<T, D> {
type Output = Self;
fn div(self, rhs: Self) -> Self {
Self::new(self.0 / rhs.0)
}
}
impl<T: Mul<Output = T>, D: Get<T>> Mul for TypeWithDefault<T, D> {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self::new(self.0 * rhs.0)
}
}
impl<T: CheckedDiv, D: Get<T>> CheckedDiv for TypeWithDefault<T, D> {
fn checked_div(&self, rhs: &Self) -> Option<Self> {
self.0.checked_div(&rhs.0).map(Self::new)
}
}
impl<T: CheckedMul, D: Get<T>> CheckedMul for TypeWithDefault<T, D> {
fn checked_mul(&self, rhs: &Self) -> Option<Self> {
self.0.checked_mul(&rhs.0).map(Self::new)
}
}
impl<T: Sub<Output = T>, D: Get<T>> Sub for TypeWithDefault<T, D> {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self::new(self.0 - rhs.0)
}
}
impl<T: CheckedSub, D: Get<T>> CheckedSub for TypeWithDefault<T, D> {
fn checked_sub(&self, rhs: &Self) -> Option<Self> {
self.0.checked_sub(&rhs.0).map(Self::new)
}
}
impl<T: Add<Output = T>, D: Get<T>> Add for TypeWithDefault<T, D> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self::new(self.0 + rhs.0)
}
}
impl<T: CheckedAdd, D: Get<T>> CheckedAdd for TypeWithDefault<T, D> {
fn checked_add(&self, rhs: &Self) -> Option<Self> {
self.0.checked_add(&rhs.0).map(Self::new)
}
}
impl<T: BitAnd<Output = T>, D: Get<T>> BitAnd for TypeWithDefault<T, D> {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self::new(self.0 & rhs.0)
}
}
impl<T: BitOr<Output = T>, D: Get<T>> BitOr for TypeWithDefault<T, D> {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self::new(self.0 | rhs.0)
}
}
impl<T: BitXor<Output = T>, D: Get<T>> BitXor for TypeWithDefault<T, D> {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
Self::new(self.0 ^ rhs.0)
}
}
impl<T: One, D: Get<T>> One for TypeWithDefault<T, D> {
fn one() -> Self {
Self::new(T::one())
}
}
impl<T: Not<Output = T>, D: Get<T>> Not for TypeWithDefault<T, D> {
type Output = Self;
fn not(self) -> Self {
Self::new(self.0.not())
}
}
impl<T: NumCast, D: Get<T>> NumCast for TypeWithDefault<T, D> {
fn from<P: ToPrimitive>(n: P) -> Option<Self> {
<T as NumCast>::from(n).map_or(None, |n| Some(Self::new(n)))
}
}
impl<T: Num, D: Get<T>> Num for TypeWithDefault<T, D> {
type FromStrRadixErr = <T as Num>::FromStrRadixErr;
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
T::from_str_radix(s, radix).map(Self::new)
}
}
impl<T: ToPrimitive, D: Get<T>> ToPrimitive for TypeWithDefault<T, D> {
fn to_i64(&self) -> Option<i64> {
self.0.to_i64()
}
fn to_u64(&self) -> Option<u64> {
self.0.to_u64()
}
fn to_i128(&self) -> Option<i128> {
self.0.to_i128()
}
fn to_u128(&self) -> Option<u128> {
self.0.to_u128()
}
}
impl<T, D: Get<T>> From<Compact<TypeWithDefault<T, D>>> for TypeWithDefault<T, D> {
fn from(c: Compact<TypeWithDefault<T, D>>) -> Self {
c.0
}
}
impl<T: HasCompact, D: Get<T>> CompactAs for TypeWithDefault<T, D> {
type As = T;
fn encode_as(&self) -> &Self::As {
&self.0
}
fn decode_from(val: Self::As) -> Result<Self, codec::Error> {
Ok(Self::new(val))
}
}
#[cfg(test)]
mod tests {
use super::TypeWithDefault;
use scale_info::TypeInfo;
use pezsp_arithmetic::traits::{AtLeast16Bit, AtLeast32Bit, AtLeast8Bit};
use pezsp_core::Get;
#[test]
#[allow(dead_code)]
fn test_type_with_default_impl_base_arithmetic() {
trait WrapAtLeast8Bit: AtLeast8Bit {}
trait WrapAtLeast16Bit: AtLeast16Bit {}
trait WrapAtLeast32Bit: AtLeast32Bit {}
struct Getu8;
impl Get<u8> for Getu8 {
fn get() -> u8 {
0
}
}
type U8WithDefault = TypeWithDefault<u8, Getu8>;
impl WrapAtLeast8Bit for U8WithDefault {}
struct Getu16;
impl Get<u16> for Getu16 {
fn get() -> u16 {
0
}
}
type U16WithDefault = TypeWithDefault<u16, Getu16>;
impl WrapAtLeast16Bit for U16WithDefault {}
struct Getu32;
impl Get<u32> for Getu32 {
fn get() -> u32 {
0
}
}
type U32WithDefault = TypeWithDefault<u32, Getu32>;
impl WrapAtLeast32Bit for U32WithDefault {}
struct Getu64;
impl Get<u64> for Getu64 {
fn get() -> u64 {
0
}
}
type U64WithDefault = TypeWithDefault<u64, Getu64>;
impl WrapAtLeast32Bit for U64WithDefault {}
struct Getu128;
impl Get<u128> for Getu128 {
fn get() -> u128 {
0
}
}
type U128WithDefault = TypeWithDefault<u128, Getu128>;
impl WrapAtLeast32Bit for U128WithDefault {}
assert_eq!(U8WithDefault::type_info(), <u8 as TypeInfo>::type_info());
assert_eq!(U16WithDefault::type_info(), <u16 as TypeInfo>::type_info());
assert_eq!(U32WithDefault::type_info(), <u32 as TypeInfo>::type_info());
assert_eq!(U64WithDefault::type_info(), <u64 as TypeInfo>::type_info());
assert_eq!(U128WithDefault::type_info(), <u128 as TypeInfo>::type_info());
}
}