diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 47386ba2d4..fbcd505887 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -4653,6 +4653,18 @@ dependencies = [ "sp-keyring", ] +[[package]] +name = "polkadot-node-core-chain-api" +version = "0.1.0" +dependencies = [ + "futures 0.3.5", + "maplit", + "polkadot-node-subsystem", + "polkadot-primitives", + "sp-blockchain", + "sp-core", +] + [[package]] name = "polkadot-node-core-proposer" version = "0.1.0" @@ -4683,7 +4695,6 @@ dependencies = [ name = "polkadot-node-core-runtime-api" version = "0.1.0" dependencies = [ - "assert_matches", "futures 0.3.5", "polkadot-node-primitives", "polkadot-node-subsystem", diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index fa56b0ec42..7fb7d7d535 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -47,6 +47,7 @@ members = [ "node/core/backing", "node/core/bitfield-signing", "node/core/candidate-validation", + "node/core/chain-api", "node/core/proposer", "node/core/runtime-api", "node/network/bridge", diff --git a/polkadot/node/core/bitfield-signing/src/lib.rs b/polkadot/node/core/bitfield-signing/src/lib.rs index e3bb68cec9..4e409a2576 100644 --- a/polkadot/node/core/bitfield-signing/src/lib.rs +++ b/polkadot/node/core/bitfield-signing/src/lib.rs @@ -26,8 +26,9 @@ use keystore::KeyStorePtr; use polkadot_node_subsystem::{ messages::{ self, AllMessages, AvailabilityStoreMessage, BitfieldDistributionMessage, - BitfieldSigningMessage, CandidateBackingMessage, RuntimeApiMessage, RuntimeApiError + BitfieldSigningMessage, CandidateBackingMessage, RuntimeApiMessage, }, + errors::RuntimeApiError, util::{self, JobManager, JobTrait, ToJobTrait, Validator}, }; use polkadot_primitives::v1::{AvailabilityBitfield, CoreState, Hash, ValidatorIndex}; diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index c040090faa..e81f41180d 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -26,8 +26,8 @@ use polkadot_subsystem::{ }; use polkadot_subsystem::messages::{ AllMessages, CandidateValidationMessage, RuntimeApiMessage, ValidationFailed, RuntimeApiRequest, - RuntimeApiError, }; +use polkadot_subsystem::errors::RuntimeApiError; use polkadot_node_primitives::{ValidationResult, ValidationOutputs}; use polkadot_primitives::v1::{ ValidationCode, OmittedValidationData, PoV, CandidateDescriptor, LocalValidationData, diff --git a/polkadot/node/core/chain-api/Cargo.toml b/polkadot/node/core/chain-api/Cargo.toml new file mode 100644 index 0000000000..61ca313e33 --- /dev/null +++ b/polkadot/node/core/chain-api/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "polkadot-node-core-chain-api" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +futures = { version = "0.3.5" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } +polkadot-primitives = { path = "../../../primitives" } +polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } + +[dev-dependencies] +futures = { version = "0.3.5", features = ["thread-pool"] } +maplit = "1.0.2" +polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem", features = ["test-helpers"] } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/node/core/chain-api/src/lib.rs b/polkadot/node/core/chain-api/src/lib.rs new file mode 100644 index 0000000000..9af759aab7 --- /dev/null +++ b/polkadot/node/core/chain-api/src/lib.rs @@ -0,0 +1,338 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Implements the Chain API Subsystem +//! +//! Provides access to the chain data. Every request may return an error. +//! At the moment, the implementation requires `Client` to implement `HeaderBackend`, +//! we may add more bounds in the future if we will need e.g. block bodies. +//! +//! Supported requests: +//! * Block hash to number +//! * Finalized block number to hash +//! * Last finalized block number +//! * Ancestors + +use polkadot_subsystem::{ + FromOverseer, OverseerSignal, + SpawnedSubsystem, Subsystem, SubsystemResult, SubsystemContext, + messages::ChainApiMessage, +}; +use polkadot_primitives::v1::{Block, BlockId}; +use sp_blockchain::HeaderBackend; + +use futures::prelude::*; + +/// The Chain API Subsystem implementation. +pub struct ChainApiSubsystem { + client: Client, +} + +impl ChainApiSubsystem { + /// Create a new Chain API subsystem with the given client. + pub fn new(client: Client) -> Self { + ChainApiSubsystem { + client + } + } +} + +impl Subsystem for ChainApiSubsystem where + Client: HeaderBackend + 'static, + Context: SubsystemContext +{ + fn start(self, ctx: Context) -> SpawnedSubsystem { + SpawnedSubsystem { + future: run(ctx, self.client).map(|_| ()).boxed(), + name: "chain-api-subsystem", + } + } +} + +async fn run( + mut ctx: impl SubsystemContext, + client: Client, +) -> SubsystemResult<()> +where + Client: HeaderBackend, +{ + loop { + match ctx.recv().await? { + FromOverseer::Signal(OverseerSignal::Conclude) => return Ok(()), + FromOverseer::Signal(OverseerSignal::ActiveLeaves(_)) => {}, + FromOverseer::Signal(OverseerSignal::BlockFinalized(_)) => {}, + FromOverseer::Communication { msg } => match msg { + ChainApiMessage::BlockNumber(hash, response_channel) => { + let result = client.number(hash).map_err(|e| e.to_string().into()); + let _ = response_channel.send(result); + }, + ChainApiMessage::FinalizedBlockHash(number, response_channel) => { + // Note: we don't verify it's finalized + let result = client.hash(number).map_err(|e| e.to_string().into()); + let _ = response_channel.send(result); + }, + ChainApiMessage::FinalizedBlockNumber(response_channel) => { + let result = client.info().finalized_number; + let _ = response_channel.send(Ok(result)); + }, + ChainApiMessage::Ancestors { hash, k, response_channel } => { + let mut hash = hash; + + let next_parent = core::iter::from_fn(|| { + let maybe_header = client.header(BlockId::Hash(hash)); + match maybe_header { + // propagate the error + Err(e) => Some(Err(e.to_string().into())), + // fewer than `k` ancestors are available + Ok(None) => None, + Ok(Some(header)) => { + hash = header.parent_hash; + Some(Ok(hash)) + } + } + }); + + let result = next_parent.take(k).collect::, _>>(); + let _ = response_channel.send(result); + }, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::collections::BTreeMap; + use futures::{future::BoxFuture, channel::oneshot}; + + use polkadot_primitives::v1::{Hash, BlockNumber, BlockId, Header}; + use polkadot_subsystem::test_helpers::{make_subsystem_context, TestSubsystemContextHandle}; + use sp_blockchain::Info as BlockInfo; + use sp_core::testing::TaskExecutor; + + #[derive(Clone)] + struct TestClient { + blocks: BTreeMap, + finalized_blocks: BTreeMap, + headers: BTreeMap, + } + + const ONE: Hash = Hash::repeat_byte(0x01); + const TWO: Hash = Hash::repeat_byte(0x02); + const THREE: Hash = Hash::repeat_byte(0x03); + const FOUR: Hash = Hash::repeat_byte(0x04); + const ERROR_PATH: Hash = Hash::repeat_byte(0xFF); + + fn default_header() -> Header { + Header { + parent_hash: Hash::zero(), + number: 100500, + state_root: Hash::zero(), + extrinsics_root: Hash::zero(), + digest: Default::default(), + } + } + + impl Default for TestClient { + fn default() -> Self { + Self { + blocks: maplit::btreemap! { + ONE => 1, + TWO => 2, + THREE => 3, + FOUR => 4, + }, + finalized_blocks: maplit::btreemap! { + 1 => ONE, + 3 => THREE, + }, + headers: maplit::btreemap! { + TWO => Header { + parent_hash: ONE, + number: 2, + ..default_header() + }, + THREE => Header { + parent_hash: TWO, + number: 3, + ..default_header() + }, + FOUR => Header { + parent_hash: THREE, + number: 4, + ..default_header() + }, + ERROR_PATH => Header { + ..default_header() + } + } + } + } + } + + fn last_key_value(map: &BTreeMap) -> (K, V) { + assert!(!map.is_empty()); + map.iter() + .last() + .map(|(k, v)| (k.clone(), v.clone())) + .unwrap() + } + + impl HeaderBackend for TestClient { + fn info(&self) -> BlockInfo { + let genesis_hash = self.blocks.iter().next().map(|(h, _)| *h).unwrap(); + let (best_hash, best_number) = last_key_value(&self.blocks); + let (finalized_number, finalized_hash) = last_key_value(&self.finalized_blocks); + + BlockInfo { + best_hash, + best_number, + genesis_hash, + finalized_hash, + finalized_number, + number_leaves: 0, + } + } + fn number(&self, hash: Hash) -> sp_blockchain::Result> { + Ok(self.blocks.get(&hash).copied()) + } + fn hash(&self, number: BlockNumber) -> sp_blockchain::Result> { + Ok(self.finalized_blocks.get(&number).copied()) + } + fn header(&self, id: BlockId) -> sp_blockchain::Result> { + match id { + // for error path testing + BlockId::Hash(hash) if hash.is_zero() => { + Err(sp_blockchain::Error::Backend("Zero hashes are illegal!".into())) + } + BlockId::Hash(hash) => { + Ok(self.headers.get(&hash).cloned()) + } + _ => unreachable!(), + } + } + fn status(&self, _id: BlockId) -> sp_blockchain::Result { + unimplemented!() + } + } + + fn test_harness( + test: impl FnOnce(TestClient, TestSubsystemContextHandle) + -> BoxFuture<'static, ()>, + ) { + let (ctx, ctx_handle) = make_subsystem_context(TaskExecutor::new()); + let client = TestClient::default(); + + let chain_api_task = run(ctx, client.clone()).map(|x| x.unwrap()); + let test_task = test(client, ctx_handle); + + futures::executor::block_on(future::join(chain_api_task, test_task)); + } + + #[test] + fn request_block_number() { + test_harness(|client, mut sender| { + async move { + let zero = Hash::zero(); + let test_cases = [ + (TWO, client.number(TWO).unwrap()), + (zero, client.number(zero).unwrap()), // not here + ]; + for (hash, expected) in &test_cases { + let (tx, rx) = oneshot::channel(); + + sender.send(FromOverseer::Communication { + msg: ChainApiMessage::BlockNumber(*hash, tx), + }).await; + + assert_eq!(rx.await.unwrap().unwrap(), *expected); + } + + sender.send(FromOverseer::Signal(OverseerSignal::Conclude)).await; + }.boxed() + }) + } + + #[test] + fn request_finalized_hash() { + test_harness(|client, mut sender| { + async move { + let test_cases = [ + (1, client.hash(1).unwrap()), // not here + (2, client.hash(2).unwrap()), + ]; + for (number, expected) in &test_cases { + let (tx, rx) = oneshot::channel(); + + sender.send(FromOverseer::Communication { + msg: ChainApiMessage::FinalizedBlockHash(*number, tx), + }).await; + + assert_eq!(rx.await.unwrap().unwrap(), *expected); + } + + sender.send(FromOverseer::Signal(OverseerSignal::Conclude)).await; + }.boxed() + }) + } + + #[test] + fn request_last_finalized_number() { + test_harness(|client, mut sender| { + async move { + let (tx, rx) = oneshot::channel(); + + let expected = client.info().finalized_number; + sender.send(FromOverseer::Communication { + msg: ChainApiMessage::FinalizedBlockNumber(tx), + }).await; + + assert_eq!(rx.await.unwrap().unwrap(), expected); + + sender.send(FromOverseer::Signal(OverseerSignal::Conclude)).await; + }.boxed() + }) + } + + #[test] + fn request_ancestors() { + test_harness(|_client, mut sender| { + async move { + let (tx, rx) = oneshot::channel(); + sender.send(FromOverseer::Communication { + msg: ChainApiMessage::Ancestors { hash: THREE, k: 4, response_channel: tx }, + }).await; + assert_eq!(rx.await.unwrap().unwrap(), vec![TWO, ONE]); + + let (tx, rx) = oneshot::channel(); + sender.send(FromOverseer::Communication { + msg: ChainApiMessage::Ancestors { hash: TWO, k: 1, response_channel: tx }, + }).await; + assert_eq!(rx.await.unwrap().unwrap(), vec![ONE]); + + let (tx, rx) = oneshot::channel(); + sender.send(FromOverseer::Communication { + msg: ChainApiMessage::Ancestors { hash: ERROR_PATH, k: 2, response_channel: tx }, + }).await; + assert!(rx.await.unwrap().is_err()); + + sender.send(FromOverseer::Signal(OverseerSignal::Conclude)).await; + }.boxed() + }) + } +} diff --git a/polkadot/node/core/runtime-api/Cargo.toml b/polkadot/node/core/runtime-api/Cargo.toml index 200aed3897..7dfde63947 100644 --- a/polkadot/node/core/runtime-api/Cargo.toml +++ b/polkadot/node/core/runtime-api/Cargo.toml @@ -17,4 +17,3 @@ polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsys sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } futures = { version = "0.3.5", features = ["thread-pool"] } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem", features = ["test-helpers"] } -assert_matches = "1.3.0" diff --git a/polkadot/node/core/runtime-api/src/lib.rs b/polkadot/node/core/runtime-api/src/lib.rs index f242381723..34d73f91ec 100644 --- a/polkadot/node/core/runtime-api/src/lib.rs +++ b/polkadot/node/core/runtime-api/src/lib.rs @@ -24,8 +24,9 @@ use polkadot_subsystem::{ FromOverseer, OverseerSignal, }; use polkadot_subsystem::messages::{ - RuntimeApiMessage, RuntimeApiRequest as Request, RuntimeApiError, + RuntimeApiMessage, RuntimeApiRequest as Request, }; +use polkadot_subsystem::errors::RuntimeApiError; use polkadot_primitives::v1::{Block, BlockId, Hash, ParachainHost}; use sp_api::{ProvideRuntimeApi}; @@ -50,7 +51,7 @@ impl Subsystem for RuntimeApiSubsystem where fn start(self, ctx: Context) -> SpawnedSubsystem { SpawnedSubsystem { future: run(ctx, self.0).map(|_| ()).boxed(), - name: "RuntimeApiSubsystem", + name: "runtime-api-subsystem", } } } diff --git a/polkadot/node/subsystem/src/errors.rs b/polkadot/node/subsystem/src/errors.rs new file mode 100644 index 0000000000..40edb1b3c1 --- /dev/null +++ b/polkadot/node/subsystem/src/errors.rs @@ -0,0 +1,57 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Error types for the subsystem requests. + +/// A description of an error causing the runtime API request to be unservable. +#[derive(Debug, Clone)] +pub struct RuntimeApiError(String); + +impl From for RuntimeApiError { + fn from(s: String) -> Self { + RuntimeApiError(s) + } +} + +impl core::fmt::Display for RuntimeApiError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + write!(f, "{}", self.0) + } +} + +/// A description of an error causing the chain API request to be unservable. +#[derive(Debug, Clone)] +pub struct ChainApiError { + msg: String, +} + +impl From<&str> for ChainApiError { + fn from(s: &str) -> Self { + s.to_owned().into() + } +} + +impl From for ChainApiError { + fn from(msg: String) -> Self { + Self { msg } + } +} + +impl core::fmt::Display for ChainApiError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + write!(f, "{}", self.msg) + } +} diff --git a/polkadot/node/subsystem/src/lib.rs b/polkadot/node/subsystem/src/lib.rs index 6ed552a3f5..7e112f5439 100644 --- a/polkadot/node/subsystem/src/lib.rs +++ b/polkadot/node/subsystem/src/lib.rs @@ -34,6 +34,7 @@ use smallvec::SmallVec; use crate::messages::AllMessages; +pub mod errors; pub mod messages; pub mod util; #[cfg(any(test, feature = "test-helpers"))] diff --git a/polkadot/node/subsystem/src/messages.rs b/polkadot/node/subsystem/src/messages.rs index 3cd06863d4..d9a1f03937 100644 --- a/polkadot/node/subsystem/src/messages.rs +++ b/polkadot/node/subsystem/src/messages.rs @@ -31,7 +31,7 @@ use polkadot_primitives::v1::{ CoreAssignment, CoreOccupied, CandidateDescriptor, ValidatorSignature, OmittedValidationData, AvailableData, GroupRotationInfo, CoreState, LocalValidationData, GlobalValidationData, OccupiedCoreAssumption, - CandidateEvent, SessionIndex, + CandidateEvent, SessionIndex, BlockNumber, }; use polkadot_node_primitives::{ MisbehaviorReport, SignedFullStatement, View, ProtocolId, ValidationResult, @@ -285,6 +285,43 @@ impl AvailabilityStoreMessage { } } +/// A response channel for the result of a chain API request. +pub type ChainApiResponseChannel = oneshot::Sender>; + +/// Chain API request subsystem message. +#[derive(Debug)] +pub enum ChainApiMessage { + /// Request the block number by hash. + /// Returns `None` if a block with the given hash is not present in the db. + BlockNumber(Hash, ChainApiResponseChannel>), + /// Request the finalized block hash by number. + /// Returns `None` if a block with the given number is not present in the db. + /// Note: the caller must ensure the block is finalized. + FinalizedBlockHash(BlockNumber, ChainApiResponseChannel>), + /// Request the last finalized block number. + /// This request always succeeds. + FinalizedBlockNumber(ChainApiResponseChannel), + /// Request the `k` ancestors block hashes of a block with the given hash. + /// The response channel may return a `Vec` of size up to `k` + /// filled with ancestors hashes with the following order: + /// `parent`, `grandparent`, ... + Ancestors { + /// The hash of the block in question. + hash: Hash, + /// The number of ancestors to request. + k: usize, + /// The response channel. + response_channel: ChainApiResponseChannel>, + }, +} + +impl ChainApiMessage { + /// If the current variant contains the relay parent hash, return it. + pub fn relay_parent(&self) -> Option { + None + } +} + /// The information on scheduler assignments that some somesystems may be querying. #[derive(Debug, Clone)] pub struct SchedulerRoster { @@ -298,18 +335,8 @@ pub struct SchedulerRoster { pub availability_cores: Vec>, } -/// A description of an error causing the runtime API request to be unservable. -#[derive(Debug, Clone)] -pub struct RuntimeApiError(String); - -impl From for RuntimeApiError { - fn from(s: String) -> Self { - RuntimeApiError(s) - } -} - /// A sender for the result of a runtime API request. -pub type RuntimeApiSender = oneshot::Sender>; +pub type RuntimeApiSender = oneshot::Sender>; /// A request to the Runtime API subsystem. #[derive(Debug)] diff --git a/polkadot/node/subsystem/src/util.rs b/polkadot/node/subsystem/src/util.rs index 440fda08f0..14f6f96ed2 100644 --- a/polkadot/node/subsystem/src/util.rs +++ b/polkadot/node/subsystem/src/util.rs @@ -22,8 +22,9 @@ use crate::{ messages::{ - AllMessages, RuntimeApiError, RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender, + AllMessages, RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender, }, + errors::{ChainApiError, RuntimeApiError}, FromOverseer, SpawnedSubsystem, Subsystem, SubsystemContext, SubsystemError, SubsystemResult, }; use futures::{ @@ -72,7 +73,10 @@ pub enum Error { /// A subsystem error #[from] Subsystem(SubsystemError), - /// An error in the runtime API. + /// An error in the Chain API. + #[from] + ChainApi(ChainApiError), + /// An error in the Runtime API. #[from] RuntimeApi(RuntimeApiError), /// The type system wants this even though it doesn't make sense diff --git a/polkadot/roadmap/implementers-guide/src/SUMMARY.md b/polkadot/roadmap/implementers-guide/src/SUMMARY.md index 86ddabe45b..63280e5142 100644 --- a/polkadot/roadmap/implementers-guide/src/SUMMARY.md +++ b/polkadot/roadmap/implementers-guide/src/SUMMARY.md @@ -39,6 +39,7 @@ - [Misbehavior Arbitration](node/utility/misbehavior-arbitration.md) - [Peer Set Manager](node/utility/peer-set-manager.md) - [Runtime API Requests](node/utility/runtime-api.md) + - [Chain API Requests](node/utility/chain-api.md) - [Data Structures and Types](types/README.md) - [Candidate](types/candidate.md) - [Backing](types/backing.md) diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/chain-api.md b/polkadot/roadmap/implementers-guide/src/node/utility/chain-api.md new file mode 100644 index 0000000000..6469db262a --- /dev/null +++ b/polkadot/roadmap/implementers-guide/src/node/utility/chain-api.md @@ -0,0 +1,19 @@ +# Chain API + +The Chain API subsystem is responsible for providing a single point of access to chain state data via a set of pre-determined queries. + +## Protocol + +Input: [`ChainApiMessage`](../../types/overseer-protocol.md#chain-api-message) + +Output: None + +## Functionality + +On receipt of `ChainApiMessage`, answer the request and provide the response to the side-channel embedded within the request. + +Currently, the following requests are supported: +* Block hash to number +* Finalized block number to hash +* Last finalized block number +* Ancestors diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index 412ac5a1d6..cfa66c089a 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -69,7 +69,7 @@ enum AvailabilityStoreMessage { /// Store a specific chunk of the candidate's erasure-coding by validator index, with an /// accompanying proof. StoreChunk(Hash, ValidatorIndex, AvailabilityChunkAndProof, ResponseChannel>), - /// Store `AvailableData`. If `ValidatorIndex` is provided, also store this validator's + /// Store `AvailableData`. If `ValidatorIndex` is provided, also store this validator's /// `AvailabilityChunkAndProof`. StoreAvailableData(Hash, Option, u32, AvailableData, ResponseChannel>), } @@ -126,6 +126,37 @@ enum CandidateSelectionMessage { } ``` +## Chain API Message + +The Chain API subsystem is responsible for providing an interface to chain data. + +```rust +enum ChainApiMessage { + /// Get the block number by hash. + /// Returns `None` if a block with the given hash is not present in the db. + BlockNumber(Hash, ResponseChannel, Error>>), + /// Get the finalized block hash by number. + /// Returns `None` if a block with the given number is not present in the db. + /// Note: the caller must ensure the block is finalized. + FinalizedBlockHash(BlockNumber, ResponseChannel, Error>>), + /// Get the last finalized block number. + /// This request always succeeds. + FinalizedBlockNumber(ResponseChannel>), + /// Request the `k` ancestors block hashes of a block with the given hash. + /// The response channel may return a `Vec` of size up to `k` + /// filled with ancestors hashes with the following order: + /// `parent`, `grandparent`, ... + Ancestors { + /// The hash of the block in question. + hash: Hash, + /// The number of ancestors to request. + k: usize, + /// The response channel. + response_channel: ResponseChannel, Error>>, + } +} +``` + ## Collator Protocol Message Messages received by the [Collator Protocol subsystem](../node/collators/collator-protocol.md)