feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
@@ -0,0 +1,206 @@
// This file is part of Substrate.
// 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 sp_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 substrate 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 substrate 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 substrate 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 Substrate.
// 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 sp_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, sp_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, sp_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 Substrate.
// 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 sp_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| sp_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 = sp_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 Substrate.
// 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, sp_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 Substrate.
// 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 sp_core::U256;
/// Abstraction over a block header for a substrate chain.
#[derive(
Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Clone, sp_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 Substrate.
// 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 Substrate.
// 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