use super::{config::*, error::*, events::HeaderSyncRequestId, validation::*, *};
pub const ZAKURA_STREAM_HEADER_SYNC: u16 = 5;
pub const ZAKURA_HEADER_SYNC_STREAM_VERSION: u16 = 7;
pub const MSG_HS_STATUS: u8 = 1;
pub const MSG_HS_GET_HEADERS: u8 = 2;
pub const MSG_HS_HEADERS: u8 = 3;
pub const MSG_HS_NEW_BLOCK: u8 = 4;
pub const MAX_HS_MESSAGE_BYTES: usize = 2 * 1024 * 1024;
pub const DEFAULT_HS_RANGE: u32 = 1000;
pub const MAX_HS_RANGE: u32 = 4000;
pub const DEFAULT_HS_MAX_INFLIGHT: u16 = 10;
pub(super) const HEADER_SYNC_MESSAGE_TYPE_BYTES: usize = 1;
pub(super) const HEADER_SYNC_REQUEST_ID_BYTES: usize = 8;
pub(super) const HEADER_SYNC_COUNT_BYTES: usize = 4;
pub(super) const HEADER_SYNC_HAS_ROOTS_BYTES: usize = 1;
pub(super) const HEADER_SYNC_BODY_SIZE_BYTES: usize = 4;
pub(super) const HEADER_SYNC_BLOCK_COMMITMENT_ROOTS_BYTES: usize =
4 + 32 + 32 + 32 + 8 + 8 + 8 + 32;
pub(super) const COMMON_HEADER_BYTES: usize = 1_487;
pub(super) const REGTEST_HEADER_BYTES: usize = 177;
pub(super) const LOCAL_MAX_HS_INFLIGHT_PER_PEER: u16 = 16;
pub(super) const HEADER_SYNC_MAX_RESIDENT_BATCHES: u32 = 16;
pub(super) const HEADER_SYNC_RETRY_AVOIDANCE: Duration = Duration::from_millis(50);
pub(super) const STATUS_PUBLICATION_RETRY_DELAY: Duration = Duration::from_millis(50);
pub(super) const HEADER_SYNC_SEEN_HASH_CAPACITY: usize = 4096;
pub(super) const DEFAULT_HS_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
pub(super) const EMPTY_HEADERS_RETRY_DELAY: Duration = Duration::from_secs(1);
pub(super) const DEFAULT_HS_STATUS_REFRESH_INTERVAL: Duration = Duration::from_secs(30);
pub(super) const DEFAULT_HS_INBOUND_STATUS_MIN_INTERVAL: Duration = Duration::from_secs(5);
pub(super) const DEFAULT_HS_INBOUND_NEW_BLOCK_MIN_INTERVAL: Duration = Duration::from_secs(5);
const _: () = assert!(MAX_HS_MESSAGE_BYTES < LOCAL_MAX_MESSAGE_BYTES as usize);
const _: () = assert!(
HEADER_SYNC_MESSAGE_TYPE_BYTES
+ HEADER_SYNC_REQUEST_ID_BYTES
+ HEADER_SYNC_COUNT_BYTES
+ (COMMON_HEADER_BYTES
+ HEADER_SYNC_BODY_SIZE_BYTES
+ HEADER_SYNC_BLOCK_COMMITMENT_ROOTS_BYTES)
* (DEFAULT_HS_RANGE as usize)
< MAX_HS_MESSAGE_BYTES
);
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum HeaderSyncMessage {
Status(HeaderSyncStatus),
GetHeaders {
start_height: block::Height,
count: u32,
want_tree_aux_roots: bool,
},
Headers {
headers: Vec<Arc<block::Header>>,
body_sizes: Vec<u32>,
tree_aux_roots: Vec<BlockCommitmentRoots>,
},
NewBlock(Arc<block::Block>),
}
impl HeaderSyncMessage {
pub fn message_type(&self) -> u8 {
match self {
Self::Status(_) => MSG_HS_STATUS,
Self::GetHeaders { .. } => MSG_HS_GET_HEADERS,
Self::Headers { .. } => MSG_HS_HEADERS,
Self::NewBlock(_) => MSG_HS_NEW_BLOCK,
}
}
pub fn encode(
&self,
request_id: Option<HeaderSyncRequestId>,
) -> Result<Vec<u8>, HeaderSyncWireError> {
let mut bytes = Vec::new();
bytes.write_u8(self.message_type())?;
match self {
Self::Status(status) => status.encode_to(&mut bytes)?,
Self::GetHeaders {
start_height,
count,
want_tree_aux_roots,
} => {
validate_get_headers_count(*count)?;
bytes.write_u64::<LittleEndian>(
request_id
.ok_or(HeaderSyncWireError::MissingRequestId {
message: "GetHeaders",
})?
.get(),
)?;
write_height(&mut bytes, *start_height)?;
bytes.write_u32::<LittleEndian>(*count)?;
bytes.write_u8(u8::from(*want_tree_aux_roots))?;
}
Self::Headers {
headers,
body_sizes,
tree_aux_roots,
} => {
validate_headers_len(headers.len(), usize_from_u32(MAX_HS_RANGE, "headers cap")?)?;
validate_body_sizes_len(headers.len(), body_sizes.len())?;
validate_tree_aux_roots_len(headers.len(), tree_aux_roots.len())?;
bytes.write_u64::<LittleEndian>(
request_id
.ok_or(HeaderSyncWireError::MissingRequestId { message: "Headers" })?
.get(),
)?;
bytes.write_u32::<LittleEndian>(u32_from_usize(headers.len(), "headers count")?)?;
bytes.write_u8(u8::from(!tree_aux_roots.is_empty()))?;
for (header, body_size) in headers.iter().zip(body_sizes) {
header.zcash_serialize(&mut bytes)?;
bytes.write_u32::<LittleEndian>(*body_size)?;
}
for roots in tree_aux_roots {
roots.zcash_serialize(&mut bytes)?;
}
}
Self::NewBlock(block) => {
block.zcash_serialize(&mut bytes)?;
}
}
if bytes.len() > MAX_HS_MESSAGE_BYTES {
return Err(HeaderSyncWireError::OversizedPayload {
actual: bytes.len(),
max: MAX_HS_MESSAGE_BYTES,
});
}
Ok(bytes)
}
pub fn decode(
bytes: &[u8],
context: HeaderSyncDecodeContext,
) -> Result<(Self, Option<HeaderSyncRequestId>), HeaderSyncWireError> {
if bytes.len() > MAX_HS_MESSAGE_BYTES {
return Err(HeaderSyncWireError::OversizedPayload {
actual: bytes.len(),
max: MAX_HS_MESSAGE_BYTES,
});
}
let mut reader = Cursor::new(bytes);
let message_type = reader.read_u8()?;
let mut request_id = None;
let message = match message_type {
MSG_HS_STATUS => Self::Status(HeaderSyncStatus::decode_from(&mut reader)?),
MSG_HS_GET_HEADERS => {
request_id = HeaderSyncRequestId::new(reader.read_u64::<LittleEndian>()?);
if request_id.is_none() {
return Err(HeaderSyncWireError::MissingRequestId {
message: "GetHeaders",
});
}
let start_height = read_height(&mut reader)?;
let count = reader.read_u32::<LittleEndian>()?;
let want_tree_aux_roots = read_bool_marker(&mut reader, "want_tree_aux_roots")?;
validate_get_headers_count(count)?;
Self::GetHeaders {
start_height,
count,
want_tree_aux_roots,
}
}
MSG_HS_HEADERS => {
request_id = HeaderSyncRequestId::new(reader.read_u64::<LittleEndian>()?);
if request_id.is_none() {
return Err(HeaderSyncWireError::MissingRequestId { message: "Headers" });
}
let count = usize_from_u32(reader.read_u32::<LittleEndian>()?, "headers count")?;
let has_roots = read_bool_marker(&mut reader, "has_roots")?;
let Some(max_headers) = context.headers_response_limit()? else {
return Err(HeaderSyncWireError::UnsolicitedHeaders);
};
if has_roots && !context.wants_tree_aux_roots() {
return Err(HeaderSyncWireError::UnrequestedTreeAuxRoots);
}
validate_headers_len(count, max_headers)?;
let mut headers = Vec::with_capacity(count);
let mut body_sizes = Vec::with_capacity(count);
let mut tree_aux_roots = if has_roots {
Vec::with_capacity(count)
} else {
Vec::new()
};
for _ in 0..count {
headers.push(Arc::new(block::Header::zcash_deserialize(&mut reader)?));
body_sizes.push(reader.read_u32::<LittleEndian>()?);
}
if has_roots {
for _ in 0..count {
tree_aux_roots.push(BlockCommitmentRoots::zcash_deserialize(&mut reader)?);
}
}
validate_tree_aux_roots_len(count, tree_aux_roots.len())?;
if let Some(requested) = context.requested {
validate_tree_aux_root_heights(requested.start_height, &tree_aux_roots)?;
}
Self::Headers {
headers,
body_sizes,
tree_aux_roots,
}
}
MSG_HS_NEW_BLOCK => {
Self::NewBlock(Arc::new(block::Block::zcash_deserialize(&mut reader)?))
}
value => return Err(HeaderSyncWireError::UnknownMessageType(value)),
};
reject_trailing(bytes, &reader)?;
Ok((message, request_id))
}
pub fn peek_headers_request_id(
bytes: &[u8],
) -> Result<HeaderSyncRequestId, HeaderSyncWireError> {
let mut reader = Cursor::new(bytes);
let message_type = reader.read_u8()?;
if message_type != MSG_HS_HEADERS {
return Err(HeaderSyncWireError::UnknownMessageType(message_type));
}
HeaderSyncRequestId::new(reader.read_u64::<LittleEndian>()?)
.ok_or(HeaderSyncWireError::MissingRequestId { message: "Headers" })
}
pub fn encode_frame(
&self,
request_id: Option<HeaderSyncRequestId>,
) -> Result<Frame, HeaderSyncWireError> {
Ok(Frame {
message_type: u16::from(self.message_type()),
flags: 0,
payload: self.encode(request_id)?,
})
}
pub fn decode_frame(
frame: Frame,
context: HeaderSyncDecodeContext,
) -> Result<(Self, Option<HeaderSyncRequestId>), HeaderSyncWireError> {
if frame.flags != 0 {
return Err(HeaderSyncWireError::UnsupportedFlags(frame.flags));
}
let (message, request_id) = Self::decode(&frame.payload, context)?;
let frame_message_type = u8::try_from(frame.message_type)
.map_err(|_| HeaderSyncWireError::UnknownFrameMessageType(frame.message_type))?;
if frame_message_type != message.message_type() {
return Err(HeaderSyncWireError::MismatchedFrameMessageType {
frame: frame.message_type,
payload: message.message_type(),
});
}
Ok((message, request_id))
}
}