use std::{fmt, sync::Arc, time::Duration};
use zakura_chain::{
block::{self, Block},
transaction::{UnminedTx, UnminedTxId},
};
use crate::{meta_addr::MetaAddr, protocol::internal::InventoryResponse, PeerSocketAddr};
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
use InventoryResponse::*;
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub enum Response {
Nil,
Peers(Vec<MetaAddr>),
Pong(Duration),
BlockHashes(Vec<block::Hash>),
BlockHeaders(Vec<block::CountedHeader>),
TransactionIds(Vec<UnminedTxId>),
Blocks(Vec<InventoryResponse<(Arc<Block>, Option<PeerSocketAddr>), block::Hash>>),
Transactions(Vec<InventoryResponse<(UnminedTx, Option<PeerSocketAddr>), UnminedTxId>>),
}
impl fmt::Display for Response {
#[allow(clippy::unwrap_in_result)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&match self {
Response::Nil => "Nil".to_string(),
Response::Peers(peers) => format!("Peers {{ peers: {} }}", peers.len()),
Response::Pong(duration) => format!("Pong {{ latency: {duration:?} }}"),
Response::BlockHashes(hashes) => format!("BlockHashes {{ hashes: {} }}", hashes.len()),
Response::BlockHeaders(headers) => {
format!("BlockHeaders {{ headers: {} }}", headers.len())
}
Response::TransactionIds(ids) => format!("TransactionIds {{ ids: {} }}", ids.len()),
Response::Blocks(blocks) if blocks.len() == 1 => {
match blocks.first().expect("len is 1") {
Available((block, _)) => format!(
"Block {{ height: {}, hash: {} }}",
block
.coinbase_height()
.as_ref()
.map(|h| h.0.to_string())
.unwrap_or_else(|| "None".into()),
block.hash(),
),
Missing(hash) => format!("Block {{ missing: {hash} }}"),
}
}
Response::Blocks(blocks) => format!(
"Blocks {{ blocks: {}, missing: {} }}",
blocks.iter().filter(|r| r.is_available()).count(),
blocks.iter().filter(|r| r.is_missing()).count()
),
Response::Transactions(transactions) => format!(
"Transactions {{ transactions: {}, missing: {} }}",
transactions.iter().filter(|r| r.is_available()).count(),
transactions.iter().filter(|r| r.is_missing()).count()
),
})
}
}
impl Response {
pub fn command(&self) -> &'static str {
match self {
Response::Nil => "Nil",
Response::Peers(_) => "Peers",
Response::Pong(_) => "Pong",
Response::BlockHashes(_) => "BlockHashes",
Response::BlockHeaders(_) => "BlockHeaders",
Response::TransactionIds(_) => "TransactionIds",
Response::Blocks(_) => "Blocks",
Response::Transactions(_) => "Transactions",
}
}
pub fn is_inventory_download(&self) -> bool {
matches!(self, Response::Blocks(_) | Response::Transactions(_))
}
#[allow(dead_code)]
pub fn is_nil(&self) -> bool {
matches!(self, Self::Nil)
}
}