feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -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
Reference in New Issue
Block a user