feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Bizinikiwi blockchain trait
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use parking_lot::RwLock;
|
||||
use pezsp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
|
||||
Justifications,
|
||||
};
|
||||
use std::collections::{btree_set::BTreeSet, HashMap, VecDeque};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
header_metadata::HeaderMetadata,
|
||||
tree_route, CachedHeaderMetadata,
|
||||
};
|
||||
|
||||
/// Blockchain database header backend. Does not perform any validation.
|
||||
pub trait HeaderBackend<Block: BlockT>: Send + Sync {
|
||||
/// Get block header. Returns `None` if block is not found.
|
||||
fn header(&self, hash: Block::Hash) -> Result<Option<Block::Header>>;
|
||||
/// Get blockchain info.
|
||||
fn info(&self) -> Info<Block>;
|
||||
/// Get block status.
|
||||
fn status(&self, hash: Block::Hash) -> Result<BlockStatus>;
|
||||
/// Get block number by hash. Returns `None` if the header is not in the chain.
|
||||
fn number(
|
||||
&self,
|
||||
hash: Block::Hash,
|
||||
) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>>;
|
||||
/// Get block hash by number. Returns `None` if the header is not in the chain.
|
||||
fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>>;
|
||||
|
||||
/// Convert an arbitrary block ID into a block hash.
|
||||
fn block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Option<Block::Hash>> {
|
||||
match *id {
|
||||
BlockId::Hash(h) => Ok(Some(h)),
|
||||
BlockId::Number(n) => self.hash(n),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an arbitrary block ID into a block hash.
|
||||
fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
|
||||
match *id {
|
||||
BlockId::Hash(h) => self.number(h),
|
||||
BlockId::Number(n) => Ok(Some(n)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get block header. Returns `UnknownBlock` error if block is not found.
|
||||
fn expect_header(&self, hash: Block::Hash) -> Result<Block::Header> {
|
||||
self.header(hash)?
|
||||
.ok_or_else(|| Error::UnknownBlock(format!("Expect header: {}", hash)))
|
||||
}
|
||||
|
||||
/// Convert an arbitrary block ID into a block number. Returns `UnknownBlock` error if block is
|
||||
/// not found.
|
||||
fn expect_block_number_from_id(&self, id: &BlockId<Block>) -> Result<NumberFor<Block>> {
|
||||
self.block_number_from_id(id).and_then(|n| {
|
||||
n.ok_or_else(|| Error::UnknownBlock(format!("Expect block number from id: {}", id)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert an arbitrary block ID into a block hash. Returns `UnknownBlock` error if block is
|
||||
/// not found.
|
||||
fn expect_block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Block::Hash> {
|
||||
self.block_hash_from_id(id).and_then(|h| {
|
||||
h.ok_or_else(|| Error::UnknownBlock(format!("Expect block hash from id: {}", id)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles stale forks.
|
||||
pub trait ForkBackend<Block: BlockT>:
|
||||
HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync
|
||||
{
|
||||
/// Returns block hashes for provided fork heads. It skips the fork if when blocks are missing
|
||||
/// (e.g. warp-sync) and internal `tree_route` function fails.
|
||||
///
|
||||
/// Example:
|
||||
/// G --- A1 --- A2 --- A3 --- A4 ( < fork1 )
|
||||
/// \-----C4 --- C5 ( < fork2 )
|
||||
/// We finalize A3 and call expand_fork(C5). Result = (C5,C4).
|
||||
fn expand_forks(
|
||||
&self,
|
||||
fork_heads: &[Block::Hash],
|
||||
) -> std::result::Result<BTreeSet<Block::Hash>, Error> {
|
||||
let mut expanded_forks = BTreeSet::new();
|
||||
for fork_head in fork_heads {
|
||||
match tree_route(self, *fork_head, self.info().finalized_hash) {
|
||||
Ok(tree_route) => {
|
||||
for block in tree_route.retracted() {
|
||||
expanded_forks.insert(block.hash);
|
||||
}
|
||||
continue;
|
||||
},
|
||||
Err(_) => {
|
||||
// There are cases when blocks are missing (e.g. warp-sync).
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_forks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, T> ForkBackend<Block> for T
|
||||
where
|
||||
Block: BlockT,
|
||||
T: HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync,
|
||||
{
|
||||
}
|
||||
|
||||
struct MinimalBlockMetadata<Block: BlockT> {
|
||||
number: NumberFor<Block>,
|
||||
hash: Block::Hash,
|
||||
parent: Block::Hash,
|
||||
}
|
||||
|
||||
impl<Block> Clone for MinimalBlockMetadata<Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self { number: self.number, hash: self.hash, parent: self.parent }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block> Copy for MinimalBlockMetadata<Block> where Block: BlockT {}
|
||||
|
||||
impl<Block> From<&CachedHeaderMetadata<Block>> for MinimalBlockMetadata<Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn from(value: &CachedHeaderMetadata<Block>) -> Self {
|
||||
Self { number: value.number, hash: value.hash, parent: value.parent }
|
||||
}
|
||||
}
|
||||
|
||||
/// Blockchain database backend. Does not perform any validation.
|
||||
pub trait Backend<Block: BlockT>:
|
||||
HeaderBackend<Block> + HeaderMetadata<Block, Error = Error>
|
||||
{
|
||||
/// Get block body. Returns `None` if block is not found.
|
||||
fn body(&self, hash: Block::Hash) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
|
||||
/// Get block justifications. Returns `None` if no justification exists.
|
||||
fn justifications(&self, hash: Block::Hash) -> Result<Option<Justifications>>;
|
||||
/// Get last finalized block hash.
|
||||
fn last_finalized(&self) -> Result<Block::Hash>;
|
||||
|
||||
/// Returns hashes of all blocks that are leaves of the block tree.
|
||||
/// in other words, that have no children, are chain heads.
|
||||
/// Results must be ordered best (longest, highest) chain first.
|
||||
fn leaves(&self) -> Result<Vec<Block::Hash>>;
|
||||
|
||||
/// Return hashes of all blocks that are children of the block with `parent_hash`.
|
||||
fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>>;
|
||||
|
||||
/// Get the most recent block hash of the longest chain that contains
|
||||
/// a block with the given `base_hash`.
|
||||
///
|
||||
/// The search space is always limited to blocks which are in the finalized
|
||||
/// chain or descendants of it.
|
||||
///
|
||||
/// Returns `Ok(None)` if `base_hash` is not found in search space.
|
||||
// TODO: document time complexity of this, see [#1444](https://github.com/pezkuwichain/kurdistan-sdk/issues/18)
|
||||
fn longest_containing(
|
||||
&self,
|
||||
base_hash: Block::Hash,
|
||||
import_lock: &RwLock<()>,
|
||||
) -> Result<Option<Block::Hash>> {
|
||||
let Some(base_header) = self.header(base_hash)? else { return Ok(None) };
|
||||
|
||||
let leaves = {
|
||||
// ensure no blocks are imported during this code block.
|
||||
// an import could trigger a reorg which could change the canonical chain.
|
||||
// we depend on the canonical chain staying the same during this code block.
|
||||
let _import_guard = import_lock.read();
|
||||
let info = self.info();
|
||||
if info.finalized_number > *base_header.number() {
|
||||
// `base_header` is on a dead fork.
|
||||
return Ok(None);
|
||||
}
|
||||
self.leaves()?
|
||||
};
|
||||
|
||||
// for each chain. longest chain first. shortest last
|
||||
for leaf_hash in leaves {
|
||||
let mut current_hash = leaf_hash;
|
||||
// go backwards through the chain (via parent links)
|
||||
loop {
|
||||
if current_hash == base_hash {
|
||||
return Ok(Some(leaf_hash));
|
||||
}
|
||||
|
||||
let current_header = self
|
||||
.header(current_hash)?
|
||||
.ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?;
|
||||
|
||||
// stop search in this chain once we go below the target's block number
|
||||
if current_header.number() < base_header.number() {
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
}
|
||||
}
|
||||
|
||||
// header may be on a dead fork -- the only leaves that are considered are
|
||||
// those which can still be finalized.
|
||||
//
|
||||
// FIXME #1558 only issue this warning when not on a dead fork
|
||||
warn!(
|
||||
target: crate::LOG_TARGET,
|
||||
"Block {:?} exists in chain but not found when following all leaves backwards",
|
||||
base_hash,
|
||||
);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Get single indexed transaction by content hash. Note that this will only fetch transactions
|
||||
/// that are indexed by the runtime with `storage_index_transaction`.
|
||||
fn indexed_transaction(&self, hash: Block::Hash) -> Result<Option<Vec<u8>>>;
|
||||
|
||||
/// Check if indexed transaction exists.
|
||||
fn has_indexed_transaction(&self, hash: Block::Hash) -> Result<bool> {
|
||||
Ok(self.indexed_transaction(hash)?.is_some())
|
||||
}
|
||||
|
||||
fn block_indexed_body(&self, hash: Block::Hash) -> Result<Option<Vec<Vec<u8>>>>;
|
||||
|
||||
/// Returns all leaves that will be displaced after the block finalization.
|
||||
fn displaced_leaves_after_finalizing(
|
||||
&self,
|
||||
finalized_block_hash: Block::Hash,
|
||||
finalized_block_number: NumberFor<Block>,
|
||||
finalized_block_parent_hash: Block::Hash,
|
||||
) -> std::result::Result<DisplacedLeavesAfterFinalization<Block>, Error> {
|
||||
// There are no forks at genesis.
|
||||
if finalized_block_number.is_zero() {
|
||||
return Ok(DisplacedLeavesAfterFinalization::default());
|
||||
}
|
||||
|
||||
let leaves = self.leaves()?;
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaves,
|
||||
?finalized_block_hash,
|
||||
?finalized_block_number,
|
||||
"Checking for displaced leaves after finalization."
|
||||
);
|
||||
|
||||
// Store hashes of finalized blocks for quick checking later, the last block is the
|
||||
// finalized one
|
||||
let mut finalized_chain = VecDeque::new();
|
||||
finalized_chain.push_front(MinimalBlockMetadata {
|
||||
number: finalized_block_number,
|
||||
hash: finalized_block_hash,
|
||||
parent: finalized_block_parent_hash,
|
||||
});
|
||||
|
||||
// Local cache is a performance optimization in case of finalized block deep below the
|
||||
// tip of the chain with a lot of leaves above finalized block
|
||||
let mut local_cache = HashMap::<Block::Hash, MinimalBlockMetadata<Block>>::new();
|
||||
|
||||
let mut result = DisplacedLeavesAfterFinalization {
|
||||
displaced_leaves: Vec::with_capacity(leaves.len()),
|
||||
displaced_blocks: Vec::with_capacity(leaves.len()),
|
||||
};
|
||||
|
||||
let mut displaced_blocks_candidates = Vec::new();
|
||||
|
||||
let genesis_hash = self.info().genesis_hash;
|
||||
|
||||
for leaf_hash in leaves {
|
||||
let mut current_header_metadata =
|
||||
MinimalBlockMetadata::from(&self.header_metadata(leaf_hash).map_err(|err| {
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaf_hash,
|
||||
?err,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Failed to fetch leaf header.",
|
||||
);
|
||||
err
|
||||
})?);
|
||||
let leaf_number = current_header_metadata.number;
|
||||
|
||||
// The genesis block is part of the canonical chain.
|
||||
if leaf_hash == genesis_hash {
|
||||
result.displaced_leaves.push((leaf_number, leaf_hash));
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaf_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Added genesis leaf to displaced leaves."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaf_number,
|
||||
?leaf_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Handle displaced leaf.",
|
||||
);
|
||||
|
||||
// Collect all block hashes until the height of the finalized block
|
||||
displaced_blocks_candidates.clear();
|
||||
while current_header_metadata.number > finalized_block_number {
|
||||
displaced_blocks_candidates.push(current_header_metadata.hash);
|
||||
|
||||
let parent_hash = current_header_metadata.parent;
|
||||
match local_cache.get(&parent_hash) {
|
||||
Some(metadata_header) => {
|
||||
current_header_metadata = *metadata_header;
|
||||
},
|
||||
None => {
|
||||
current_header_metadata = MinimalBlockMetadata::from(
|
||||
&self.header_metadata(parent_hash).map_err(|err| {
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?err,
|
||||
?parent_hash,
|
||||
?leaf_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Failed to fetch parent header during leaf tracking.",
|
||||
);
|
||||
|
||||
err
|
||||
})?,
|
||||
);
|
||||
// Cache locally in case more branches above finalized block reference
|
||||
// the same block hash
|
||||
local_cache.insert(parent_hash, current_header_metadata);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If points back to the finalized header then nothing left to do, this leaf will be
|
||||
// checked again later
|
||||
if current_header_metadata.hash == finalized_block_hash {
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaf_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Leaf points to the finalized header, skipping for now.",
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// We reuse `displaced_blocks_candidates` to store the current metadata.
|
||||
// This block is not displaced if there is a gap in the ancestry. We
|
||||
// check for this gap later.
|
||||
displaced_blocks_candidates.push(current_header_metadata.hash);
|
||||
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
current_hash = ?current_header_metadata.hash,
|
||||
current_num = ?current_header_metadata.number,
|
||||
?finalized_block_number,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Looking for path from finalized block number to current leaf number"
|
||||
);
|
||||
|
||||
// Collect the rest of the displaced blocks of leaf branch
|
||||
for distance_from_finalized in 1_u32.. {
|
||||
// Find block at `distance_from_finalized` from finalized block
|
||||
let (finalized_chain_block_number, finalized_chain_block_hash) =
|
||||
match finalized_chain.iter().rev().nth(distance_from_finalized as usize) {
|
||||
Some(header) => (header.number, header.hash),
|
||||
None => {
|
||||
let to_fetch = finalized_chain.front().expect("Not empty; qed");
|
||||
let metadata = match self.header_metadata(to_fetch.parent) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(Error::UnknownBlock(_)) => {
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
distance_from_finalized,
|
||||
hash = ?to_fetch.parent,
|
||||
number = ?to_fetch.number,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Tried to fetch unknown block, block ancestry has gaps."
|
||||
);
|
||||
break;
|
||||
},
|
||||
Err(err) => {
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
hash = ?to_fetch.parent,
|
||||
number = ?to_fetch.number,
|
||||
?err,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Failed to fetch header for parent hash.",
|
||||
);
|
||||
return Err(err);
|
||||
},
|
||||
};
|
||||
let metadata = MinimalBlockMetadata::from(&metadata);
|
||||
let result = (metadata.number, metadata.hash);
|
||||
finalized_chain.push_front(metadata);
|
||||
result
|
||||
},
|
||||
};
|
||||
|
||||
if current_header_metadata.hash == finalized_chain_block_hash {
|
||||
// Found the block on the finalized chain, nothing left to do
|
||||
result.displaced_leaves.push((leaf_number, leaf_hash));
|
||||
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaf_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Leaf is ancestor of finalized block."
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if current_header_metadata.number <= finalized_chain_block_number {
|
||||
// Skip more blocks until we get all blocks on finalized chain until the height
|
||||
// of the parent block
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent_hash = current_header_metadata.parent;
|
||||
if finalized_chain_block_hash == parent_hash {
|
||||
// Reached finalized chain, nothing left to do
|
||||
result.displaced_blocks.extend(displaced_blocks_candidates.drain(..));
|
||||
result.displaced_leaves.push((leaf_number, leaf_hash));
|
||||
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?leaf_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Found displaced leaf."
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Store displaced block and look deeper for block on finalized chain
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?parent_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Found displaced block. Looking further.",
|
||||
);
|
||||
displaced_blocks_candidates.push(parent_hash);
|
||||
current_header_metadata = MinimalBlockMetadata::from(
|
||||
&self.header_metadata(parent_hash).map_err(|err| {
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
?err,
|
||||
?parent_hash,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Failed to fetch header for parent during displaced block collection",
|
||||
);
|
||||
err
|
||||
})?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// There could be duplicates shared by multiple branches, clean them up
|
||||
result.displaced_blocks.sort_unstable();
|
||||
result.displaced_blocks.dedup();
|
||||
|
||||
debug!(
|
||||
target: crate::LOG_TARGET,
|
||||
%finalized_block_hash,
|
||||
?finalized_block_number,
|
||||
?result,
|
||||
elapsed = ?now.elapsed(),
|
||||
"Finished checking for displaced leaves after finalization.",
|
||||
);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of [`Backend::displaced_leaves_after_finalizing`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisplacedLeavesAfterFinalization<Block: BlockT> {
|
||||
/// A list of hashes and block numbers of displaced leaves.
|
||||
pub displaced_leaves: Vec<(NumberFor<Block>, Block::Hash)>,
|
||||
|
||||
/// A list of hashes displaced blocks from all displaced leaves.
|
||||
pub displaced_blocks: Vec<Block::Hash>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Default for DisplacedLeavesAfterFinalization<Block> {
|
||||
fn default() -> Self {
|
||||
Self { displaced_leaves: Vec::new(), displaced_blocks: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> DisplacedLeavesAfterFinalization<Block> {
|
||||
/// Returns a collection of hashes for the displaced leaves.
|
||||
pub fn hashes(&self) -> impl Iterator<Item = Block::Hash> + '_ {
|
||||
self.displaced_leaves.iter().map(|(_, hash)| *hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the type of block gaps that may result from either warp sync or fast sync.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
|
||||
pub enum BlockGapType {
|
||||
/// Both the header and body are missing, as a result of warp sync.
|
||||
MissingHeaderAndBody,
|
||||
/// The block body is missing, as a result of fast sync.
|
||||
MissingBody,
|
||||
}
|
||||
|
||||
/// Represents the block gap resulted by warp sync or fast sync.
|
||||
///
|
||||
/// A block gap is a range of blocks where either the bodies, or both headers and bodies are
|
||||
/// missing.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)]
|
||||
pub struct BlockGap<N> {
|
||||
/// The starting block number of the gap (inclusive).
|
||||
pub start: N,
|
||||
/// The ending block number of the gap (inclusive).
|
||||
pub end: N,
|
||||
/// The type of gap.
|
||||
pub gap_type: BlockGapType,
|
||||
}
|
||||
|
||||
/// Blockchain info
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct Info<Block: BlockT> {
|
||||
/// Best block hash.
|
||||
pub best_hash: Block::Hash,
|
||||
/// Best block number.
|
||||
pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
|
||||
/// Genesis block hash.
|
||||
pub genesis_hash: Block::Hash,
|
||||
/// The head of the finalized chain.
|
||||
pub finalized_hash: Block::Hash,
|
||||
/// Last finalized block number.
|
||||
pub finalized_number: <<Block as BlockT>::Header as HeaderT>::Number,
|
||||
/// Last finalized state.
|
||||
pub finalized_state: Option<(Block::Hash, <<Block as BlockT>::Header as HeaderT>::Number)>,
|
||||
/// Number of concurrent leave forks.
|
||||
pub number_leaves: usize,
|
||||
/// Missing blocks after warp sync or fast sync.
|
||||
pub block_gap: Option<BlockGap<NumberFor<Block>>>,
|
||||
}
|
||||
|
||||
/// Block status.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BlockStatus {
|
||||
/// Already in the blockchain.
|
||||
InChain,
|
||||
/// Not in the queue or the blockchain.
|
||||
Unknown,
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Bizinikiwi client possible errors.
|
||||
|
||||
use codec::Error as CodecError;
|
||||
use pezsp_api::ApiError;
|
||||
use pezsp_consensus;
|
||||
use pezsp_runtime::transaction_validity::TransactionValidityError;
|
||||
use pezsp_state_machine;
|
||||
use std::{self, result};
|
||||
|
||||
/// Client Result type alias
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Error when the runtime failed to apply an extrinsic.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ApplyExtrinsicFailed {
|
||||
/// The transaction cannot be included into the current block.
|
||||
///
|
||||
/// This doesn't necessary mean that the transaction itself is invalid, but it might be just
|
||||
/// unapplicable onto the current block.
|
||||
#[error("Extrinsic is not valid: {0:?}")]
|
||||
Validity(#[from] TransactionValidityError),
|
||||
|
||||
#[error("Application specific error")]
|
||||
Application(#[source] Box<dyn 'static + std::error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
/// Bizinikiwi Client error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
#[error("Cancelled oneshot channel {0}")]
|
||||
OneShotCancelled(#[from] futures::channel::oneshot::Canceled),
|
||||
|
||||
#[error(transparent)]
|
||||
Consensus(#[from] pezsp_consensus::Error),
|
||||
|
||||
#[error("Backend error: {0}")]
|
||||
Backend(String),
|
||||
|
||||
#[error("UnknownBlock: {0}")]
|
||||
UnknownBlock(String),
|
||||
|
||||
#[error("UnknownBlocks: {0}")]
|
||||
UnknownBlocks(String),
|
||||
|
||||
#[error(transparent)]
|
||||
ApplyExtrinsicFailed(#[from] ApplyExtrinsicFailed),
|
||||
|
||||
#[error("Child type is invalid")]
|
||||
InvalidChildType,
|
||||
|
||||
#[error("RemoteBodyRequest: invalid extrinsics root expected: {expected} but got {received}")]
|
||||
ExtrinsicRootInvalid { received: String, expected: String },
|
||||
|
||||
// `inner` cannot be made member, since it lacks `std::error::Error` trait bounds.
|
||||
#[error("Execution failed: {0}")]
|
||||
Execution(Box<dyn pezsp_state_machine::Error>),
|
||||
|
||||
#[error("Blockchain")]
|
||||
Blockchain(#[source] Box<Error>),
|
||||
|
||||
/// A error used by various storage subsystems.
|
||||
///
|
||||
/// Eventually this will be replaced.
|
||||
#[error("{0}")]
|
||||
StorageChanges(pezsp_state_machine::DefaultError),
|
||||
|
||||
#[error("Invalid child storage key")]
|
||||
InvalidChildStorageKey,
|
||||
|
||||
#[error("Current state of blockchain has invalid authorities set")]
|
||||
InvalidAuthoritiesSet,
|
||||
|
||||
#[error("Failed to get runtime version: {0}")]
|
||||
VersionInvalid(String),
|
||||
|
||||
#[error("Provided state is invalid")]
|
||||
InvalidState,
|
||||
|
||||
#[error("error decoding justification for header")]
|
||||
JustificationDecode,
|
||||
|
||||
#[error("bad justification for header: {0}")]
|
||||
BadJustification(String),
|
||||
|
||||
#[error("outdated justification")]
|
||||
OutdatedJustification,
|
||||
|
||||
#[error("This method is not currently available when running in light client mode")]
|
||||
NotAvailableOnLightClient,
|
||||
|
||||
#[error("Remote node has responded with invalid header proof")]
|
||||
InvalidCHTProof,
|
||||
|
||||
#[error("Remote data fetch has been cancelled")]
|
||||
RemoteFetchCancelled,
|
||||
|
||||
#[error("Remote data fetch has been failed")]
|
||||
RemoteFetchFailed,
|
||||
|
||||
#[error("Error decoding call result of {0}")]
|
||||
CallResultDecode(&'static str, #[source] CodecError),
|
||||
|
||||
#[error("Error at calling runtime api: {0}")]
|
||||
RuntimeApiError(#[from] ApiError),
|
||||
|
||||
#[error("Runtime :code missing in storage")]
|
||||
RuntimeCodeMissing,
|
||||
|
||||
#[error("Changes tries are not supported by the runtime")]
|
||||
ChangesTriesNotSupported,
|
||||
|
||||
#[error("Error reading changes tries configuration")]
|
||||
ErrorReadingChangesTriesConfig,
|
||||
|
||||
#[error("Failed to check changes proof: {0}")]
|
||||
ChangesTrieAccessFailed(String),
|
||||
|
||||
#[error("Did not finalize blocks in sequential order.")]
|
||||
NonSequentialFinalization(String),
|
||||
|
||||
#[error("Potential long-range attack: block not in finalized chain.")]
|
||||
NotInFinalizedChain,
|
||||
|
||||
#[error("Failed to get hash of block for building CHT")]
|
||||
MissingHashRequiredForCHT,
|
||||
|
||||
#[error("Calculated state root does not match.")]
|
||||
InvalidStateRoot,
|
||||
|
||||
#[error("Incomplete block import pipeline.")]
|
||||
IncompletePipeline,
|
||||
|
||||
#[error("Transaction pool not ready for block production.")]
|
||||
TransactionPoolNotReady,
|
||||
|
||||
#[error("Database error: {0}")]
|
||||
DatabaseError(#[from] pezsp_database::error::DatabaseError),
|
||||
|
||||
#[error("Failed to get header for hash {0}")]
|
||||
MissingHeader(String),
|
||||
|
||||
#[error("State Database error: {0}")]
|
||||
StateDatabase(String),
|
||||
|
||||
#[error("Statement store error: {0}")]
|
||||
StatementStore(String),
|
||||
|
||||
#[error("Failed to set the chain head to a block that's too old.")]
|
||||
SetHeadTooOld,
|
||||
|
||||
#[error(transparent)]
|
||||
Application(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
// Should be removed/improved once
|
||||
// the storage `fn`s returns typed errors.
|
||||
#[error("Runtime code error: {0}")]
|
||||
RuntimeCode(&'static str),
|
||||
|
||||
// Should be removed/improved once
|
||||
// the storage `fn`s returns typed errors.
|
||||
#[error("Storage error: {0}")]
|
||||
Storage(String),
|
||||
}
|
||||
|
||||
impl From<Box<dyn pezsp_state_machine::Error + Send + Sync + 'static>> for Error {
|
||||
fn from(e: Box<dyn pezsp_state_machine::Error + Send + Sync + 'static>) -> Self {
|
||||
Self::from_state(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn pezsp_state_machine::Error>> for Error {
|
||||
fn from(e: Box<dyn pezsp_state_machine::Error>) -> Self {
|
||||
Self::from_state(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for ApiError {
|
||||
fn from(err: Error) -> ApiError {
|
||||
match err {
|
||||
Error::UnknownBlock(msg) => ApiError::UnknownBlock(msg),
|
||||
Error::RuntimeApiError(err) => err,
|
||||
e => ApiError::Application(Box::new(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Chain a blockchain error.
|
||||
pub fn from_blockchain(e: Box<Error>) -> Self {
|
||||
Error::Blockchain(e)
|
||||
}
|
||||
|
||||
/// Chain a state error.
|
||||
pub fn from_state(e: Box<dyn pezsp_state_machine::Error>) -> Self {
|
||||
Error::Execution(e)
|
||||
}
|
||||
|
||||
/// Construct from a state db error.
|
||||
// Can not be done directly, since that would make cargo run out of stack if
|
||||
// `sc-state-db` is lib is added as dependency.
|
||||
pub fn from_state_db<E>(e: E) -> Self
|
||||
where
|
||||
E: std::fmt::Debug,
|
||||
{
|
||||
Error::StateDatabase(format!("{:?}", e))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implements tree backend, cached header metadata and algorithms
|
||||
//! to compute routes efficiently over the tree of headers.
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use schnellru::{ByLength, LruMap};
|
||||
use pezsp_core::U256;
|
||||
use pezsp_runtime::{
|
||||
traits::{Block as BlockT, Header, NumberFor, One},
|
||||
Saturating,
|
||||
};
|
||||
|
||||
/// Set to the expected max difference between `best` and `finalized` blocks at sync.
|
||||
pub(crate) const LRU_CACHE_SIZE: u32 = 5_000;
|
||||
|
||||
/// Get the lowest common ancestor between two blocks in the tree.
|
||||
///
|
||||
/// This implementation is efficient because our trees have very few and
|
||||
/// small branches, and because of our current query pattern:
|
||||
/// lca(best, final), lca(best + 1, final), lca(best + 2, final), etc.
|
||||
/// The first call is O(h) but the others are O(1).
|
||||
pub fn lowest_common_ancestor<Block: BlockT, T: HeaderMetadata<Block> + ?Sized>(
|
||||
backend: &T,
|
||||
id_one: Block::Hash,
|
||||
id_two: Block::Hash,
|
||||
) -> Result<HashAndNumber<Block>, T::Error> {
|
||||
let mut header_one = backend.header_metadata(id_one)?;
|
||||
if header_one.parent == id_two {
|
||||
return Ok(HashAndNumber { hash: id_two, number: header_one.number - One::one() });
|
||||
}
|
||||
|
||||
let mut header_two = backend.header_metadata(id_two)?;
|
||||
if header_two.parent == id_one {
|
||||
return Ok(HashAndNumber { hash: id_one, number: header_one.number });
|
||||
}
|
||||
|
||||
let mut orig_header_one = header_one.clone();
|
||||
let mut orig_header_two = header_two.clone();
|
||||
|
||||
// We move through ancestor links as much as possible, since ancestor >= parent.
|
||||
|
||||
while header_one.number > header_two.number {
|
||||
let ancestor_one = backend.header_metadata(header_one.ancestor)?;
|
||||
|
||||
if ancestor_one.number >= header_two.number {
|
||||
header_one = ancestor_one;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while header_one.number < header_two.number {
|
||||
let ancestor_two = backend.header_metadata(header_two.ancestor)?;
|
||||
|
||||
if ancestor_two.number >= header_one.number {
|
||||
header_two = ancestor_two;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Then we move the remaining path using parent links.
|
||||
|
||||
while header_one.hash != header_two.hash {
|
||||
if header_one.number > header_two.number {
|
||||
header_one = backend.header_metadata(header_one.parent)?;
|
||||
} else {
|
||||
header_two = backend.header_metadata(header_two.parent)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Update cached ancestor links.
|
||||
|
||||
if orig_header_one.number > header_one.number {
|
||||
orig_header_one.ancestor = header_one.hash;
|
||||
backend.insert_header_metadata(orig_header_one.hash, orig_header_one);
|
||||
}
|
||||
|
||||
if orig_header_two.number > header_one.number {
|
||||
orig_header_two.ancestor = header_one.hash;
|
||||
backend.insert_header_metadata(orig_header_two.hash, orig_header_two);
|
||||
}
|
||||
|
||||
Ok(HashAndNumber { hash: header_one.hash, number: header_one.number })
|
||||
}
|
||||
|
||||
/// Compute a tree-route between two blocks. See tree-route docs for more details.
|
||||
pub fn tree_route<Block: BlockT, T: HeaderMetadata<Block> + ?Sized>(
|
||||
backend: &T,
|
||||
from: Block::Hash,
|
||||
to: Block::Hash,
|
||||
) -> Result<TreeRoute<Block>, T::Error> {
|
||||
let mut from = backend.header_metadata(from)?;
|
||||
let mut to = backend.header_metadata(to)?;
|
||||
|
||||
let mut to_branch =
|
||||
Vec::with_capacity(Into::<U256>::into(to.number.saturating_sub(from.number)).as_usize());
|
||||
while to.number > from.number {
|
||||
to_branch.push(HashAndNumber { number: to.number, hash: to.hash });
|
||||
|
||||
to = backend.header_metadata(to.parent)?;
|
||||
}
|
||||
|
||||
let mut from_branch =
|
||||
Vec::with_capacity(Into::<U256>::into(to.number.saturating_sub(from.number)).as_usize());
|
||||
while from.number > to.number {
|
||||
from_branch.push(HashAndNumber { number: from.number, hash: from.hash });
|
||||
from = backend.header_metadata(from.parent)?;
|
||||
}
|
||||
|
||||
// numbers are equal now. walk backwards until the block is the same
|
||||
|
||||
while to.hash != from.hash {
|
||||
to_branch.push(HashAndNumber { number: to.number, hash: to.hash });
|
||||
to = backend.header_metadata(to.parent)?;
|
||||
|
||||
from_branch.push(HashAndNumber { number: from.number, hash: from.hash });
|
||||
from = backend.header_metadata(from.parent)?;
|
||||
}
|
||||
|
||||
// add the pivot block. and append the reversed to-branch
|
||||
// (note that it's reverse order originals)
|
||||
let pivot = from_branch.len();
|
||||
from_branch.reserve_exact(to_branch.len() + 1);
|
||||
from_branch.push(HashAndNumber { number: to.number, hash: to.hash });
|
||||
from_branch.extend(to_branch.into_iter().rev());
|
||||
|
||||
Ok(TreeRoute { route: from_branch, pivot })
|
||||
}
|
||||
|
||||
/// Hash and number of a block.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HashAndNumber<Block: BlockT> {
|
||||
/// The number of the block.
|
||||
pub number: NumberFor<Block>,
|
||||
/// The hash of the block.
|
||||
pub hash: Block::Hash,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Eq for HashAndNumber<Block> {}
|
||||
|
||||
impl<Block: BlockT> PartialEq for HashAndNumber<Block> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.number.eq(&other.number) && self.hash.eq(&other.hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Ord for HashAndNumber<Block> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self.number.cmp(&other.number) {
|
||||
std::cmp::Ordering::Equal => self.hash.cmp(&other.hash),
|
||||
result => result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> PartialOrd for HashAndNumber<Block> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(&other))
|
||||
}
|
||||
}
|
||||
|
||||
/// A tree-route from one block to another in the chain.
|
||||
///
|
||||
/// All blocks prior to the pivot in the vector is the reverse-order unique ancestry
|
||||
/// of the first block, the block at the pivot index is the common ancestor,
|
||||
/// and all blocks after the pivot is the ancestry of the second block, in
|
||||
/// order.
|
||||
///
|
||||
/// The ancestry sets will include the given blocks, and thus the tree-route is
|
||||
/// never empty.
|
||||
///
|
||||
/// ```text
|
||||
/// Tree route from R1 to E2. Retracted is [R1, R2, R3], Common is C, enacted [E1, E2]
|
||||
/// <- R3 <- R2 <- R1
|
||||
/// /
|
||||
/// C
|
||||
/// \-> E1 -> E2
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// Tree route from C to E2. Retracted empty. Common is C, enacted [E1, E2]
|
||||
/// C -> E1 -> E2
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TreeRoute<Block: BlockT> {
|
||||
route: Vec<HashAndNumber<Block>>,
|
||||
pivot: usize,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> TreeRoute<Block> {
|
||||
/// Creates a new `TreeRoute`.
|
||||
///
|
||||
/// To preserve the structure safety invariants it is required that `pivot < route.len()`.
|
||||
pub fn new(route: Vec<HashAndNumber<Block>>, pivot: usize) -> Result<Self, String> {
|
||||
if pivot < route.len() {
|
||||
Ok(TreeRoute { route, pivot })
|
||||
} else {
|
||||
Err(format!(
|
||||
"TreeRoute pivot ({}) should be less than route length ({})",
|
||||
pivot,
|
||||
route.len()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a slice of all retracted blocks in reverse order (towards common ancestor).
|
||||
pub fn retracted(&self) -> &[HashAndNumber<Block>] {
|
||||
&self.route[..self.pivot]
|
||||
}
|
||||
|
||||
/// Convert into all retracted blocks in reverse order (towards common ancestor).
|
||||
pub fn into_retracted(mut self) -> Vec<HashAndNumber<Block>> {
|
||||
self.route.truncate(self.pivot);
|
||||
self.route
|
||||
}
|
||||
|
||||
/// Get the common ancestor block. This might be one of the two blocks of the
|
||||
/// route.
|
||||
pub fn common_block(&self) -> &HashAndNumber<Block> {
|
||||
self.route.get(self.pivot).expect(
|
||||
"tree-routes are computed between blocks; \
|
||||
which are included in the route; \
|
||||
thus it is never empty; qed",
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a slice of enacted blocks (descendants of the common ancestor)
|
||||
pub fn enacted(&self) -> &[HashAndNumber<Block>] {
|
||||
&self.route[self.pivot + 1..]
|
||||
}
|
||||
|
||||
/// Returns the last block.
|
||||
pub fn last(&self) -> Option<&HashAndNumber<Block>> {
|
||||
self.route.last()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles header metadata: hash, number, parent hash, etc.
|
||||
pub trait HeaderMetadata<Block: BlockT> {
|
||||
/// Error used in case the header metadata is not found.
|
||||
type Error: std::error::Error;
|
||||
|
||||
fn header_metadata(
|
||||
&self,
|
||||
hash: Block::Hash,
|
||||
) -> Result<CachedHeaderMetadata<Block>, Self::Error>;
|
||||
fn insert_header_metadata(
|
||||
&self,
|
||||
hash: Block::Hash,
|
||||
header_metadata: CachedHeaderMetadata<Block>,
|
||||
);
|
||||
fn remove_header_metadata(&self, hash: Block::Hash);
|
||||
}
|
||||
|
||||
/// Caches header metadata in an in-memory LRU cache.
|
||||
pub struct HeaderMetadataCache<Block: BlockT> {
|
||||
cache: Mutex<LruMap<Block::Hash, CachedHeaderMetadata<Block>>>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadataCache<Block> {
|
||||
/// Creates a new LRU header metadata cache with `capacity`.
|
||||
pub fn new(capacity: u32) -> Self {
|
||||
HeaderMetadataCache { cache: Mutex::new(LruMap::new(ByLength::new(capacity))) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Default for HeaderMetadataCache<Block> {
|
||||
fn default() -> Self {
|
||||
Self::new(LRU_CACHE_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadataCache<Block> {
|
||||
pub fn header_metadata(&self, hash: Block::Hash) -> Option<CachedHeaderMetadata<Block>> {
|
||||
self.cache.lock().get(&hash).cloned()
|
||||
}
|
||||
|
||||
pub fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
||||
self.cache.lock().insert(hash, metadata);
|
||||
}
|
||||
|
||||
pub fn remove_header_metadata(&self, hash: Block::Hash) {
|
||||
self.cache.lock().remove(&hash);
|
||||
}
|
||||
}
|
||||
|
||||
/// Cached header metadata. Used to efficiently traverse the tree.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CachedHeaderMetadata<Block: BlockT> {
|
||||
/// Hash of the header.
|
||||
pub hash: Block::Hash,
|
||||
/// Block number.
|
||||
pub number: NumberFor<Block>,
|
||||
/// Hash of parent header.
|
||||
pub parent: Block::Hash,
|
||||
/// Block state root.
|
||||
pub state_root: Block::Hash,
|
||||
/// Hash of an ancestor header. Used to jump through the tree.
|
||||
ancestor: Block::Hash,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> From<&Block::Header> for CachedHeaderMetadata<Block> {
|
||||
fn from(header: &Block::Header) -> Self {
|
||||
CachedHeaderMetadata {
|
||||
hash: header.hash(),
|
||||
number: *header.number(),
|
||||
parent: *header.parent_hash(),
|
||||
state_root: *header.state_root(),
|
||||
ancestor: *header.parent_hash(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Bizinikiwi blockchain traits and primitives.
|
||||
|
||||
mod backend;
|
||||
mod error;
|
||||
mod header_metadata;
|
||||
|
||||
pub use backend::*;
|
||||
pub use error::*;
|
||||
pub use header_metadata::*;
|
||||
|
||||
const LOG_TARGET: &str = "db::blockchain";
|
||||
Reference in New Issue
Block a user