#[cfg(any(test, feature = "proptest-impl"))]
use super::state::BlockSyncFrontiers;
use super::{request::*, *};
use std::num::NonZeroU64;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct BlockSyncBlockMeta {
pub height: block::Height,
pub hash: block::Hash,
pub size: BlockSizeEstimate,
}
#[derive(Clone, Debug)]
pub enum BlockSyncEvent {
PeerConnected(BlockSyncPeerSession),
PeerDisconnected(ZakuraPeerId),
RetryBodyAvailability {
hash: block::Hash,
},
#[cfg(any(test, feature = "proptest-impl"))]
HeaderTipChanged {
height: block::Height,
hash: block::Hash,
},
#[cfg(any(test, feature = "proptest-impl"))]
StateFrontiersChanged(BlockSyncFrontiers),
#[cfg(any(test, feature = "proptest-impl"))]
ChainTipGrow(BlockSyncFrontiers),
#[cfg(any(test, feature = "proptest-impl"))]
ChainTipReset(BlockSyncFrontiers),
ScopedNeededBlocks {
query_id: NonZeroU64,
scope: zakura_header_chain::BodyWorkAuthority,
body_anchor: zakura_header_chain::Frontier,
blocks: Vec<BlockSyncBlockMeta>,
},
#[cfg(test)]
NeededBlocks(Vec<BlockSyncBlockMeta>),
BlockApplyFinished {
owner: zakura_header_chain::BodyWorkOwner,
source: zakura_header_chain::SourceId,
token: BlockApplyToken,
height: block::Height,
hash: block::Hash,
outcome: BlockApplyOutcome,
},
BlockRangeResponseFinished {
peer: ZakuraPeerId,
start_height: block::Height,
requested_count: u32,
returned_count: u32,
},
BlockRangeResponseReady {
peer: ZakuraPeerId,
start_height: block::Height,
requested_count: u32,
blocks: Vec<(block::Height, Arc<block::Block>, usize)>,
},
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BlockApplyResult {
Committed,
Duplicate,
Rejected,
Unavailable,
TimedOut,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BlockApplyOutcome {
verification: Box<zakura_header_chain::BodyVerificationOutcome>,
duplicate: bool,
}
impl BlockApplyOutcome {
pub fn committed(evidence: zakura_header_chain::VerifiedBodyEvidence) -> Self {
Self {
verification: Box::new(zakura_header_chain::BodyVerificationOutcome::Verified(
evidence,
)),
duplicate: false,
}
}
pub fn duplicate(evidence: zakura_header_chain::VerifiedBodyEvidence) -> Self {
Self {
verification: Box::new(zakura_header_chain::BodyVerificationOutcome::Verified(
evidence,
)),
duplicate: true,
}
}
pub fn payload_mismatch(evidence: zakura_header_chain::BodyPayloadMismatch) -> Self {
Self {
verification: Box::new(
zakura_header_chain::BodyVerificationOutcome::PayloadMismatch(evidence),
),
duplicate: false,
}
}
pub fn consensus_invalid(evidence: zakura_header_chain::ConsensusBodyInvalid) -> Self {
Self {
verification: Box::new(
zakura_header_chain::BodyVerificationOutcome::ConsensusInvalid(evidence),
),
duplicate: false,
}
}
pub fn retryable(evidence: zakura_header_chain::TransientBodyFailure) -> Self {
Self {
verification: Box::new(zakura_header_chain::BodyVerificationOutcome::Retryable(
evidence,
)),
duplicate: false,
}
}
pub fn verification(&self) -> &zakura_header_chain::BodyVerificationOutcome {
self.verification.as_ref()
}
pub fn into_verification(self) -> zakura_header_chain::BodyVerificationOutcome {
*self.verification
}
pub(crate) fn attributed_source(&self) -> Option<zakura_header_chain::SourceId> {
match self.verification.as_ref() {
zakura_header_chain::BodyVerificationOutcome::PayloadMismatch(evidence) => {
Some(evidence.source)
}
zakura_header_chain::BodyVerificationOutcome::ConsensusInvalid(evidence) => {
Some(evidence.source)
}
zakura_header_chain::BodyVerificationOutcome::Verified(_)
| zakura_header_chain::BodyVerificationOutcome::Retryable(_) => None,
}
}
pub(crate) fn retryable_mut(
&mut self,
) -> Option<&mut zakura_header_chain::TransientBodyFailure> {
match self.verification.as_mut() {
zakura_header_chain::BodyVerificationOutcome::Retryable(failure) => Some(failure),
_ => None,
}
}
pub fn evidence(&self) -> zakura_header_chain::EvidenceId {
match self.verification.as_ref() {
zakura_header_chain::BodyVerificationOutcome::Verified(evidence) => evidence.evidence,
zakura_header_chain::BodyVerificationOutcome::PayloadMismatch(evidence) => {
evidence.evidence
}
zakura_header_chain::BodyVerificationOutcome::ConsensusInvalid(evidence) => {
evidence.evidence
}
zakura_header_chain::BodyVerificationOutcome::Retryable(evidence) => evidence.evidence,
}
}
pub fn result(&self) -> BlockApplyResult {
match self.verification.as_ref() {
zakura_header_chain::BodyVerificationOutcome::Verified(_) if self.duplicate => {
BlockApplyResult::Duplicate
}
zakura_header_chain::BodyVerificationOutcome::Verified(_) => {
BlockApplyResult::Committed
}
zakura_header_chain::BodyVerificationOutcome::PayloadMismatch(_)
| zakura_header_chain::BodyVerificationOutcome::ConsensusInvalid(_) => {
BlockApplyResult::Rejected
}
zakura_header_chain::BodyVerificationOutcome::Retryable(evidence)
if evidence.kind == zakura_header_chain::TransientBodyFailureKind::Timeout =>
{
BlockApplyResult::TimedOut
}
zakura_header_chain::BodyVerificationOutcome::Retryable(_) => {
BlockApplyResult::Unavailable
}
}
}
}
pub type BlockApplyToken = u64;
#[derive(Clone, Debug)]
pub enum BlockSyncAction {
QueryNeededBlocks {
query_id: NonZeroU64,
from: block::Height,
limit: u32,
best_header_tip: block::Height,
scope: zakura_header_chain::BodyWorkAuthority,
},
QueryBlocksByHeightRange {
peer: ZakuraPeerId,
start: block::Height,
count: u32,
},
SubmitBlock {
owner: zakura_header_chain::BodyWorkOwner,
source: zakura_header_chain::SourceId,
token: BlockApplyToken,
block: Arc<block::Block>,
},
RecordBodyUnavailable {
expected_version: zakura_header_chain::StateVersion,
failure: zakura_header_chain::TransientBodyFailure,
},
RecordBodyInvalid {
expected_version: zakura_header_chain::StateVersion,
invalid: zakura_header_chain::ConsensusBodyInvalid,
},
RestartBodyAvailability {
expected_version: zakura_header_chain::StateVersion,
discovery: zakura_header_chain::BodySupplierDiscovered,
},
RetryBodyAvailability {
expected_version: zakura_header_chain::StateVersion,
retry: zakura_header_chain::OperatorBodyRetry,
},
Misbehavior {
peer: ZakuraPeerId,
reason: BlockSyncMisbehavior,
},
}
impl BlockSyncAction {
pub(super) fn metric_label(&self) -> &'static str {
match self {
Self::QueryNeededBlocks { .. } => "query_needed_blocks",
Self::QueryBlocksByHeightRange { .. } => "query_blocks_by_height_range",
Self::SubmitBlock { .. } => "submit_block",
Self::RecordBodyUnavailable { .. } => "record_body_unavailable",
Self::RecordBodyInvalid { .. } => "record_body_invalid",
Self::RestartBodyAvailability { .. } => "restart_body_availability",
Self::RetryBodyAvailability { .. } => "retry_body_availability",
Self::Misbehavior { .. } => "misbehavior",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BlockSyncMisbehavior {
MalformedMessage,
UnsolicitedBlock,
GetBlocksTooLong,
GetBlocksSpam,
BodyPayloadMismatch(zakura_header_chain::BodyPayloadMismatch),
ConsensusBodyInvalid(zakura_header_chain::ConsensusBodyInvalid),
InvalidBlock,
SizeMismatch,
InvalidStatus,
UnsolicitedDone,
RangeUnavailable,
StatusSpam,
}
#[derive(Clone, Debug)]
pub(super) enum RoutineToReactor {
StatusReceived {
peer: ZakuraPeerId,
send_reply: bool,
},
ServeGetBlocks {
peer: ZakuraPeerId,
start_height: block::Height,
count: u32,
},
RequeryNeeded,
Misbehavior {
peer: ZakuraPeerId,
reason: BlockSyncMisbehavior,
},
}