snarkos_node_bft_events/
block_response.rs1use super::*;
17
18#[derive(Clone, PartialEq, Eq)]
19pub struct BlockResponse<N: Network> {
20 pub request: BlockRequest,
22 pub blocks: Data<DataBlocks<N>>,
24}
25
26impl<N: Network> EventTrait for BlockResponse<N> {
27 #[inline]
29 fn name(&self) -> Cow<'static, str> {
30 let start = self.request.start_height;
31 let end = self.request.end_height;
32 match start + 1 == end {
33 true => format!("BlockResponse {start}"),
34 false => format!("BlockResponse {start}..{end}"),
35 }
36 .into()
37 }
38}
39
40impl<N: Network> ToBytes for BlockResponse<N> {
41 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
42 self.request.write_le(&mut writer)?;
43 self.blocks.write_le(&mut writer)
44 }
45}
46
47impl<N: Network> FromBytes for BlockResponse<N> {
48 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
49 let request = BlockRequest::read_le(&mut reader)?;
50 let blocks = Data::read_le(&mut reader)?;
51
52 Ok(Self { request, blocks })
53 }
54}
55
56impl<N: Network> std::fmt::Debug for BlockResponse<N> {
57 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
58 write!(f, "{}", self.name())
59 }
60}
61
62#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
64pub struct DataBlocks<N: Network>(pub Vec<Block<N>>);
65
66impl<N: Network> DataBlocks<N> {
67 pub const MAXIMUM_NUMBER_OF_BLOCKS: u8 = 5;
69
70 pub fn ensure_response_is_well_formed(
72 &self,
73 peer_ip: SocketAddr,
74 start_height: u32,
75 end_height: u32,
76 ) -> Result<()> {
77 ensure!(!self.0.is_empty(), "Peer '{peer_ip}' sent an empty block response ({start_height}..{end_height})");
79 if !self.0.windows(2).all(|w| w[0].height() + 1 == w[1].height()) {
81 bail!("Peer '{peer_ip}' sent an invalid block response (blocks are not sequentially ordered)")
82 }
83
84 let candidate_start_height = self.first().map(|b| b.height()).unwrap_or(0);
86 let candidate_end_height = 1 + self.last().map(|b| b.height()).unwrap_or(0);
87 if start_height != candidate_start_height || end_height != candidate_end_height {
89 bail!("Peer '{peer_ip}' sent an invalid block response (range does not match block request)")
90 }
91 Ok(())
92 }
93}
94
95impl<N: Network> std::ops::Deref for DataBlocks<N> {
96 type Target = Vec<Block<N>>;
97
98 fn deref(&self) -> &Self::Target {
100 &self.0
101 }
102}
103
104impl<N: Network> ToBytes for DataBlocks<N> {
105 #[inline]
107 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
108 let num_blocks = self.0.len() as u8;
110 if num_blocks > Self::MAXIMUM_NUMBER_OF_BLOCKS {
112 return Err(error("Block response exceeds maximum number of blocks"));
113 }
114 num_blocks.write_le(&mut writer)?;
116 self.0.iter().take(num_blocks as usize).try_for_each(|block| block.write_le(&mut writer))
118 }
119}
120
121impl<N: Network> FromBytes for DataBlocks<N> {
122 #[inline]
124 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
125 let num_blocks = u8::read_le(&mut reader)?;
127 if num_blocks > Self::MAXIMUM_NUMBER_OF_BLOCKS {
129 return Err(error("Block response exceeds maximum number of blocks"));
130 }
131 let blocks = (0..num_blocks).map(|_| Block::read_le(&mut reader)).collect::<Result<Vec<_>, _>>()?;
133 Ok(Self(blocks))
134 }
135}
136
137#[cfg(test)]
138pub mod prop_tests {
139 use crate::{BlockResponse, DataBlocks, block_request::prop_tests::any_block_request};
140 use snarkvm::{
141 ledger::snarkvm_ledger_test_helpers::sample_genesis_block,
142 prelude::{FromBytes, TestRng, ToBytes, block::Block, narwhal::Data},
143 };
144
145 use bytes::{Buf, BufMut, BytesMut};
146 use proptest::{
147 collection::vec,
148 prelude::{BoxedStrategy, Strategy, any},
149 };
150 use test_strategy::proptest;
151
152 type CurrentNetwork = snarkvm::prelude::MainnetV0;
153
154 pub fn any_block() -> BoxedStrategy<Block<CurrentNetwork>> {
155 any::<u64>().prop_map(|seed| sample_genesis_block(&mut TestRng::fixed(seed))).boxed()
156 }
157
158 pub fn any_data_blocks() -> BoxedStrategy<DataBlocks<CurrentNetwork>> {
159 vec(any_block(), 0..=1).prop_map(DataBlocks).boxed()
160 }
161
162 pub fn any_block_response() -> BoxedStrategy<BlockResponse<CurrentNetwork>> {
163 (any_block_request(), any_data_blocks())
164 .prop_map(|(request, data_blocks)| BlockResponse { request, blocks: Data::Object(data_blocks) })
165 .boxed()
166 }
167
168 #[proptest]
169 fn block_response_roundtrip(#[strategy(any_block_response())] block_response: BlockResponse<CurrentNetwork>) {
170 let mut bytes = BytesMut::default().writer();
171 block_response.write_le(&mut bytes).unwrap();
172 let decoded = BlockResponse::<CurrentNetwork>::read_le(&mut bytes.into_inner().reader()).unwrap();
173 assert_eq!(block_response.request, decoded.request);
174 assert_eq!(
175 block_response.blocks.deserialize_blocking().unwrap(),
176 decoded.blocks.deserialize_blocking().unwrap(),
177 );
178 }
179}