use crate::util::protobuf;
use alloc::{borrow::ToOwned as _, vec::Vec};
use core::num::NonZero;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlocksRequestConfig {
pub start: BlocksRequestConfigStart,
pub desired_count: NonZero<u32>,
pub direction: BlocksRequestDirection,
pub fields: BlocksRequestFields,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlocksRequestDirection {
Ascending,
Descending,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlocksRequestFields {
pub header: bool,
pub body: bool,
pub justifications: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlocksRequestConfigStart {
Hash([u8; 32]),
Number(u64),
}
pub fn build_block_request(
block_number_bytes: usize,
config: &BlocksRequestConfig,
) -> impl Iterator<Item = impl AsRef<[u8]>> {
let mut fields = 0u32;
if config.fields.header {
fields |= 1 << 24;
}
if config.fields.body {
fields |= 1 << 25;
}
if config.fields.justifications {
fields |= 1 << 28;
}
let from_block = match config.start {
BlocksRequestConfigStart::Hash(h) => {
either::Left(protobuf::bytes_tag_encode(2, h).map(either::Left))
}
BlocksRequestConfigStart::Number(n) => {
let mut data = Vec::with_capacity(block_number_bytes);
data.extend_from_slice(&n.to_le_bytes());
data.resize(block_number_bytes, 0);
either::Right(protobuf::bytes_tag_encode(3, data).map(either::Right))
}
};
protobuf::uint32_tag_encode(1, fields)
.map(either::Left)
.map(either::Left)
.map(either::Left)
.chain(
from_block
.map(either::Right)
.map(either::Left)
.map(either::Left),
)
.chain(
protobuf::enum_tag_encode(
5,
match config.direction {
BlocksRequestDirection::Ascending => 0,
BlocksRequestDirection::Descending => 1,
},
)
.map(either::Left)
.map(either::Right)
.map(either::Left),
)
.chain(
protobuf::uint32_tag_encode(6, config.desired_count.get())
.map(either::Right)
.map(either::Right)
.map(either::Left),
)
.chain(protobuf::bool_tag_encode(7, true).map(either::Right))
}
pub fn decode_block_request(
expected_block_number_bytes: usize,
request_bytes: &[u8],
) -> Result<BlocksRequestConfig, DecodeBlockRequestError> {
let mut parser = nom::combinator::all_consuming::<_, nom::error::Error<&[u8]>, _>(
nom::combinator::complete(protobuf::message_decode! {
#[required] fields = 1 => protobuf::uint32_tag_decode,
#[optional] hash = 2 => protobuf::bytes_tag_decode,
#[optional] number = 3 => protobuf::bytes_tag_decode,
#[optional] direction = 5 => protobuf::enum_tag_decode,
#[optional] max_blocks = 6 => protobuf::uint32_tag_decode,
}),
);
let decoded = match nom::Finish::finish(nom::Parser::parse(&mut parser, request_bytes)) {
Ok((_, rq)) => rq,
Err(_) => return Err(DecodeBlockRequestError::ProtobufDecode),
};
Ok(BlocksRequestConfig {
start: match (decoded.hash, decoded.number) {
(Some(h), None) => BlocksRequestConfigStart::Hash(
<[u8; 32]>::try_from(h)
.map_err(|_| DecodeBlockRequestError::InvalidBlockHashLength)?,
),
(None, Some(n)) => {
if n.len() != expected_block_number_bytes {
return Err(DecodeBlockRequestError::InvalidBlockNumber);
}
let mut num = 0u64;
let mut shift = 0u32;
for byte in n {
let shifted = u64::from(*byte)
.checked_mul(1 << shift)
.ok_or(DecodeBlockRequestError::InvalidBlockNumber)?;
num = num
.checked_add(shifted)
.ok_or(DecodeBlockRequestError::InvalidBlockNumber)?;
shift = shift
.checked_add(8)
.ok_or(DecodeBlockRequestError::InvalidBlockNumber)?;
}
BlocksRequestConfigStart::Number(num)
}
(Some(_), Some(_)) => return Err(DecodeBlockRequestError::ProtobufDecode),
(None, None) => return Err(DecodeBlockRequestError::MissingStartBlock),
},
desired_count: {
NonZero::<u32>::new(decoded.max_blocks.unwrap_or(u32::MAX))
.unwrap_or(NonZero::<u32>::new(u32::MAX).unwrap())
},
direction: match decoded.direction {
None | Some(0) => BlocksRequestDirection::Ascending,
Some(1) => BlocksRequestDirection::Descending,
Some(_) => return Err(DecodeBlockRequestError::InvalidDirection),
},
fields: {
if (decoded.fields & !(1 << 24 | 1 << 25 | 1 << 28)) != 0 {
return Err(DecodeBlockRequestError::UnknownFieldBits);
}
BlocksRequestFields {
header: (decoded.fields & (1 << 24)) != 0,
body: (decoded.fields & (1 << 25)) != 0,
justifications: (decoded.fields & (1 << 28)) != 0,
}
},
})
}
pub fn build_block_response(response: Vec<BlockData>) -> impl Iterator<Item = impl AsRef<[u8]>> {
response.into_iter().flat_map(|block| {
protobuf::message_tag_encode(1, {
let justifications = if let Some(justifications) = block.justifications {
let mut j = Vec::with_capacity(
4 + justifications
.iter()
.fold(0, |sz, j| sz + 4 + 6 + j.justification.len()),
);
j.extend_from_slice(
crate::util::encode_scale_compact_usize(justifications.len()).as_ref(),
);
for justification in &justifications {
j.extend_from_slice(&justification.engine_id);
j.extend_from_slice(
crate::util::encode_scale_compact_usize(justification.justification.len())
.as_ref(),
);
j.extend_from_slice(&justification.justification);
}
Some(j)
} else {
None
};
protobuf::bytes_tag_encode(1, block.hash)
.map(either::Left)
.chain(
block
.header
.into_iter()
.flat_map(|h| protobuf::bytes_tag_encode(2, h))
.map(either::Right),
)
.map(either::Left)
.chain(
block
.body
.into_iter()
.flat_map(|b| b.into_iter())
.flat_map(|tx| protobuf::bytes_tag_encode(3, tx))
.map(either::Left)
.chain(
justifications
.into_iter()
.flat_map(|j| protobuf::bytes_tag_encode(8, j))
.map(either::Right),
)
.map(either::Right),
)
})
})
}
pub fn decode_block_response(
response_bytes: &[u8],
) -> Result<Vec<BlockData>, DecodeBlockResponseError> {
let mut parser = nom::combinator::all_consuming::<_, nom::error::Error<&[u8]>, _>(
nom::combinator::complete(protobuf::message_decode! {
#[repeated(max = 32768)] blocks = 1 => protobuf::message_tag_decode(protobuf::message_decode!{
#[required] hash = 1 => protobuf::bytes_tag_decode,
#[optional] header = 2 => protobuf::bytes_tag_decode,
#[repeated(max = usize::MAX)] body = 3 => protobuf::bytes_tag_decode,
#[optional] justifications = 8 => protobuf::bytes_tag_decode,
}),
}),
);
let blocks = match nom::Finish::finish(nom::Parser::parse(&mut parser, response_bytes)) {
Ok((_, out)) => out.blocks,
Err(_) => return Err(DecodeBlockResponseError::ProtobufDecode),
};
let mut blocks_out = Vec::with_capacity(blocks.len());
for block in blocks {
if block.hash.len() != 32 {
return Err(DecodeBlockResponseError::InvalidHashLength);
}
blocks_out.push(BlockData {
hash: <[u8; 32]>::try_from(block.hash).unwrap(),
header: block.header.as_ref().map(|h| h.to_vec()),
body: Some(block.body.into_iter().map(|tx| tx.to_vec()).collect()),
justifications: if let Some(justifications) = block.justifications {
let result: nom::IResult<_, _> = nom::Parser::parse(
&mut nom::combinator::all_consuming(nom::combinator::complete(
decode_justifications,
)),
justifications,
);
match result {
Ok((_, out)) => Some(out),
Err(nom::Err::Error(_) | nom::Err::Failure(_)) => {
return Err(DecodeBlockResponseError::InvalidJustifications);
}
Err(_) => unreachable!(),
}
} else {
None
},
});
}
Ok(blocks_out)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockData {
pub hash: [u8; 32],
pub header: Option<Vec<u8>>,
pub body: Option<Vec<Vec<u8>>>,
pub justifications: Option<Vec<Justification>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Justification {
pub engine_id: [u8; 4],
pub justification: Vec<u8>,
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum DecodeBlockRequestError {
ProtobufDecode,
ZeroBlocksRequested,
InvalidDirection,
MissingStartBlock,
InvalidBlockNumber,
InvalidBlockHashLength,
UnknownFieldBits,
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum DecodeBlockResponseError {
ProtobufDecode,
InvalidHashLength,
BodyDecodeError,
InvalidJustifications,
}
fn decode_justifications<'a, E: nom::error::ParseError<&'a [u8]>>(
bytes: &'a [u8],
) -> nom::IResult<&'a [u8], Vec<Justification>, E> {
nom::Parser::parse(
&mut nom::combinator::flat_map(crate::util::nom_scale_compact_usize, |num_elems| {
nom::multi::many_m_n(
num_elems,
num_elems,
nom::combinator::map(
(
nom::bytes::streaming::take(4u32),
crate::util::nom_bytes_decode,
),
move |(consensus_engine, justification)| Justification {
engine_id: <[u8; 4]>::try_from(consensus_engine).unwrap(),
justification: justification.to_owned(),
},
),
)
}),
bytes,
)
}
#[cfg(test)]
mod tests {
#[test]
fn regression_2339() {
let _ = super::decode_block_request(4, &[26, 10]);
}
#[test]
fn regression_incomplete_justification() {
let _ = super::decode_block_response(&[
200, 200, 255, 255, 10, 8, 0, 47, 0, 1, 26, 0, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
88, 88, 88, 88, 88, 88, 1, 10, 1, 255, 2, 0, 0, 1, 255, 2, 10, 0, 36, 1, 8, 105, 105,
105, 105, 105, 105, 97, 105, 105, 88, 1, 0, 0, 88, 88, 88, 88, 88, 88, 10, 175, 10, 0,
105, 1, 10, 1, 255, 2, 0, 0, 10, 4, 66, 0, 66, 38, 88, 88, 18, 0, 88, 26, 0, 8, 5, 0,
0, 0, 0, 0, 0, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 88, 88, 88, 88, 88, 0, 0,
88, 88, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 255, 0, 2, 10, 0, 36, 1, 8, 105,
105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 10, 48,
10, 0, 105, 1, 10, 2, 0, 12, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10,
1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88,
88, 0, 88, 88, 36, 10, 1, 255, 2, 10, 0, 36, 1, 8, 0, 1, 26, 0, 88, 88, 88, 88, 88, 88,
10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 244,
1, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18,
0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 26, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2,
10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 18, 0, 0, 0, 0, 0,
0, 0, 88, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36,
10, 1, 255, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0, 0, 0, 0, 8, 105, 105, 105, 105, 105, 105,
97, 105, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 128, 0, 0, 0, 32, 0,
0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88, 88, 36, 10, 1, 255, 2, 10, 0,
105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88,
88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1,
88, 88, 88, 88, 36, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 47, 0, 1, 0, 0, 88, 88,
88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0,
26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105,
105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 10, 32, 10, 0, 105, 139,
10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1,
0, 1, 26, 0, 88, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 18,
0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255,
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10,
1, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105,
105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0,
0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0,
0, 0, 0, 8, 105, 105, 105, 105, 105, 105, 97, 105, 88, 88, 88, 88, 88, 10, 48, 10, 0,
105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36,
10, 1, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 93, 105, 105, 105, 105, 105,
97, 105, 105, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1,
255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10,
0, 105, 1, 8, 105, 105, 105, 105, 97, 105, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1,
10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88,
88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 93, 105, 105, 105, 105, 105, 97, 105,
105, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105,
1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88,
88, 10, 32, 10, 0, 105, 139, 10, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10,
1, 255, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0, 0, 0, 0, 8, 105, 105, 105, 105, 105, 105, 97,
105, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0,
0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1,
8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88,
10, 48, 10, 0, 105, 0, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88,
88, 88, 36, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88,
88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1,
88, 88, 88, 88, 36, 142, 1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97,
105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 10, 32, 10, 0, 105, 139, 10, 1, 255,
2, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88,
88, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 36, 1, 8, 105, 105, 105, 105,
105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 2, 0, 0, 0, 0, 32, 88, 36, 10, 1,
255, 255, 255, 251, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0, 0, 0, 0, 8, 105, 105, 105, 105,
105, 105, 97, 105, 88, 88, 88, 88, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 5, 26, 1, 88, 88,
88, 88, 36, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88,
88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0, 18, 0, 26,
1, 88, 88, 88, 88, 36, 142, 1, 255, 2, 255, 10, 0, 105, 1, 8, 105, 255, 2, 10, 0, 36,
1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 2, 0, 0,
0, 0, 1, 255, 2, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
]);
}
#[test]
fn regression_2833() {
let decoded = super::decode_block_request(
4,
&[
8, 128, 128, 128, 136, 1, 26, 4, 237, 91, 33, 0, 48, 1, 56, 1,
],
)
.unwrap();
assert!(matches!(
decoded.direction,
super::BlocksRequestDirection::Ascending
));
}
}