mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 01:41:09 +00:00
Remove all (non-dev) client references from frame, activate dependency enforcer (#4184)
* Move transaction pool to primitives * move backend, errors into primitives * remove unused client depencies * Move rpc-api into primitives * Move peerset back to client * Move rpc/api back to client, move palette/support/rpc into utils * move support-rpc into subfolder * move system-rpc into utils * move transaction-pool and -graph back into client * fix broken imports * Clean up test primitives * Make support test utils independent of frame * remove unnecessary node dependencies from service * Reactivate dependency script: - only enforce the now achieved status quo will remain - allow for primitives to depend on /client for now without failing - more discriptive error message so people understand, what it wants - minor fix to differentiative between ../client and /client (which may be a subfolder) - don't allow this to fail anylonger. * fix doc comment * 'Should not' rather than 'must not'. * Revert unwanted dependency changes * fix faulty import * fixup derive_more version * fix wrong import path
This commit is contained in:
committed by
GitHub
parent
b2aab98e69
commit
bd652793db
@@ -0,0 +1,275 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate blockchain trait
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
||||
use sr_primitives::generic::BlockId;
|
||||
use sr_primitives::Justification;
|
||||
use log::warn;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::header_metadata::HeaderMetadata;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
/// 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, id: BlockId<Block>) -> Result<Option<Block::Header>>;
|
||||
/// Get blockchain info.
|
||||
fn info(&self) -> Info<Block>;
|
||||
/// Get block status.
|
||||
fn status(&self, id: BlockId<Block>) -> 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(_) => Ok(self.header(*id)?.map(|h| h.number().clone())),
|
||||
BlockId::Number(n) => Ok(Some(n)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get block header. Returns `UnknownBlock` error if block is not found.
|
||||
fn expect_header(&self, id: BlockId<Block>) -> Result<Block::Header> {
|
||||
self.header(id)?.ok_or_else(|| Error::UnknownBlock(format!("{}", id)))
|
||||
}
|
||||
|
||||
/// 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!("{}", 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(|n| n.ok_or_else(|| Error::UnknownBlock(format!("{}", id))))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
|
||||
/// Get block justification. Returns `None` if justification does not exist.
|
||||
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>>;
|
||||
/// Get last finalized block hash.
|
||||
fn last_finalized(&self) -> Result<Block::Hash>;
|
||||
/// Returns data cache reference, if it is enabled on this backend.
|
||||
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
|
||||
|
||||
/// 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 best (longest) chains
|
||||
/// that contain block with the given `target_hash`.
|
||||
///
|
||||
/// The search space is always limited to blocks which are in the finalized
|
||||
/// chain or descendents of it.
|
||||
///
|
||||
/// If `maybe_max_block_number` is `Some(max_block_number)`
|
||||
/// the search is limited to block `numbers <= max_block_number`.
|
||||
/// in other words as if there were no blocks greater `max_block_number`.
|
||||
/// Returns `Ok(None)` if `target_hash` is not found in search space.
|
||||
/// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444)
|
||||
fn best_containing(
|
||||
&self,
|
||||
target_hash: Block::Hash,
|
||||
maybe_max_number: Option<NumberFor<Block>>,
|
||||
import_lock: &RwLock<()>,
|
||||
) -> Result<Option<Block::Hash>> {
|
||||
let target_header = {
|
||||
match self.header(BlockId::Hash(target_hash))? {
|
||||
Some(x) => x,
|
||||
// target not in blockchain
|
||||
None => { return Ok(None); },
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
// target outside search range
|
||||
if target_header.number() > &max_number {
|
||||
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();
|
||||
|
||||
// this can be `None` if the best chain is shorter than the target header.
|
||||
let maybe_canon_hash = self.hash(*target_header.number())?;
|
||||
|
||||
if maybe_canon_hash.as_ref() == Some(&target_hash) {
|
||||
// if a `max_number` is given we try to fetch the block at the
|
||||
// given depth, if it doesn't exist or `max_number` is not
|
||||
// provided, we continue to search from all leaves below.
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
if let Some(header) = self.hash(max_number)? {
|
||||
return Ok(Some(header));
|
||||
}
|
||||
}
|
||||
} else if info.finalized_number >= *target_header.number() {
|
||||
// header is on a dead fork.
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
self.leaves()?
|
||||
};
|
||||
|
||||
// for each chain. longest chain first. shortest last
|
||||
for leaf_hash in leaves {
|
||||
// start at the leaf
|
||||
let mut current_hash = leaf_hash;
|
||||
|
||||
// if search is not restricted then the leaf is the best
|
||||
let mut best_hash = leaf_hash;
|
||||
|
||||
// go backwards entering the search space
|
||||
// waiting until we are <= max_number
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
loop {
|
||||
let current_header = self.header(BlockId::Hash(current_hash.clone()))?
|
||||
.ok_or_else(|| Error::from(format!("failed to get header for hash {}", current_hash)))?;
|
||||
|
||||
if current_header.number() <= &max_number {
|
||||
best_hash = current_header.hash();
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
}
|
||||
}
|
||||
|
||||
// go backwards through the chain (via parent links)
|
||||
loop {
|
||||
// until we find target
|
||||
if current_hash == target_hash {
|
||||
return Ok(Some(best_hash));
|
||||
}
|
||||
|
||||
let current_header = self.header(BlockId::Hash(current_hash.clone()))?
|
||||
.ok_or_else(|| Error::from(format!("failed to get header for hash {}", current_hash)))?;
|
||||
|
||||
// stop search in this chain once we go below the target's block number
|
||||
if current_header.number() < target_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!(
|
||||
"Block {:?} exists in chain but not found when following all \
|
||||
leaves backwards. Number limit = {:?}",
|
||||
target_hash,
|
||||
maybe_max_number,
|
||||
);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides access to the optional cache.
|
||||
pub trait ProvideCache<Block: BlockT> {
|
||||
/// Returns data cache reference, if it is enabled on this backend.
|
||||
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
|
||||
}
|
||||
|
||||
/// Blockchain optional data cache.
|
||||
pub trait Cache<Block: BlockT>: Send + Sync {
|
||||
/// Initialize genesis value for the given cache.
|
||||
///
|
||||
/// The operation should be performed once before anything else is inserted in the cache.
|
||||
/// Otherwise cache may end up in inconsistent state.
|
||||
fn initialize(&self, key: &well_known_cache_keys::Id, value_at_genesis: Vec<u8>) -> Result<()>;
|
||||
/// Returns cached value by the given key.
|
||||
///
|
||||
/// Returned tuple is the range where value has been active and the value itself.
|
||||
fn get_at(
|
||||
&self,
|
||||
key: &well_known_cache_keys::Id,
|
||||
block: &BlockId<Block>,
|
||||
) -> Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>;
|
||||
}
|
||||
|
||||
/// Blockchain info
|
||||
#[derive(Debug)]
|
||||
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,
|
||||
}
|
||||
|
||||
/// Block status.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum BlockStatus {
|
||||
/// Already in the blockchain.
|
||||
InChain,
|
||||
/// Not in the queue or the blockchain.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// A list of all well known keys in the blockchain cache.
|
||||
pub mod well_known_cache_keys {
|
||||
/// The type representing cache keys.
|
||||
pub type Id = sp_consensus::import_queue::CacheKeyId;
|
||||
|
||||
/// A list of authorities.
|
||||
pub const AUTHORITIES: Id = *b"auth";
|
||||
|
||||
/// Current Epoch data.
|
||||
pub const EPOCH: Id = *b"epch";
|
||||
|
||||
/// Changes trie configuration.
|
||||
pub const CHANGES_TRIE_CONFIG: Id = *b"chtr";
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate client possible errors.
|
||||
|
||||
use std::{self, error, result};
|
||||
use sp_state_machine;
|
||||
use sr_primitives::transaction_validity::TransactionValidityError;
|
||||
#[allow(deprecated)]
|
||||
use sp_block_builder_runtime_api::compatability_v3;
|
||||
use sp_consensus;
|
||||
use derive_more::{Display, From};
|
||||
use parity_scale_codec::Error as CodecError;
|
||||
|
||||
/// Client Result type alias
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
||||
/// Error when the runtime failed to apply an extrinsic.
|
||||
#[derive(Debug, Display)]
|
||||
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
|
||||
/// unappliable onto the current block.
|
||||
#[display(fmt = "Extrinsic is not valid: {:?}", _0)]
|
||||
Validity(TransactionValidityError),
|
||||
/// This is used for miscelanious errors that can be represented by string and not handleable.
|
||||
///
|
||||
/// This will become obsolete with complete migration to v4 APIs.
|
||||
#[display(fmt = "Extrinsic failed: {:?}", _0)]
|
||||
Msg(String),
|
||||
}
|
||||
|
||||
/// Substrate Client error
|
||||
#[derive(Debug, Display, From)]
|
||||
pub enum Error {
|
||||
/// Consensus Error
|
||||
#[display(fmt = "Consensus: {}", _0)]
|
||||
Consensus(sp_consensus::Error),
|
||||
/// Backend error.
|
||||
#[display(fmt = "Backend error: {}", _0)]
|
||||
#[from(ignore)]
|
||||
Backend(String),
|
||||
/// Unknown block.
|
||||
#[display(fmt = "UnknownBlock: {}", _0)]
|
||||
#[from(ignore)]
|
||||
UnknownBlock(String),
|
||||
/// The `apply_extrinsic` is not valid due to the given `TransactionValidityError`.
|
||||
#[display(fmt = "{:?}", _0)]
|
||||
ApplyExtrinsicFailed(ApplyExtrinsicFailed),
|
||||
/// Execution error.
|
||||
#[display(fmt = "Execution: {}", _0)]
|
||||
Execution(Box<dyn sp_state_machine::Error>),
|
||||
/// Blockchain error.
|
||||
#[display(fmt = "Blockchain: {}", _0)]
|
||||
Blockchain(Box<Error>),
|
||||
/// Invalid authorities set received from the runtime.
|
||||
#[display(fmt = "Current state of blockchain has invalid authorities set")]
|
||||
InvalidAuthoritiesSet,
|
||||
/// Could not get runtime version.
|
||||
#[display(fmt = "On-chain runtime does not specify version")]
|
||||
VersionInvalid,
|
||||
/// Genesis config is invalid.
|
||||
#[display(fmt = "Genesis config provided is invalid")]
|
||||
GenesisInvalid,
|
||||
/// Error decoding header justification.
|
||||
#[display(fmt = "error decoding justification for header")]
|
||||
JustificationDecode,
|
||||
/// Justification for header is correctly encoded, but invalid.
|
||||
#[display(fmt = "bad justification for header: {}", _0)]
|
||||
#[from(ignore)]
|
||||
BadJustification(String),
|
||||
/// Not available on light client.
|
||||
#[display(fmt = "This method is not currently available when running in light client mode")]
|
||||
NotAvailableOnLightClient,
|
||||
/// Invalid remote CHT-based proof.
|
||||
#[display(fmt = "Remote node has responded with invalid header proof")]
|
||||
InvalidCHTProof,
|
||||
/// Remote fetch has been cancelled.
|
||||
#[display(fmt = "Remote data fetch has been cancelled")]
|
||||
RemoteFetchCancelled,
|
||||
/// Remote fetch has been failed.
|
||||
#[display(fmt = "Remote data fetch has been failed")]
|
||||
RemoteFetchFailed,
|
||||
/// Error decoding call result.
|
||||
#[display(fmt = "Error decoding call result of {}: {}", _0, _1)]
|
||||
CallResultDecode(&'static str, CodecError),
|
||||
/// Error converting a parameter between runtime and node.
|
||||
#[display(fmt = "Error converting `{}` between runtime and node", _0)]
|
||||
#[from(ignore)]
|
||||
RuntimeParamConversion(String),
|
||||
/// Changes tries are not supported.
|
||||
#[display(fmt = "Changes tries are not supported by the runtime")]
|
||||
ChangesTriesNotSupported,
|
||||
/// Key changes query has failed.
|
||||
#[display(fmt = "Failed to check changes proof: {}", _0)]
|
||||
#[from(ignore)]
|
||||
ChangesTrieAccessFailed(String),
|
||||
/// Last finalized block not parent of current.
|
||||
#[display(fmt = "Did not finalize blocks in sequential order.")]
|
||||
#[from(ignore)]
|
||||
NonSequentialFinalization(String),
|
||||
/// Safety violation: new best block not descendent of last finalized.
|
||||
#[display(fmt = "Potential long-range attack: block not in finalized chain.")]
|
||||
NotInFinalizedChain,
|
||||
/// Hash that is required for building CHT is missing.
|
||||
#[display(fmt = "Failed to get hash of block for building CHT")]
|
||||
MissingHashRequiredForCHT,
|
||||
/// Invalid calculated state root on block import.
|
||||
#[display(fmt = "Calculated state root does not match.")]
|
||||
InvalidStateRoot,
|
||||
/// A convenience variant for String
|
||||
#[display(fmt = "{}", _0)]
|
||||
#[from(ignore)]
|
||||
Msg(String),
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Error::Consensus(e) => Some(e),
|
||||
Error::Blockchain(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Self {
|
||||
Error::Msg(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Error {
|
||||
fn from(s: &'a str) -> Self {
|
||||
Error::Msg(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl From<compatability_v3::ApplyError> for ApplyExtrinsicFailed {
|
||||
fn from(e: compatability_v3::ApplyError) -> Self {
|
||||
use self::compatability_v3::ApplyError::*;
|
||||
match e {
|
||||
Validity(tx_validity) => Self::Validity(tx_validity),
|
||||
e => Self::Msg(format!("Apply extrinsic failed: {:?}", 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 sp_state_machine::Error>) -> Self {
|
||||
Error::Execution(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implements tree backend, cached header metadata and algorithms
|
||||
//! to compute routes efficiently over the tree of headers.
|
||||
|
||||
use sr_primitives::traits::{Block as BlockT, NumberFor, Header};
|
||||
use parking_lot::RwLock;
|
||||
use lru::LruCache;
|
||||
|
||||
/// Set to the expected max difference between `best` and `finalized` blocks at sync.
|
||||
const LRU_CACHE_SIZE: usize = 5_000;
|
||||
|
||||
/// Get 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>>(
|
||||
backend: &T,
|
||||
id_one: Block::Hash,
|
||||
id_two: Block::Hash,
|
||||
) -> Result<HashAndNumber<Block>, T::Error> {
|
||||
let mut header_one = backend.header_metadata(id_one)?;
|
||||
let mut header_two = backend.header_metadata(id_two)?;
|
||||
|
||||
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>>(
|
||||
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 from_branch = Vec::new();
|
||||
let mut to_branch = Vec::new();
|
||||
|
||||
while to.number > from.number {
|
||||
to_branch.push(HashAndNumber {
|
||||
number: to.number,
|
||||
hash: to.hash,
|
||||
});
|
||||
|
||||
to = backend.header_metadata(to.parent)?;
|
||||
}
|
||||
|
||||
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.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)]
|
||||
pub struct HashAndNumber<Block: BlockT> {
|
||||
/// The number of the block.
|
||||
pub number: NumberFor<Block>,
|
||||
/// The hash of the block.
|
||||
pub hash: Block::Hash,
|
||||
}
|
||||
|
||||
/// A tree-route from one block to another in the chain.
|
||||
///
|
||||
/// All blocks prior to the pivot in the deque 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)]
|
||||
pub struct TreeRoute<Block: BlockT> {
|
||||
route: Vec<HashAndNumber<Block>>,
|
||||
pivot: usize,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> TreeRoute<Block> {
|
||||
/// Get a slice of all retracted blocks in reverse order (towards common ancestor)
|
||||
pub fn retracted(&self) -> &[HashAndNumber<Block>] {
|
||||
&self.route[..self.pivot]
|
||||
}
|
||||
|
||||
/// 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 (descendents of the common ancestor)
|
||||
pub fn enacted(&self) -> &[HashAndNumber<Block>] {
|
||||
&self.route[self.pivot + 1 ..]
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
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: RwLock<LruCache<Block::Hash, CachedHeaderMetadata<Block>>>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadataCache<Block> {
|
||||
/// Creates a new LRU header metadata cache with `capacity`.
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
HeaderMetadataCache {
|
||||
cache: RwLock::new(LruCache::new(capacity)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Default for HeaderMetadataCache<Block> {
|
||||
fn default() -> Self {
|
||||
HeaderMetadataCache {
|
||||
cache: RwLock::new(LruCache::new(LRU_CACHE_SIZE)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadata<Block> for HeaderMetadataCache<Block> {
|
||||
type Error = String;
|
||||
|
||||
fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
||||
self.cache.write().get(&hash).cloned()
|
||||
.ok_or("header metadata not found in cache".to_owned())
|
||||
}
|
||||
|
||||
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
||||
self.cache.write().put(hash, metadata);
|
||||
}
|
||||
|
||||
fn remove_header_metadata(&self, hash: Block::Hash) {
|
||||
self.cache.write().pop(&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,
|
||||
/// 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().clone(),
|
||||
number: header.number().clone(),
|
||||
parent: header.parent_hash().clone(),
|
||||
ancestor: header.parent_hash().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate blockchain traits and primtives
|
||||
|
||||
mod backend;
|
||||
mod header_metadata;
|
||||
mod error;
|
||||
|
||||
pub use error::*;
|
||||
pub use backend::*;
|
||||
pub use header_metadata::*;
|
||||
Reference in New Issue
Block a user