use super::{error::*, validation::*, wire::*, *};
use crate::zakura::ServicePeerLimits;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct HeaderSyncStatus {
pub tip_height: block::Height,
pub tip_hash: block::Hash,
pub anchor_height: block::Height,
pub max_headers_per_response: u32,
pub max_inflight_requests: u16,
}
impl HeaderSyncStatus {
pub(super) fn encode_to<W: Write>(&self, writer: &mut W) -> Result<(), HeaderSyncWireError> {
write_height(writer, self.tip_height)?;
self.tip_hash.zcash_serialize(&mut *writer)?;
write_height(writer, self.anchor_height)?;
writer.write_u32::<LittleEndian>(clamp_advertised_range(self.max_headers_per_response))?;
writer.write_u16::<LittleEndian>(self.max_inflight_requests)?;
Ok(())
}
pub(super) fn decode_from<R: Read>(reader: &mut R) -> Result<Self, HeaderSyncWireError> {
Ok(Self {
tip_height: read_height(reader)?,
tip_hash: block::Hash::zcash_deserialize(&mut *reader)?,
anchor_height: read_height(reader)?,
max_headers_per_response: clamp_advertised_range(reader.read_u32::<LittleEndian>()?),
max_inflight_requests: reader.read_u16::<LittleEndian>()?,
})
}
}
impl Default for HeaderSyncStatus {
fn default() -> Self {
Self {
tip_height: block::Height::MIN,
tip_hash: block::Hash([0; 32]),
anchor_height: block::Height::MIN,
max_headers_per_response: DEFAULT_HS_RANGE,
max_inflight_requests: DEFAULT_HS_MAX_INFLIGHT,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct ZakuraHeaderSyncConfig {
pub max_headers_per_response: u32,
pub max_inflight_requests: u16,
#[serde(with = "humantime_serde")]
pub status_refresh_interval: Duration,
pub peer_limits: ServicePeerLimits,
pub accept_new_blocks: bool,
pub anchor_height: Option<block::Height>,
pub anchor_hash: Option<block::Hash>,
}
impl Default for ZakuraHeaderSyncConfig {
fn default() -> Self {
Self {
max_headers_per_response: DEFAULT_HS_RANGE,
max_inflight_requests: DEFAULT_HS_MAX_INFLIGHT,
status_refresh_interval: DEFAULT_HS_STATUS_REFRESH_INTERVAL,
peer_limits: ServicePeerLimits::default(),
accept_new_blocks: true,
anchor_height: None,
anchor_hash: None,
}
}
}
impl ZakuraHeaderSyncConfig {
pub fn advertised_max_headers_per_response(&self) -> u32 {
clamp_advertised_range(self.max_headers_per_response)
}
pub fn advertised_max_inflight_requests(&self) -> u16 {
self.max_inflight_requests
.clamp(1, LOCAL_MAX_HS_INFLIGHT_PER_PEER)
}
pub fn anchor(
&self,
network: &Network,
) -> Result<(block::Height, block::Hash), HeaderSyncStartError> {
match (self.anchor_height, self.anchor_hash) {
(Some(height), Some(hash)) => Ok((height, hash)),
(None, None) => Ok((block::Height(0), network.genesis_hash())),
_ => Err(HeaderSyncStartError::IncompleteAnchor),
}
}
}
pub fn header_sync_header_bytes_for_network(network: &Network) -> usize {
if network
.parameters()
.is_some_and(|parameters| parameters.is_regtest())
{
REGTEST_HEADER_BYTES
} else {
COMMON_HEADER_BYTES
}
}
pub fn header_sync_count_by_byte_budget(
network: &Network,
max_frame_bytes: u32,
want_tree_aux_roots: bool,
) -> u32 {
let frame_payload_cap = usize::try_from(max_frame_bytes)
.unwrap_or(usize::MAX)
.saturating_sub(FRAME_HEADER_BYTES);
let payload_cap = MAX_HS_MESSAGE_BYTES.min(frame_payload_cap);
let root_bytes = if want_tree_aux_roots {
HEADER_SYNC_BLOCK_COMMITMENT_ROOTS_BYTES
} else {
0
};
let header_bytes = header_sync_header_bytes_for_network(network)
.saturating_add(HEADER_SYNC_BODY_SIZE_BYTES)
.saturating_add(root_bytes);
let count = payload_cap.saturating_sub(
HEADER_SYNC_MESSAGE_TYPE_BYTES
+ HEADER_SYNC_REQUEST_ID_BYTES
+ HEADER_SYNC_COUNT_BYTES
+ HEADER_SYNC_HAS_ROOTS_BYTES,
) / header_bytes;
u32::try_from(count)
.unwrap_or(u32::MAX)
.clamp(1, MAX_HS_RANGE)
}
pub fn clamp_header_sync_request_count(
desired_count: u32,
peer_max_headers_per_response: u32,
network: &Network,
max_frame_bytes: u32,
want_tree_aux_roots: bool,
) -> u32 {
desired_count
.min(clamp_advertised_range(peer_max_headers_per_response))
.min(MAX_HS_RANGE)
.min(header_sync_count_by_byte_budget(
network,
max_frame_bytes,
want_tree_aux_roots,
))
.max(1)
}
pub fn inbound_get_headers_count_limit(
config: &ZakuraHeaderSyncConfig,
network: &Network,
max_frame_bytes: u32,
want_tree_aux_roots: bool,
) -> u32 {
clamp_header_sync_request_count(
u32::MAX,
config.advertised_max_headers_per_response(),
network,
max_frame_bytes,
want_tree_aux_roots,
)
}