fuel_core_p2p/codecs/
postcard.rs

1use 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        // Given
102        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        // When
109        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        // Then
120        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        // Given
130        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        // When
140        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        // Then
151        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        // Given
161        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        // When
168        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        // Then
179        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        // Given
188        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        // When
196        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        // Then
207        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        // Given
219        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        // When
227        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        // Then
238        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        // Given
249        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        // When
257        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            // We cannot access the codec trait from an old node here, 
264            // so we deserialize directly using the `V1ResponseMessage` type.
265            codec.codec.decode(&buf).expect("Deserialization as V1ResponseMessage should succeed");
266
267        // Then
268        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        // Given
277        let response = V1ResponseMessage::SealedHeaders(None);
278        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
279            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
280
281        // When
282        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        // Then
292        assert!(matches!(
293            deserialized,
294            V2ResponseMessage::SealedHeaders(Err(
295                ResponseMessageErrorCode::ProtocolV1EmptyResponse
296            ))
297        ));
298    }
299}