// Copyright 2017 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 . //! Substrate blockchain trait use primitives::AuthorityId; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use runtime_primitives::generic::BlockId; use runtime_primitives::bft::Justification; use error::{ErrorKind, Result}; /// Blockchain database header backend. Does not perform any validation. pub trait HeaderBackend: Send + Sync { /// Get block header. Returns `None` if block is not found. fn header(&self, id: BlockId) -> Result>; /// Get blockchain info. fn info(&self) -> Result>; /// Get block status. fn status(&self, id: BlockId) -> Result; /// Get block number by hash. Returns `None` if the header is not in the chain. fn number(&self, hash: Block::Hash) -> Result::Header as HeaderT>::Number>>; /// Get block hash by number. Returns `None` if the header is not in the chain. fn hash(&self, number: NumberFor) -> Result>; /// Get block header. Returns `UnknownBlock` error if block is not found. fn expect_header(&self, id: BlockId) -> Result { self.header(id)?.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into()) } } /// Blockchain database backend. Does not perform any validation. pub trait Backend: HeaderBackend { /// Get block body. Returns `None` if block is not found. fn body(&self, id: BlockId) -> Result::Extrinsic>>>; /// Get block justification. Returns `None` if justification does not exist. fn justification(&self, id: BlockId) -> Result>>; /// Get last finalized block hash. fn last_finalized(&self) -> Result; /// Returns data cache reference, if it is enabled on this backend. fn cache(&self) -> Option<&Cache>; /// 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, heighest) chain first. fn leaves(&self) -> Result>; } /// Blockchain optional data cache. pub trait Cache: Send + Sync { /// Returns the set of authorities, that was active at given block or None if there's no entry in the cache. fn authorities_at(&self, block: BlockId) -> Option>; } /// Block import outcome pub enum ImportResult { /// Imported successfully. Imported, /// Block already exists, skippped. AlreadyInChain, /// Unknown parent. UnknownParent, /// Other errror. Err(E), } /// Blockchain info #[derive(Debug)] pub struct Info { /// Best block hash. pub best_hash: Block::Hash, /// Best block number. pub best_number: <::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: <::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, } /// An entry in a tree route. #[derive(Debug)] pub struct RouteEntry { /// The number of the block. pub number: ::Number, /// 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. /// /// ```ignore /// Tree route from R1 to E2. Retracted is [R1, R2, R3], Common is C, enacted [E1, E2] /// <- R3 <- R2 <- R1 /// / /// C /// \-> E1 -> E2 /// ``` /// /// ```ignore /// Tree route from C to E2. Retracted empty. Common is C, enacted [E1, E2] /// C -> E1 -> E2 /// ``` #[derive(Debug)] pub struct TreeRoute { route: Vec>, pivot: usize, } impl TreeRoute { /// Get a slice of all retracted blocks in reverse order (towards common ancestor) pub fn retracted(&self) -> &[RouteEntry] { &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) -> &RouteEntry { 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) -> &[RouteEntry] { &self.route[self.pivot + 1 ..] } } /// Compute a tree-route between two blocks. See tree-route docs for more details. pub fn tree_route>( backend: &Backend, from: BlockId, to: BlockId, ) -> Result> { use runtime_primitives::traits::Header; let load_header = |id: BlockId| { match backend.header(id) { Ok(Some(hdr)) => Ok(hdr), Ok(None) => Err(ErrorKind::UnknownBlock(format!("Unknown block {:?}", id)).into()), Err(e) => Err(e), } }; let mut from = load_header(from)?; let mut to = load_header(to)?; let mut from_branch = Vec::new(); let mut to_branch = Vec::new(); while to.number() > from.number() { to_branch.push(RouteEntry { number: to.number().clone(), hash: to.hash(), }); to = load_header(BlockId::Hash(*to.parent_hash()))?; } while from.number() > to.number() { from_branch.push(RouteEntry { number: from.number().clone(), hash: from.hash(), }); from = load_header(BlockId::Hash(*from.parent_hash()))?; } // numbers are equal now. walk backwards until the block is the same while to != from { to_branch.push(RouteEntry { number: to.number().clone(), hash: to.hash(), }); to = load_header(BlockId::Hash(*to.parent_hash()))?; from_branch.push(RouteEntry { number: from.number().clone(), hash: from.hash(), }); from = load_header(BlockId::Hash(*from.parent_hash()))?; } // add the pivot block. and append the reversed to-branch (note that it's reverse order originalls) let pivot = from_branch.len(); from_branch.push(RouteEntry { number: to.number().clone(), hash: to.hash(), }); from_branch.extend(to_branch.into_iter().rev()); Ok(TreeRoute { route: from_branch, pivot, }) }