fuel_core_p2p/codecs/
postcard.rs1use super::{
2 Decode,
3 Encode,
4 gossipsub::GossipsubMessageHandler,
5 request_response::RequestResponseMessageHandler,
6};
7
8use std::{
9 borrow::Cow,
10 io,
11 num::NonZeroU32,
12};
13
14#[derive(Clone, Default)]
15pub struct PostcardCodec;
16
17impl RequestResponseMessageHandler<PostcardCodec> {
18 pub fn new(max_block_size: NonZeroU32) -> Self {
19 Self {
20 codec: PostcardCodec,
21 max_response_size: max_block_size,
22 }
23 }
24}
25
26impl GossipsubMessageHandler<PostcardCodec> {
27 pub fn new() -> Self {
28 GossipsubMessageHandler {
29 codec: PostcardCodec,
30 }
31 }
32}
33
34impl<T> Encode<T> for PostcardCodec
35where
36 T: ?Sized + serde::Serialize,
37{
38 type Encoder<'a>
39 = Cow<'a, [u8]>
40 where
41 T: 'a;
42 type Error = io::Error;
43
44 fn encode<'a>(&self, value: &'a T) -> Result<Self::Encoder<'a>, Self::Error> {
45 Ok(Cow::Owned(postcard::to_allocvec(value).map_err(|e| {
46 io::Error::new(io::ErrorKind::Other, e.to_string())
47 })?))
48 }
49}
50
51impl<T> Decode<T> for PostcardCodec
52where
53 T: serde::de::DeserializeOwned,
54{
55 type Error = io::Error;
56
57 fn decode(&self, bytes: &[u8]) -> Result<T, Self::Error> {
58 postcard::from_bytes(bytes)
59 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
60 }
61}
62
63#[cfg(test)]
64#[allow(non_snake_case)]
65mod tests {
66 use fuel_core_types::{
67 blockchain::SealedBlockHeader,
68 fuel_tx::Transaction,
69 services::p2p::NetworkableTransactionPool,
70 };
71 use libp2p::request_response::Codec;
72
73 use super::*;
74 use crate::{
75 codecs::request_response::RequestResponseMessageHandler,
76 request_response::{
77 messages::{
78 RequestMessage,
79 ResponseMessageErrorCode,
80 V1ResponseMessage,
81 V2ResponseMessage,
82 },
83 protocols::RequestResponseProtocol,
84 },
85 };
86
87 const MAX_REQUEST_SIZE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1024) };
88
89 #[test]
90 fn test_request_size_fits() {
91 let arbitrary_range = 2..6;
92 let m = RequestMessage::Transactions(arbitrary_range);
93 assert!(
94 postcard::to_stdvec(&m).unwrap().len() <= MAX_REQUEST_SIZE.get() as usize
95 );
96 }
97
98 #[tokio::test]
99 async fn codec__serialization_roundtrip_using_v2_on_successful_response_returns_original_value__sealed_headers()
100 {
101 let sealed_block_headers = vec![SealedBlockHeader::default()];
103 let response = V2ResponseMessage::SealedHeaders(Ok(sealed_block_headers.clone()));
104 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
105 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
106 let mut buf = Vec::with_capacity(1024);
107
108 codec
110 .write_response(&RequestResponseProtocol::V2, &mut buf, response)
111 .await
112 .expect("Valid Vec<SealedBlockHeader> should be serialized using v1");
113
114 let deserialized = codec
115 .read_response(&RequestResponseProtocol::V2, &mut buf.as_slice())
116 .await
117 .expect("Valid Vec<SealedBlockHeader> should be deserialized using v1");
118
119 assert!(matches!(
121 deserialized,
122 V2ResponseMessage::SealedHeaders(Ok(sealed_headers)) if sealed_headers == sealed_block_headers
123 ));
124 }
125
126 #[tokio::test]
127 async fn codec__serialization_roundtrip_using_v2_on_successful_response_returns_original_value__full_transactions()
128 {
129 let full_transactions = vec![Some(NetworkableTransactionPool::Transaction(
131 Transaction::default_test_tx(),
132 ))];
133 let response =
134 V2ResponseMessage::TxPoolFullTransactions(Ok(full_transactions.clone()));
135 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
136 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
137 let mut buf = Vec::with_capacity(1024);
138
139 codec
141 .write_response(&RequestResponseProtocol::V2, &mut buf, response)
142 .await
143 .expect("Valid full transactions should be serialized using v2");
144
145 let deserialized = codec
146 .read_response(&RequestResponseProtocol::V2, &mut buf.as_slice())
147 .await
148 .expect("Valid full transactions should be deserialized using v2");
149
150 assert!(matches!(
152 deserialized,
153 V2ResponseMessage::TxPoolFullTransactions(Ok(actual)) if actual == full_transactions
154 ));
155 }
156
157 #[tokio::test]
158 async fn codec__serialization_roundtrip_using_v1_on_successful_response_returns_original_value()
159 {
160 let sealed_block_headers = vec![SealedBlockHeader::default()];
162 let response = V2ResponseMessage::SealedHeaders(Ok(sealed_block_headers.clone()));
163 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
164 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
165 let mut buf = Vec::with_capacity(1024);
166
167 codec
169 .write_response(&RequestResponseProtocol::V1, &mut buf, response)
170 .await
171 .expect("Valid Vec<SealedBlockHeader> should be serialized using v1");
172
173 let deserialized = codec
174 .read_response(&RequestResponseProtocol::V1, &mut buf.as_slice())
175 .await
176 .expect("Valid Vec<SealedBlockHeader> should be deserialized using v1");
177
178 assert!(
180 matches!(deserialized, V2ResponseMessage::SealedHeaders(Ok(sealed_headers)) if sealed_headers == sealed_block_headers)
181 );
182 }
183
184 #[tokio::test]
185 async fn codec__serialization_roundtrip_using_v2_on_error_response_returns_original_value()
186 {
187 let response = V2ResponseMessage::SealedHeaders(Err(
189 ResponseMessageErrorCode::ProtocolV1EmptyResponse,
190 ));
191 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
192 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
193 let mut buf = Vec::with_capacity(1024);
194
195 codec
197 .write_response(&RequestResponseProtocol::V2, &mut buf, response.clone())
198 .await
199 .expect("Valid Vec<SealedBlockHeader> is serialized using v1");
200
201 let deserialized = codec
202 .read_response(&RequestResponseProtocol::V2, &mut buf.as_slice())
203 .await
204 .expect("Valid Vec<SealedBlockHeader> is deserialized using v1");
205
206 assert!(matches!(
208 deserialized,
209 V2ResponseMessage::SealedHeaders(Err(
210 ResponseMessageErrorCode::ProtocolV1EmptyResponse
211 ))
212 ));
213 }
214
215 #[tokio::test]
216 async fn codec__serialization_roundtrip_using_v1_on_error_response_returns_predefined_error_code()
217 {
218 let response = V2ResponseMessage::SealedHeaders(Err(
220 ResponseMessageErrorCode::RequestedRangeTooLarge,
221 ));
222 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
223 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
224 let mut buf = Vec::with_capacity(1024);
225
226 codec
228 .write_response(&RequestResponseProtocol::V1, &mut buf, response.clone())
229 .await
230 .expect("Valid Vec<SealedBlockHeader> is serialized using v1");
231
232 let deserialized = codec
233 .read_response(&RequestResponseProtocol::V1, &mut buf.as_slice())
234 .await
235 .expect("Valid Vec<SealedBlockHeader> is deserialized using v1");
236
237 assert!(matches!(
239 deserialized,
240 V2ResponseMessage::SealedHeaders(Err(
241 ResponseMessageErrorCode::ProtocolV1EmptyResponse
242 ))
243 ));
244 }
245
246 #[tokio::test]
247 async fn codec__write_response_is_backwards_compatible_with_v1() {
248 let response = V2ResponseMessage::SealedHeaders(Err(
250 ResponseMessageErrorCode::ProtocolV1EmptyResponse,
251 ));
252 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
253 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
254 let mut buf = Vec::with_capacity(1024);
255
256 codec
258 .write_response(&RequestResponseProtocol::V1, &mut buf, response.clone())
259 .await
260 .expect("Valid Vec<SealedBlockHeader> is serialized using v1");
261
262 let deserialized_as_v1 =
263 codec.codec.decode(&buf).expect("Deserialization as V1ResponseMessage should succeed");
266
267 assert!(matches!(
269 deserialized_as_v1,
270 V1ResponseMessage::SealedHeaders(None)
271 ));
272 }
273
274 #[tokio::test]
275 async fn codec__read_response_is_backwards_compatible_with_v1() {
276 let response = V1ResponseMessage::SealedHeaders(None);
278 let mut codec: RequestResponseMessageHandler<PostcardCodec> =
279 RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
280
281 let buf = codec
283 .codec
284 .encode(&response)
285 .expect("Serialization as V1ResponseMessage should succeed");
286 let deserialized = codec
287 .read_response(&RequestResponseProtocol::V1, &mut &*buf)
288 .await
289 .expect("Valid Vec<SealedBlockHeader> is deserialized using v1");
290
291 assert!(matches!(
293 deserialized,
294 V2ResponseMessage::SealedHeaders(Err(
295 ResponseMessageErrorCode::ProtocolV1EmptyResponse
296 ))
297 ));
298 }
299}