use crate::{
chain::{blocks_tree, chain_information},
finality::decode,
header, verify,
};
use alloc::{borrow::ToOwned as _, boxed::Box, vec::Vec};
use core::{cmp, mem, num::NonZero, ops, time::Duration};
mod disjoint;
mod pending_blocks;
pub mod sources;
pub use pending_blocks::{RequestId, RequestParams, SourceId};
#[derive(Debug)]
pub struct Config {
pub chain_information: chain_information::ValidChainInformation,
pub block_number_bytes: usize,
pub allow_unknown_consensus_engines: bool,
pub sources_capacity: usize,
pub blocks_capacity: usize,
pub max_disjoint_headers: usize,
pub max_requests_per_block: NonZero<u32>,
pub download_bodies: bool,
}
pub struct AllForksSync<TBl, TRq, TSrc> {
chain: blocks_tree::NonFinalizedTree<TBl>,
inner: Box<Inner<TBl, TRq, TSrc>>,
}
struct Inner<TBl, TRq, TSrc> {
blocks: pending_blocks::PendingBlocks<PendingBlock<TBl>, TRq, Source<TSrc>>,
}
struct PendingBlock<TBl> {
header: Option<Vec<u8>>,
body: Option<Vec<Vec<u8>>>,
user_data: TBl,
}
struct Source<TSrc> {
unverified_finality_proofs: SourcePendingJustificationProofs,
finalized_block_number: u64,
pending_finality_proofs: SourcePendingJustificationProofs,
user_data: TSrc,
}
enum SourcePendingJustificationProofs {
None,
One {
target_height: u64,
proof: FinalityProofs,
},
Two {
low_target_height: u64,
low_proof: FinalityProofs,
high_target_height: u64,
high_proof: FinalityProofs,
},
}
impl SourcePendingJustificationProofs {
fn is_none(&self) -> bool {
matches!(self, SourcePendingJustificationProofs::None)
}
fn insert(&mut self, new_target_height: u64, new_proof: FinalityProofs) {
debug_assert!(
!matches!(&new_proof, FinalityProofs::Justifications(list) if list.is_empty())
);
match mem::replace(self, SourcePendingJustificationProofs::None) {
SourcePendingJustificationProofs::None => {
*self = SourcePendingJustificationProofs::One {
target_height: new_target_height,
proof: new_proof,
};
}
SourcePendingJustificationProofs::One {
target_height,
proof,
} if target_height < new_target_height => {
*self = SourcePendingJustificationProofs::Two {
low_target_height: target_height,
low_proof: proof,
high_target_height: new_target_height,
high_proof: new_proof,
};
}
SourcePendingJustificationProofs::One {
target_height,
proof,
} if target_height > new_target_height => {
*self = SourcePendingJustificationProofs::Two {
low_target_height: new_target_height,
low_proof: new_proof,
high_target_height: target_height,
high_proof: proof,
};
}
SourcePendingJustificationProofs::One { .. } => {
*self = SourcePendingJustificationProofs::One {
target_height: new_target_height,
proof: new_proof,
};
}
SourcePendingJustificationProofs::Two {
high_target_height,
low_proof,
low_target_height,
..
} if new_target_height >= high_target_height => {
*self = SourcePendingJustificationProofs::Two {
high_proof: new_proof,
high_target_height: new_target_height,
low_proof,
low_target_height,
};
}
SourcePendingJustificationProofs::Two {
high_proof,
high_target_height,
low_target_height,
..
} if new_target_height <= low_target_height => {
*self = SourcePendingJustificationProofs::Two {
high_proof,
high_target_height,
low_proof: new_proof,
low_target_height: new_target_height,
};
}
val @ SourcePendingJustificationProofs::Two { .. } => {
*self = val;
}
}
}
fn take_one(&mut self) -> Option<FinalityProof> {
match mem::replace(self, SourcePendingJustificationProofs::None) {
SourcePendingJustificationProofs::None => {
*self = SourcePendingJustificationProofs::None;
None
}
SourcePendingJustificationProofs::One {
proof: FinalityProofs::GrandpaCommit(commit),
..
} => {
*self = SourcePendingJustificationProofs::None;
Some(FinalityProof::GrandpaCommit(commit))
}
SourcePendingJustificationProofs::One {
proof: FinalityProofs::Justifications(justifications),
..
} if justifications.len() == 1 => {
*self = SourcePendingJustificationProofs::None;
let j = justifications.into_iter().next().unwrap();
Some(FinalityProof::Justification(j))
}
SourcePendingJustificationProofs::One {
target_height,
proof: FinalityProofs::Justifications(mut justifications),
} => {
let j = justifications.pop().unwrap();
*self = SourcePendingJustificationProofs::One {
target_height,
proof: FinalityProofs::Justifications(justifications),
};
Some(FinalityProof::Justification(j))
}
SourcePendingJustificationProofs::Two {
high_proof: FinalityProofs::GrandpaCommit(commit),
low_proof,
low_target_height,
..
} => {
*self = SourcePendingJustificationProofs::One {
target_height: low_target_height,
proof: low_proof,
};
Some(FinalityProof::GrandpaCommit(commit))
}
SourcePendingJustificationProofs::Two {
high_proof: FinalityProofs::Justifications(justifications),
low_proof,
low_target_height,
..
} if justifications.len() == 1 => {
let j = justifications.into_iter().next().unwrap();
*self = SourcePendingJustificationProofs::One {
target_height: low_target_height,
proof: low_proof,
};
Some(FinalityProof::Justification(j))
}
SourcePendingJustificationProofs::Two {
high_proof: FinalityProofs::Justifications(mut justifications),
high_target_height,
low_proof,
low_target_height,
} => {
let j = justifications.pop().unwrap();
*self = SourcePendingJustificationProofs::Two {
high_proof: FinalityProofs::Justifications(justifications),
high_target_height,
low_proof,
low_target_height,
};
Some(FinalityProof::Justification(j))
}
}
}
fn merge(&mut self, other: Self) {
match other {
SourcePendingJustificationProofs::None => {}
SourcePendingJustificationProofs::One {
target_height,
proof,
} => self.insert(target_height, proof),
SourcePendingJustificationProofs::Two {
high_proof,
high_target_height,
low_proof,
low_target_height,
} => {
self.insert(high_target_height, high_proof);
self.insert(low_target_height, low_proof);
}
}
}
}
enum FinalityProofs {
GrandpaCommit(Vec<u8>),
Justifications(Vec<([u8; 4], Vec<u8>)>),
}
enum FinalityProof {
GrandpaCommit(Vec<u8>),
Justification(([u8; 4], Vec<u8>)),
}
impl<TBl, TRq, TSrc> AllForksSync<TBl, TRq, TSrc> {
pub fn new(config: Config) -> Self {
let finalized_block_height = config
.chain_information
.as_ref()
.finalized_block_header
.number;
let chain = blocks_tree::NonFinalizedTree::new(blocks_tree::Config {
chain_information: config.chain_information,
block_number_bytes: config.block_number_bytes,
blocks_capacity: config.blocks_capacity,
allow_unknown_consensus_engines: config.allow_unknown_consensus_engines,
});
Self {
chain,
inner: Box::new(Inner {
blocks: pending_blocks::PendingBlocks::new(pending_blocks::Config {
blocks_capacity: config.blocks_capacity,
finalized_block_height,
max_requests_per_block: config.max_requests_per_block,
sources_capacity: config.sources_capacity,
download_bodies: config.download_bodies,
}),
}),
}
}
pub fn block_number_bytes(&self) -> usize {
self.chain.block_number_bytes()
}
pub fn as_chain_information(&'_ self) -> chain_information::ValidChainInformationRef<'_> {
self.chain.as_chain_information()
}
pub fn finalized_block_header(&self) -> &[u8] {
self.chain.finalized_block_header()
}
pub fn finalized_block_number(&self) -> u64 {
self.chain.finalized_block_height()
}
pub fn finalized_block_hash(&self) -> &[u8; 32] {
self.chain.finalized_block_hash()
}
pub fn set_finalized_block(
&mut self,
block_hash: &[u8; 32],
) -> Result<SetFinalizedBlockResult<TBl>, blocks_tree::SetFinalizedError> {
let iter = self.chain.set_finalized_block(block_hash)?;
let updates_best_block = iter.updates_best_block();
let mut finalized_blocks = Vec::new();
let mut pruned_blocks = Vec::new();
for block in iter {
match block.ty {
blocks_tree::RemovedBlockType::Finalized => finalized_blocks.push(RemovedBlock {
block_hash: block.block_hash,
block_number: block.block_number,
user_data: block.user_data,
scale_encoded_header: block.scale_encoded_header,
}),
blocks_tree::RemovedBlockType::Pruned => pruned_blocks.push(RemovedBlock {
block_hash: block.block_hash,
block_number: block.block_number,
user_data: block.user_data,
scale_encoded_header: block.scale_encoded_header,
}),
}
}
if let Some(last) = finalized_blocks.last() {
let _ = self
.inner
.blocks
.set_finalized_block_height(last.block_number)
.count();
}
Ok(SetFinalizedBlockResult {
finalized_blocks,
pruned_blocks,
updates_best_block,
})
}
pub fn best_block_header(&self) -> &[u8] {
self.chain.best_block_header()
}
pub fn best_block_number(&self) -> u64 {
self.chain.best_block_height()
}
pub fn best_block_hash(&self) -> &[u8; 32] {
self.chain.best_block_hash()
}
pub fn non_finalized_blocks_unordered(&'_ self) -> impl Iterator<Item = header::HeaderRef<'_>> {
self.chain.iter_unordered()
}
pub fn non_finalized_blocks_ancestry_order(
&'_ self,
) -> impl Iterator<Item = header::HeaderRef<'_>> {
self.chain.iter_ancestry_order()
}
pub fn prepare_add_source(
&'_ mut self,
best_block_number: u64,
best_block_hash: [u8; 32],
) -> AddSource<'_, TBl, TRq, TSrc> {
if best_block_number <= self.chain.finalized_block_height() {
return AddSource::OldBestBlock(AddSourceOldBlock {
inner: self,
best_block_hash,
best_block_number,
});
}
let best_block_already_verified = self.chain.contains_non_finalized_block(&best_block_hash);
let best_block_in_disjoints_list = self
.inner
.blocks
.contains_unverified_block(best_block_number, &best_block_hash);
match (best_block_already_verified, best_block_in_disjoints_list) {
(false, false) => AddSource::UnknownBestBlock(AddSourceUnknown {
inner: self,
best_block_hash,
best_block_number,
}),
(true, false) => AddSource::BestBlockAlreadyVerified(AddSourceKnown {
inner: self,
best_block_hash,
best_block_number,
}),
(false, true) => AddSource::BestBlockPendingVerification(AddSourceKnown {
inner: self,
best_block_hash,
best_block_number,
}),
(true, true) => unreachable!(),
}
}
pub fn remove_source(
&mut self,
source_id: SourceId,
) -> (TSrc, impl Iterator<Item = (RequestId, RequestParams, TRq)>) {
let (user_data, iter) = self.inner.blocks.remove_source(source_id);
(user_data.user_data, iter)
}
pub fn sources(&self) -> impl ExactSizeIterator<Item = SourceId> {
self.inner.blocks.sources()
}
pub fn source_knows_non_finalized_block(
&self,
source_id: SourceId,
height: u64,
hash: &[u8; 32],
) -> bool {
self.inner
.blocks
.source_knows_non_finalized_block(source_id, height, hash)
}
pub fn knows_non_finalized_block<'a>(
&'a self,
height: u64,
hash: &[u8; 32],
) -> impl Iterator<Item = SourceId> + use<'a, TBl, TRq, TSrc> {
self.inner.blocks.knows_non_finalized_block(height, hash)
}
pub fn add_known_block_to_source(&mut self, source_id: SourceId, height: u64, hash: [u8; 32]) {
self.inner
.blocks
.add_known_block_to_source(source_id, height, hash);
}
pub fn source_best_block(&self, source_id: SourceId) -> (u64, &[u8; 32]) {
self.inner.blocks.source_best_block(source_id)
}
pub fn source_num_ongoing_requests(&self, source_id: SourceId) -> usize {
self.inner.blocks.source_num_ongoing_requests(source_id)
}
pub fn desired_requests(&self) -> impl Iterator<Item = (SourceId, &TSrc, RequestParams)> {
let justification_requests =
self.chain
.finality_checkpoints()
.flat_map(move |(block_height, block_hash)| {
self.inner
.blocks
.sources()
.filter(move |s| {
self.inner.blocks[*s].unverified_finality_proofs.is_none()
&& self.inner.blocks[*s].finalized_block_number >= block_height
})
.map(move |source_id| {
(
source_id,
&self.inner.blocks[source_id].user_data,
RequestParams {
first_block_hash: *block_hash,
first_block_height: block_height,
num_blocks: NonZero::<u64>::new(1).unwrap(),
},
)
})
});
let block_requests = self
.inner
.blocks
.desired_requests()
.filter(move |rq| {
!self
.chain
.contains_non_finalized_block(&rq.request_params.first_block_hash)
})
.map(move |rq| {
(
rq.source_id,
&self.inner.blocks[rq.source_id].user_data,
rq.request_params,
)
});
justification_requests.chain(block_requests)
}
pub fn add_request(
&mut self,
source_id: SourceId,
detail: RequestParams,
user_data: TRq,
) -> RequestId {
self.inner.blocks.add_request(source_id, detail, user_data)
}
pub fn obsolete_requests(&self) -> impl Iterator<Item = (RequestId, &TRq)> {
self.inner.blocks.obsolete_requests()
}
pub fn request_source_id(&self, request_id: RequestId) -> SourceId {
self.inner.blocks.request_source_id(request_id)
}
pub fn finish_request(
&'_ mut self,
request_id: RequestId,
) -> (TRq, FinishRequest<'_, TBl, TRq, TSrc>) {
let (
pending_blocks::RequestParams {
first_block_hash: requested_block_hash,
first_block_height: requested_block_height,
..
},
source_id,
request_user_data,
) = self.inner.blocks.remove_request(request_id);
(
request_user_data,
FinishRequest {
inner: self,
source_id,
any_progress: false,
index_in_response: 0,
requested_block_hash,
requested_block_height,
expected_next_hash: requested_block_hash,
expected_next_height: requested_block_height,
},
)
}
pub fn block_announce(
&'_ mut self,
source_id: SourceId,
announced_scale_encoded_header: Vec<u8>,
is_best: bool,
) -> BlockAnnounceOutcome<'_, TBl, TRq, TSrc> {
let announced_header = match header::decode(
&announced_scale_encoded_header,
self.chain.block_number_bytes(),
) {
Ok(h) => h,
Err(error) => return BlockAnnounceOutcome::InvalidHeader(error),
};
let announced_header_number = announced_header.number;
let announced_header_parent_hash = *announced_header.parent_hash;
let announced_header_hash = announced_header.hash(self.chain.block_number_bytes());
if announced_header_number <= self.chain.finalized_block_height() {
if is_best {
self.inner.blocks.add_known_block_to_source_and_set_best(
source_id,
announced_header_number,
announced_header_hash,
);
}
return BlockAnnounceOutcome::TooOld {
announce_block_height: announced_header_number,
finalized_block_height: self.chain.finalized_block_height(),
};
}
if self
.chain
.contains_non_finalized_block(&announced_header_hash)
{
return BlockAnnounceOutcome::AlreadyVerified(AnnouncedBlockKnown {
inner: self,
announced_header_hash,
announced_header_number,
announced_header_parent_hash,
announced_header_encoded: announced_scale_encoded_header,
source_id,
is_in_chain: true,
is_best,
});
}
if !self
.inner
.blocks
.contains_unverified_block(announced_header_number, &announced_header_hash)
{
BlockAnnounceOutcome::Unknown(AnnouncedBlockUnknown {
inner: self,
announced_header_hash,
announced_header_number,
announced_header_parent_hash,
announced_header_encoded: announced_scale_encoded_header,
source_id,
is_best,
})
} else {
BlockAnnounceOutcome::AlreadyPending(AnnouncedBlockKnown {
inner: self,
announced_header_hash,
announced_header_number,
announced_header_parent_hash,
announced_header_encoded: announced_scale_encoded_header,
is_in_chain: false,
source_id,
is_best,
})
}
}
pub fn update_source_finality_state(
&mut self,
source_id: SourceId,
finalized_block_height: u64,
) {
let source = &mut self.inner.blocks[source_id];
source.finalized_block_number =
cmp::max(source.finalized_block_number, finalized_block_height);
}
pub fn grandpa_commit_message(
&mut self,
source_id: SourceId,
scale_encoded_commit: Vec<u8>,
) -> GrandpaCommitMessageOutcome {
let source = &mut self.inner.blocks[source_id];
let block_number = match decode::decode_grandpa_commit(
&scale_encoded_commit,
self.chain.block_number_bytes(),
) {
Ok(msg) => msg.target_number,
Err(_) => return GrandpaCommitMessageOutcome::ParseError,
};
source.finalized_block_number = cmp::max(source.finalized_block_number, block_number);
source.unverified_finality_proofs.insert(
block_number,
FinalityProofs::GrandpaCommit(scale_encoded_commit),
);
GrandpaCommitMessageOutcome::Queued
}
pub fn process_one(mut self) -> ProcessOne<TBl, TRq, TSrc> {
let block_to_verify = self.inner.blocks.unverified_leaves().find(|block| {
block.parent_block_hash == *self.chain.finalized_block_hash()
|| self
.chain
.contains_non_finalized_block(&block.parent_block_hash)
});
if let Some(block) = block_to_verify {
return ProcessOne::BlockVerify(BlockVerify {
parent: self,
block_to_verify: block,
});
}
let source_id_with_finality_proof = self
.inner
.blocks
.sources()
.find(|id| !self.inner.blocks[*id].unverified_finality_proofs.is_none());
if let Some(source_id_with_finality_proof) = source_id_with_finality_proof {
let finality_proof_to_verify = self.inner.blocks[source_id_with_finality_proof]
.unverified_finality_proofs
.take_one()
.unwrap(); return ProcessOne::FinalityProofVerify(FinalityProofVerify {
parent: self,
source_id: source_id_with_finality_proof,
finality_proof_to_verify,
});
}
ProcessOne::AllSync { sync: self }
}
}
impl<TBl, TRq, TSrc> ops::Index<SourceId> for AllForksSync<TBl, TRq, TSrc> {
type Output = TSrc;
#[track_caller]
fn index(&self, id: SourceId) -> &TSrc {
&self.inner.blocks[id].user_data
}
}
impl<TBl, TRq, TSrc> ops::IndexMut<SourceId> for AllForksSync<TBl, TRq, TSrc> {
#[track_caller]
fn index_mut(&mut self, id: SourceId) -> &mut TSrc {
&mut self.inner.blocks[id].user_data
}
}
impl<'a, TBl, TRq, TSrc> ops::Index<(u64, &'a [u8; 32])> for AllForksSync<TBl, TRq, TSrc> {
type Output = TBl;
#[track_caller]
fn index(&self, (block_height, block_hash): (u64, &'a [u8; 32])) -> &TBl {
if let Some(block) = self.chain.non_finalized_block_user_data(block_hash) {
return block;
}
&self
.inner
.blocks
.unverified_block_user_data(block_height, block_hash)
.user_data
}
}
impl<'a, TBl, TRq, TSrc> ops::IndexMut<(u64, &'a [u8; 32])> for AllForksSync<TBl, TRq, TSrc> {
#[track_caller]
fn index_mut(&mut self, (block_height, block_hash): (u64, &'a [u8; 32])) -> &mut TBl {
if let Some(block) = self.chain.non_finalized_block_user_data_mut(block_hash) {
return block;
}
&mut self
.inner
.blocks
.unverified_block_user_data_mut(block_height, block_hash)
.user_data
}
}
pub struct FinishRequest<'a, TBl, TRq, TSrc> {
inner: &'a mut AllForksSync<TBl, TRq, TSrc>,
source_id: SourceId,
any_progress: bool,
index_in_response: usize,
requested_block_hash: [u8; 32],
requested_block_height: u64,
expected_next_hash: [u8; 32],
expected_next_height: u64,
}
impl<'a, TBl, TRq, TSrc> FinishRequest<'a, TBl, TRq, TSrc> {
pub fn add_block(
mut self,
scale_encoded_header: Vec<u8>,
scale_encoded_extrinsics: Vec<Vec<u8>>,
scale_encoded_justifications: impl Iterator<Item = ([u8; 4], impl AsRef<[u8]>)>,
) -> Result<AddBlock<'a, TBl, TRq, TSrc>, AncestrySearchResponseError> {
if self.expected_next_hash != header::hash_from_scale_encoded_header(&scale_encoded_header)
{
return Err(AncestrySearchResponseError::UnexpectedBlock);
}
let decoded_header =
match header::decode(&scale_encoded_header, self.inner.chain.block_number_bytes()) {
Ok(h) => h,
Err(err) => return Err(AncestrySearchResponseError::InvalidHeader(err)),
};
if self.expected_next_height != decoded_header.number {
return Err(AncestrySearchResponseError::UnexpectedBlock);
}
if self.inner.inner.blocks.downloading_bodies() {
let calculated = header::extrinsics_root(&scale_encoded_extrinsics);
if calculated != *decoded_header.extrinsics_root {
return Err(AncestrySearchResponseError::ExtrinsicsRootMismatch);
}
}
self.any_progress = true;
if decoded_header.number <= self.inner.chain.finalized_block_height() {
return Err(AncestrySearchResponseError::TooOld);
}
let justifications = scale_encoded_justifications
.map(|(e, j)| (e, j.as_ref().to_owned()))
.collect::<Vec<_>>();
if self
.inner
.chain
.contains_non_finalized_block(&self.expected_next_hash)
{
if !justifications.is_empty() {
self.inner.inner.blocks[self.source_id]
.unverified_finality_proofs
.insert(
decoded_header.number,
FinalityProofs::Justifications(justifications),
);
}
return Ok(AddBlock::AlreadyInChain(AddBlockOccupied {
inner: self,
block_number: decoded_header.number,
block_parent_hash: *decoded_header.parent_hash,
block_header: scale_encoded_header,
is_verified: true,
}));
}
if decoded_header.number == self.inner.chain.finalized_block_height() + 1
&& *decoded_header.parent_hash != *self.inner.chain.finalized_block_hash()
{
let error = AncestrySearchResponseError::NotFinalizedChain {
discarded_unverified_block_headers: Vec::new(), };
return Err(error);
}
if !self
.inner
.inner
.blocks
.contains_unverified_block(decoded_header.number, &self.expected_next_hash)
{
Ok(AddBlock::UnknownBlock(AddBlockVacant {
inner: self,
block_number: decoded_header.number,
block_parent_hash: *decoded_header.parent_hash,
block_header: scale_encoded_header,
scale_encoded_extrinsics,
justifications,
}))
} else {
if !justifications.is_empty() {
self.inner.inner.blocks[self.source_id]
.unverified_finality_proofs
.insert(
decoded_header.number,
FinalityProofs::Justifications(justifications),
);
}
if self.inner.inner.blocks.downloading_bodies() {
self.inner
.inner
.blocks
.set_unverified_block_header_body_known(
decoded_header.number,
&self.expected_next_hash,
*decoded_header.parent_hash,
);
self.inner
.inner
.blocks
.unverified_block_user_data_mut(decoded_header.number, &self.expected_next_hash)
.body = Some(scale_encoded_extrinsics);
}
Ok(AddBlock::AlreadyPending(AddBlockOccupied {
inner: self,
block_number: decoded_header.number,
block_parent_hash: *decoded_header.parent_hash,
block_header: scale_encoded_header,
is_verified: false,
}))
}
}
pub fn finish(self) {
drop(self);
}
}
impl<'a, TBl, TRq, TSrc> Drop for FinishRequest<'a, TBl, TRq, TSrc> {
fn drop(&mut self) {
if !self.any_progress {
self.inner.inner.blocks.remove_known_block_of_source(
self.source_id,
self.requested_block_height,
&self.requested_block_hash,
);
}
}
}
pub enum AddBlock<'a, TBl, TRq, TSrc> {
AlreadyPending(AddBlockOccupied<'a, TBl, TRq, TSrc>),
UnknownBlock(AddBlockVacant<'a, TBl, TRq, TSrc>),
AlreadyInChain(AddBlockOccupied<'a, TBl, TRq, TSrc>),
}
pub struct AddBlockOccupied<'a, TBl, TRq, TSrc> {
inner: FinishRequest<'a, TBl, TRq, TSrc>,
block_header: Vec<u8>,
block_number: u64,
block_parent_hash: [u8; 32],
is_verified: bool,
}
impl<'a, TBl, TRq, TSrc> AddBlockOccupied<'a, TBl, TRq, TSrc> {
pub fn user_data_mut(&mut self) -> &mut TBl {
if self.is_verified {
&mut self.inner.inner.chain[&self.inner.expected_next_hash]
} else {
&mut self
.inner
.inner
.inner
.blocks
.unverified_block_user_data_mut(self.block_number, &self.inner.expected_next_hash)
.user_data
}
}
pub fn replace(mut self, user_data: TBl) -> (FinishRequest<'a, TBl, TRq, TSrc>, TBl) {
self.inner.inner.inner.blocks.add_known_block_to_source(
self.inner.source_id,
self.block_number,
self.inner.expected_next_hash,
);
self.inner.inner.inner.blocks.add_known_block_to_source(
self.inner.source_id,
self.block_number - 1,
self.block_parent_hash,
);
let former_user_data = if self.is_verified {
mem::replace(
&mut self.inner.inner.chain[&self.inner.expected_next_hash],
user_data,
)
} else {
self.inner
.inner
.inner
.blocks
.set_unverified_block_header_known(
self.block_number,
&self.inner.expected_next_hash,
self.block_parent_hash,
);
let block_user_data = self
.inner
.inner
.inner
.blocks
.unverified_block_user_data_mut(self.block_number, &self.inner.expected_next_hash);
if block_user_data.header.is_none() {
block_user_data.header = Some(self.block_header);
}
mem::replace(&mut block_user_data.user_data, user_data)
};
self.inner.expected_next_hash = self.block_parent_hash;
self.inner.expected_next_height -= 1;
self.inner.index_in_response += 1;
(self.inner, former_user_data)
}
}
pub struct AddBlockVacant<'a, TBl, TRq, TSrc> {
inner: FinishRequest<'a, TBl, TRq, TSrc>,
block_header: Vec<u8>,
block_number: u64,
block_parent_hash: [u8; 32],
justifications: Vec<([u8; 4], Vec<u8>)>,
scale_encoded_extrinsics: Vec<Vec<u8>>,
}
impl<'a, TBl, TRq, TSrc> AddBlockVacant<'a, TBl, TRq, TSrc> {
pub fn insert(mut self, user_data: TBl) -> FinishRequest<'a, TBl, TRq, TSrc> {
self.inner.inner.inner.blocks.add_known_block_to_source(
self.inner.source_id,
self.block_number,
self.inner.expected_next_hash,
);
self.inner.inner.inner.blocks.add_known_block_to_source(
self.inner.source_id,
self.block_number - 1,
self.block_parent_hash,
);
self.inner.inner.inner.blocks.insert_unverified_block(
self.block_number,
self.inner.expected_next_hash,
if self.inner.inner.inner.blocks.downloading_bodies() {
pending_blocks::UnverifiedBlockState::HeaderBody {
parent_hash: self.block_parent_hash,
}
} else {
pending_blocks::UnverifiedBlockState::Header {
parent_hash: self.block_parent_hash,
}
},
PendingBlock {
header: Some(self.block_header),
body: Some(self.scale_encoded_extrinsics),
user_data,
},
);
if !self.justifications.is_empty() {
self.inner.inner.inner.blocks[self.inner.source_id]
.unverified_finality_proofs
.insert(
self.block_number,
FinalityProofs::Justifications(self.justifications),
);
}
while self.inner.inner.inner.blocks.num_unverified_blocks() >= 100 {
let (height, hash) = match self
.inner
.inner
.inner
.blocks
.unnecessary_unverified_blocks()
.next()
{
Some((n, h)) => (n, *h),
None => break,
};
self.inner
.inner
.inner
.blocks
.remove_unverified_block(height, &hash);
}
self.inner.expected_next_hash = self.block_parent_hash;
self.inner.expected_next_height -= 1;
self.inner.index_in_response += 1;
self.inner
}
}
pub enum BlockAnnounceOutcome<'a, TBl, TRq, TSrc> {
TooOld {
announce_block_height: u64,
finalized_block_height: u64,
},
AlreadyVerified(AnnouncedBlockKnown<'a, TBl, TRq, TSrc>),
AlreadyPending(AnnouncedBlockKnown<'a, TBl, TRq, TSrc>),
Unknown(AnnouncedBlockUnknown<'a, TBl, TRq, TSrc>),
InvalidHeader(header::Error),
}
#[must_use]
pub struct AnnouncedBlockKnown<'a, TBl, TRq, TSrc> {
inner: &'a mut AllForksSync<TBl, TRq, TSrc>,
announced_header_hash: [u8; 32],
announced_header_parent_hash: [u8; 32],
announced_header_number: u64,
announced_header_encoded: Vec<u8>,
is_in_chain: bool,
is_best: bool,
source_id: SourceId,
}
impl<'a, TBl, TRq, TSrc> AnnouncedBlockKnown<'a, TBl, TRq, TSrc> {
pub fn parent_hash(&self) -> &[u8; 32] {
&self.announced_header_parent_hash
}
pub fn height(&self) -> u64 {
self.announced_header_number
}
pub fn hash(&self) -> &[u8; 32] {
&self.announced_header_hash
}
pub fn user_data_mut(&mut self) -> &mut TBl {
if self.is_in_chain {
&mut self.inner.chain[&self.announced_header_hash]
} else {
&mut self
.inner
.inner
.blocks
.unverified_block_user_data_mut(
self.announced_header_number,
&self.announced_header_hash,
)
.user_data
}
}
pub fn update_source_and_block(self) {
if self.is_best {
self.inner
.inner
.blocks
.add_known_block_to_source_and_set_best(
self.source_id,
self.announced_header_number,
self.announced_header_hash,
);
} else {
self.inner.inner.blocks.add_known_block_to_source(
self.source_id,
self.announced_header_number,
self.announced_header_hash,
);
}
self.inner.inner.blocks.add_known_block_to_source(
self.source_id,
self.announced_header_number - 1,
self.announced_header_parent_hash,
);
if !self.is_in_chain {
self.inner.inner.blocks.set_unverified_block_header_known(
self.announced_header_number,
&self.announced_header_hash,
self.announced_header_parent_hash,
);
let block_user_data = self.inner.inner.blocks.unverified_block_user_data_mut(
self.announced_header_number,
&self.announced_header_hash,
);
if block_user_data.header.is_none() {
block_user_data.header = Some(self.announced_header_encoded);
}
if self.announced_header_number == self.inner.chain.finalized_block_height() + 1
&& self.announced_header_parent_hash != *self.inner.chain.finalized_block_hash()
{
self.inner.inner.blocks.mark_unverified_block_as_bad(
self.announced_header_number,
&self.announced_header_hash,
);
}
}
}
}
#[must_use]
pub struct AnnouncedBlockUnknown<'a, TBl, TRq, TSrc> {
inner: &'a mut AllForksSync<TBl, TRq, TSrc>,
announced_header_hash: [u8; 32],
announced_header_parent_hash: [u8; 32],
announced_header_number: u64,
announced_header_encoded: Vec<u8>,
is_best: bool,
source_id: SourceId,
}
impl<'a, TBl, TRq, TSrc> AnnouncedBlockUnknown<'a, TBl, TRq, TSrc> {
pub fn parent_hash(&self) -> &[u8; 32] {
&self.announced_header_parent_hash
}
pub fn height(&self) -> u64 {
self.announced_header_number
}
pub fn hash(&self) -> &[u8; 32] {
&self.announced_header_hash
}
pub fn insert_and_update_source(self, user_data: TBl) {
if self.is_best {
self.inner
.inner
.blocks
.add_known_block_to_source_and_set_best(
self.source_id,
self.announced_header_number,
self.announced_header_hash,
);
} else {
self.inner.inner.blocks.add_known_block_to_source(
self.source_id,
self.announced_header_number,
self.announced_header_hash,
);
}
self.inner.inner.blocks.add_known_block_to_source(
self.source_id,
self.announced_header_number - 1,
self.announced_header_parent_hash,
);
self.inner.inner.blocks.insert_unverified_block(
self.announced_header_number,
self.announced_header_hash,
pending_blocks::UnverifiedBlockState::Header {
parent_hash: self.announced_header_parent_hash,
},
PendingBlock {
header: Some(self.announced_header_encoded),
body: None,
user_data,
},
);
while self.inner.inner.blocks.num_unverified_blocks() >= 100 {
let (height, hash) = match self
.inner
.inner
.blocks
.unnecessary_unverified_blocks()
.next()
{
Some((n, h)) => (n, *h),
None => break,
};
self.inner
.inner
.blocks
.remove_unverified_block(height, &hash);
}
}
}
pub enum AncestrySearchResponseError {
InvalidHeader(header::Error),
UnexpectedBlock,
ExtrinsicsRootMismatch,
NotFinalizedChain {
discarded_unverified_block_headers: Vec<Vec<u8>>,
},
TooOld,
}
#[must_use]
pub enum AddSource<'a, TBl, TRq, TSrc> {
OldBestBlock(AddSourceOldBlock<'a, TBl, TRq, TSrc>),
BestBlockAlreadyVerified(AddSourceKnown<'a, TBl, TRq, TSrc>),
BestBlockPendingVerification(AddSourceKnown<'a, TBl, TRq, TSrc>),
UnknownBestBlock(AddSourceUnknown<'a, TBl, TRq, TSrc>),
}
#[must_use]
pub struct AddSourceOldBlock<'a, TBl, TRq, TSrc> {
inner: &'a mut AllForksSync<TBl, TRq, TSrc>,
best_block_number: u64,
best_block_hash: [u8; 32],
}
impl<'a, TBl, TRq, TSrc> AddSourceOldBlock<'a, TBl, TRq, TSrc> {
pub fn add_source(self, source_user_data: TSrc) -> SourceId {
self.inner.inner.blocks.add_source(
Source {
user_data: source_user_data,
unverified_finality_proofs: SourcePendingJustificationProofs::None,
finalized_block_number: 0,
pending_finality_proofs: SourcePendingJustificationProofs::None,
},
self.best_block_number,
self.best_block_hash,
)
}
}
#[must_use]
pub struct AddSourceKnown<'a, TBl, TRq, TSrc> {
inner: &'a mut AllForksSync<TBl, TRq, TSrc>,
best_block_number: u64,
best_block_hash: [u8; 32],
}
impl<'a, TBl, TRq, TSrc> AddSourceKnown<'a, TBl, TRq, TSrc> {
pub fn user_data_mut(&mut self) -> &mut TBl {
if let Some(block_access) = self
.inner
.chain
.non_finalized_block_user_data_mut(&self.best_block_hash)
{
block_access
} else {
&mut self
.inner
.inner
.blocks
.unverified_block_user_data_mut(self.best_block_number, &self.best_block_hash)
.user_data
}
}
pub fn add_source(self, source_user_data: TSrc) -> SourceId {
self.inner.inner.blocks.add_source(
Source {
user_data: source_user_data,
unverified_finality_proofs: SourcePendingJustificationProofs::None,
finalized_block_number: 0,
pending_finality_proofs: SourcePendingJustificationProofs::None,
},
self.best_block_number,
self.best_block_hash,
)
}
}
#[must_use]
pub struct AddSourceUnknown<'a, TBl, TRq, TSrc> {
inner: &'a mut AllForksSync<TBl, TRq, TSrc>,
best_block_number: u64,
best_block_hash: [u8; 32],
}
impl<'a, TBl, TRq, TSrc> AddSourceUnknown<'a, TBl, TRq, TSrc> {
pub fn add_source_and_insert_block(
self,
source_user_data: TSrc,
best_block_user_data: TBl,
) -> SourceId {
let source_id = self.inner.inner.blocks.add_source(
Source {
user_data: source_user_data,
unverified_finality_proofs: SourcePendingJustificationProofs::None,
finalized_block_number: 0,
pending_finality_proofs: SourcePendingJustificationProofs::None,
},
self.best_block_number,
self.best_block_hash,
);
self.inner.inner.blocks.insert_unverified_block(
self.best_block_number,
self.best_block_hash,
pending_blocks::UnverifiedBlockState::HeightHash,
PendingBlock {
header: None,
body: None,
user_data: best_block_user_data,
},
);
source_id
}
}
pub struct BlockVerify<TBl, TRq, TSrc> {
parent: AllForksSync<TBl, TRq, TSrc>,
block_to_verify: pending_blocks::TreeRoot,
}
impl<TBl, TRq, TSrc> BlockVerify<TBl, TRq, TSrc> {
pub fn hash(&self) -> &[u8; 32] {
&self.block_to_verify.block_hash
}
pub fn scale_encoded_extrinsics(
&self,
) -> Option<impl ExactSizeIterator<Item = impl AsRef<[u8]> + Clone> + Clone> {
if self.parent.inner.blocks.downloading_bodies() {
Some(
self.parent
.inner
.blocks
.unverified_block_user_data(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
)
.body
.as_ref()
.unwrap_or_else(|| unreachable!())
.iter(),
)
} else {
None
}
}
pub fn scale_encoded_header(&self) -> &[u8] {
self.parent
.inner
.blocks
.unverified_block_user_data(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
)
.header
.as_ref()
.unwrap()
}
pub fn verify_header(
mut self,
now_from_unix_epoch: Duration,
) -> HeaderVerifyOutcome<TBl, TRq, TSrc> {
let to_verify_scale_encoded_header = self.scale_encoded_header().to_owned();
let result = match self
.parent
.chain
.verify_header(to_verify_scale_encoded_header, now_from_unix_epoch)
{
Ok(blocks_tree::HeaderVerifySuccess::Verified {
verified_header,
is_new_best,
}) => {
Ok((verified_header, is_new_best))
}
Err(blocks_tree::HeaderVerifyError::VerificationFailed(error)) => {
self.parent.inner.blocks.mark_unverified_block_as_bad(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
);
Err(HeaderVerifyError::VerificationFailed(error))
}
Err(blocks_tree::HeaderVerifyError::ConsensusMismatch) => {
self.parent.inner.blocks.mark_unverified_block_as_bad(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
);
Err(HeaderVerifyError::ConsensusMismatch)
}
Err(blocks_tree::HeaderVerifyError::UnknownConsensusEngine) => {
self.parent.inner.blocks.mark_unverified_block_as_bad(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
);
Err(HeaderVerifyError::UnknownConsensusEngine)
}
Ok(blocks_tree::HeaderVerifySuccess::Duplicate)
| Err(
blocks_tree::HeaderVerifyError::BadParent { .. }
| blocks_tree::HeaderVerifyError::InvalidHeader(_),
) => unreachable!(),
};
match result {
Ok((verified_header, is_new_best)) => HeaderVerifyOutcome::Success {
is_new_best,
success: HeaderVerifySuccess {
parent: self.parent,
block_to_verify: self.block_to_verify,
verified_header,
},
},
Err(error) => HeaderVerifyOutcome::Error {
sync: self.parent,
error,
},
}
}
pub fn cancel(self) -> AllForksSync<TBl, TRq, TSrc> {
self.parent
}
}
pub struct HeaderVerifySuccess<TBl, TRq, TSrc> {
parent: AllForksSync<TBl, TRq, TSrc>,
block_to_verify: pending_blocks::TreeRoot,
verified_header: blocks_tree::VerifiedHeader,
}
impl<TBl, TRq, TSrc> HeaderVerifySuccess<TBl, TRq, TSrc> {
pub fn height(&self) -> u64 {
self.block_to_verify.block_number
}
pub fn hash(&self) -> &[u8; 32] {
&self.block_to_verify.block_hash
}
pub fn parent_hash(&self) -> &[u8; 32] {
&self.block_to_verify.parent_block_hash
}
pub fn parent_user_data(&self) -> Option<&TBl> {
self.parent
.chain
.non_finalized_block_user_data(&self.block_to_verify.parent_block_hash)
}
pub fn scale_encoded_header(&self) -> &[u8] {
self.verified_header.scale_encoded_header()
}
pub fn scale_encoded_extrinsics(
&self,
) -> Option<impl ExactSizeIterator<Item = impl AsRef<[u8]> + Clone> + Clone> {
if self.parent.inner.blocks.downloading_bodies() {
Some(
self.parent
.inner
.blocks
.unverified_block_user_data(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
)
.body
.as_ref()
.unwrap_or_else(|| unreachable!())
.iter(),
)
} else {
None
}
}
pub fn parent_scale_encoded_header(&self) -> &[u8] {
if self.block_to_verify.parent_block_hash == *self.parent.chain.finalized_block_hash() {
self.parent.chain.finalized_block_header()
} else {
self.parent
.chain
.non_finalized_block_header(&self.block_to_verify.parent_block_hash)
.unwrap()
}
}
pub fn cancel(self) -> AllForksSync<TBl, TRq, TSrc> {
self.parent
}
pub fn reject_bad_block(mut self) -> AllForksSync<TBl, TRq, TSrc> {
self.parent.inner.blocks.mark_unverified_block_as_bad(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
);
self.parent
}
pub fn finish(mut self) -> AllForksSync<TBl, TRq, TSrc> {
let pending_block = self.parent.inner.blocks.remove_unverified_block(
self.block_to_verify.block_number,
&self.block_to_verify.block_hash,
);
self.parent
.chain
.insert_verified_header(self.verified_header, pending_block.user_data);
for source in self.parent.inner.blocks.sources_user_data_iter_mut() {
let pending = mem::replace(
&mut source.pending_finality_proofs,
SourcePendingJustificationProofs::None,
);
source.unverified_finality_proofs.merge(pending)
}
self.parent
}
}
pub struct FinalityProofVerify<TBl, TRq, TSrc> {
parent: AllForksSync<TBl, TRq, TSrc>,
source_id: SourceId,
finality_proof_to_verify: FinalityProof,
}
impl<TBl, TRq, TSrc> FinalityProofVerify<TBl, TRq, TSrc> {
pub fn sender(&self) -> (SourceId, &TSrc) {
(self.source_id, &self.parent[self.source_id])
}
pub fn perform(
mut self,
randomness_seed: [u8; 32],
) -> (
AllForksSync<TBl, TRq, TSrc>,
FinalityProofVerifyOutcome<TBl>,
) {
let finality_apply = match self.finality_proof_to_verify {
FinalityProof::GrandpaCommit(scale_encoded_commit) => {
match self
.parent
.chain
.verify_grandpa_commit_message(&scale_encoded_commit, randomness_seed)
{
Ok(finality_apply) => finality_apply,
Err(blocks_tree::CommitVerifyError::FinalityVerify(
blocks_tree::FinalityVerifyError::EqualToFinalized
| blocks_tree::FinalityVerifyError::BelowFinalized,
)) => return (self.parent, FinalityProofVerifyOutcome::AlreadyFinalized),
Err(
blocks_tree::CommitVerifyError::FinalityVerify(
blocks_tree::FinalityVerifyError::UnknownTargetBlock {
block_number,
..
},
)
| blocks_tree::CommitVerifyError::FinalityVerify(
blocks_tree::FinalityVerifyError::TooFarAhead {
justification_block_number: block_number,
..
},
)
| blocks_tree::CommitVerifyError::NotEnoughKnownBlocks {
target_block_number: block_number,
},
) => {
self.parent.inner.blocks[self.source_id]
.pending_finality_proofs
.insert(
block_number,
FinalityProofs::GrandpaCommit(scale_encoded_commit),
);
return (
self.parent,
FinalityProofVerifyOutcome::GrandpaCommitPending,
);
}
Err(err) => {
return (
self.parent,
FinalityProofVerifyOutcome::GrandpaCommitError(err),
);
}
}
}
FinalityProof::Justification((consensus_engine_id, scale_encoded_justification)) => {
match self.parent.chain.verify_justification(
consensus_engine_id,
&scale_encoded_justification,
randomness_seed,
) {
Ok(finality_apply) => finality_apply,
Err(blocks_tree::JustificationVerifyError::FinalityVerify(
blocks_tree::FinalityVerifyError::EqualToFinalized
| blocks_tree::FinalityVerifyError::BelowFinalized,
)) => return (self.parent, FinalityProofVerifyOutcome::AlreadyFinalized),
Err(err) => {
return (
self.parent,
FinalityProofVerifyOutcome::JustificationError(err),
);
}
}
}
};
let finalized_blocks_iter = finality_apply.apply();
let updates_best_block = finalized_blocks_iter.updates_best_block();
let mut finalized_blocks = Vec::new();
let mut pruned_blocks = Vec::new();
for block in finalized_blocks_iter {
if matches!(block.ty, blocks_tree::RemovedBlockType::Finalized) {
finalized_blocks.push(RemovedBlock {
block_hash: block.block_hash,
block_number: block.block_number,
user_data: block.user_data,
scale_encoded_header: block.scale_encoded_header,
});
} else {
pruned_blocks.push(RemovedBlock {
block_hash: block.block_hash,
block_number: block.block_number,
user_data: block.user_data,
scale_encoded_header: block.scale_encoded_header,
});
}
}
let _finalized_blocks = self
.parent
.inner
.blocks
.set_finalized_block_height(finalized_blocks.last().unwrap().block_number);
(
self.parent,
FinalityProofVerifyOutcome::NewFinalized {
finalized_blocks_newest_to_oldest: finalized_blocks,
pruned_blocks,
updates_best_block,
},
)
}
pub fn cancel(self) -> AllForksSync<TBl, TRq, TSrc> {
self.parent
}
}
#[derive(Debug, Clone)]
pub enum GrandpaCommitMessageOutcome {
ParseError, Queued,
}
pub enum ProcessOne<TBl, TRq, TSrc> {
AllSync {
sync: AllForksSync<TBl, TRq, TSrc>,
},
BlockVerify(BlockVerify<TBl, TRq, TSrc>),
FinalityProofVerify(FinalityProofVerify<TBl, TRq, TSrc>),
}
pub enum HeaderVerifyOutcome<TBl, TRq, TSrc> {
Success {
is_new_best: bool,
success: HeaderVerifySuccess<TBl, TRq, TSrc>,
},
Error {
sync: AllForksSync<TBl, TRq, TSrc>,
error: HeaderVerifyError,
},
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum HeaderVerifyError {
UnknownConsensusEngine,
ConsensusMismatch,
#[display("{_0}")]
VerificationFailed(verify::header_only::Error),
}
#[derive(Debug)]
pub enum FinalityProofVerifyOutcome<TBl> {
NewFinalized {
finalized_blocks_newest_to_oldest: Vec<RemovedBlock<TBl>>,
pruned_blocks: Vec<RemovedBlock<TBl>>,
updates_best_block: bool,
},
AlreadyFinalized,
GrandpaCommitPending,
JustificationError(blocks_tree::JustificationVerifyError),
GrandpaCommitError(blocks_tree::CommitVerifyError),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RemovedBlock<TBl> {
pub block_hash: [u8; 32],
pub block_number: u64,
pub user_data: TBl,
pub scale_encoded_header: Vec<u8>,
}
pub struct SetFinalizedBlockResult<TBl> {
pub finalized_blocks: Vec<RemovedBlock<TBl>>,
pub pruned_blocks: Vec<RemovedBlock<TBl>>,
pub updates_best_block: bool,
}