feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,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",
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
Runtime Modules shared primitive types.
|
||||
|
||||
License: Apache-2.0
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../docs
|
||||
@@ -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
+131
@@ -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)
|
||||
}
|
||||
}
|
||||
+185
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user