Skip to main content

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(
46            postcard::to_allocvec(value).map_err(|e| io::Error::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).map_err(|e| io::Error::other(e.to_string()))
59    }
60}
61
62#[cfg(test)]
63#[allow(non_snake_case)]
64mod tests {
65    use fuel_core_types::{
66        blockchain::SealedBlockHeader,
67        fuel_tx::Transaction,
68        services::p2p::NetworkableTransactionPool,
69    };
70    use libp2p::request_response::Codec;
71
72    use super::*;
73    use crate::{
74        codecs::request_response::RequestResponseMessageHandler,
75        request_response::{
76            messages::{
77                RequestMessage,
78                ResponseMessageErrorCode,
79                V1ResponseMessage,
80                V2ResponseMessage,
81            },
82            protocols::RequestResponseProtocol,
83        },
84    };
85
86    const MAX_REQUEST_SIZE: NonZeroU32 = NonZeroU32::new(1024).unwrap();
87
88    #[test]
89    fn test_request_size_fits() {
90        let arbitrary_range = 2..6;
91        let m = RequestMessage::Transactions(arbitrary_range);
92        assert!(
93            postcard::to_stdvec(&m).unwrap().len() <= MAX_REQUEST_SIZE.get() as usize
94        );
95    }
96
97    #[tokio::test]
98    async fn codec__serialization_roundtrip_using_v2_on_successful_response_returns_original_value__sealed_headers()
99     {
100        // Given
101        let sealed_block_headers = vec![SealedBlockHeader::default()];
102        let response = V2ResponseMessage::SealedHeaders(Ok(sealed_block_headers.clone()));
103        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
104            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
105        let mut buf = Vec::with_capacity(1024);
106
107        // When
108        codec
109            .write_response(&RequestResponseProtocol::V2, &mut buf, response)
110            .await
111            .expect("Valid Vec<SealedBlockHeader> should be serialized using v1");
112
113        let deserialized = codec
114            .read_response(&RequestResponseProtocol::V2, &mut buf.as_slice())
115            .await
116            .expect("Valid Vec<SealedBlockHeader> should be deserialized using v1");
117
118        // Then
119        assert!(matches!(
120            deserialized,
121            V2ResponseMessage::SealedHeaders(Ok(sealed_headers)) if sealed_headers == sealed_block_headers
122        ));
123    }
124
125    #[tokio::test]
126    async fn codec__serialization_roundtrip_using_v2_on_successful_response_returns_original_value__full_transactions()
127     {
128        // Given
129        let full_transactions = vec![Some(NetworkableTransactionPool::Transaction(
130            Transaction::default_test_tx(),
131        ))];
132        let response =
133            V2ResponseMessage::TxPoolFullTransactions(Ok(full_transactions.clone()));
134        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
135            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
136        let mut buf = Vec::with_capacity(1024);
137
138        // When
139        codec
140            .write_response(&RequestResponseProtocol::V2, &mut buf, response)
141            .await
142            .expect("Valid full transactions should be serialized using v2");
143
144        let deserialized = codec
145            .read_response(&RequestResponseProtocol::V2, &mut buf.as_slice())
146            .await
147            .expect("Valid full transactions should be deserialized using v2");
148
149        // Then
150        assert!(matches!(
151            deserialized,
152            V2ResponseMessage::TxPoolFullTransactions(Ok(actual)) if actual == full_transactions
153        ));
154    }
155
156    #[tokio::test]
157    async fn codec__serialization_roundtrip_using_v1_on_successful_response_returns_original_value()
158     {
159        // Given
160        let sealed_block_headers = vec![SealedBlockHeader::default()];
161        let response = V2ResponseMessage::SealedHeaders(Ok(sealed_block_headers.clone()));
162        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
163            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
164        let mut buf = Vec::with_capacity(1024);
165
166        // When
167        codec
168            .write_response(&RequestResponseProtocol::V1, &mut buf, response)
169            .await
170            .expect("Valid Vec<SealedBlockHeader> should be serialized using v1");
171
172        let deserialized = codec
173            .read_response(&RequestResponseProtocol::V1, &mut buf.as_slice())
174            .await
175            .expect("Valid Vec<SealedBlockHeader> should be deserialized using v1");
176
177        // Then
178        assert!(
179            matches!(deserialized, V2ResponseMessage::SealedHeaders(Ok(sealed_headers)) if sealed_headers == sealed_block_headers)
180        );
181    }
182
183    #[tokio::test]
184    async fn codec__serialization_roundtrip_using_v2_on_error_response_returns_original_value()
185     {
186        // Given
187        let response = V2ResponseMessage::SealedHeaders(Err(
188            ResponseMessageErrorCode::ProtocolV1EmptyResponse,
189        ));
190        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
191            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
192        let mut buf = Vec::with_capacity(1024);
193
194        // When
195        codec
196            .write_response(&RequestResponseProtocol::V2, &mut buf, response.clone())
197            .await
198            .expect("Valid Vec<SealedBlockHeader> is serialized using v1");
199
200        let deserialized = codec
201            .read_response(&RequestResponseProtocol::V2, &mut buf.as_slice())
202            .await
203            .expect("Valid Vec<SealedBlockHeader> is deserialized using v1");
204
205        // Then
206        assert!(matches!(
207            deserialized,
208            V2ResponseMessage::SealedHeaders(Err(
209                ResponseMessageErrorCode::ProtocolV1EmptyResponse
210            ))
211        ));
212    }
213
214    #[tokio::test]
215    async fn codec__serialization_roundtrip_using_v1_on_error_response_returns_predefined_error_code()
216     {
217        // Given
218        let response = V2ResponseMessage::SealedHeaders(Err(
219            ResponseMessageErrorCode::RequestedRangeTooLarge,
220        ));
221        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
222            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
223        let mut buf = Vec::with_capacity(1024);
224
225        // When
226        codec
227            .write_response(&RequestResponseProtocol::V1, &mut buf, response.clone())
228            .await
229            .expect("Valid Vec<SealedBlockHeader> is serialized using v1");
230
231        let deserialized = codec
232            .read_response(&RequestResponseProtocol::V1, &mut buf.as_slice())
233            .await
234            .expect("Valid Vec<SealedBlockHeader> is deserialized using v1");
235
236        // Then
237        assert!(matches!(
238            deserialized,
239            V2ResponseMessage::SealedHeaders(Err(
240                ResponseMessageErrorCode::ProtocolV1EmptyResponse
241            ))
242        ));
243    }
244
245    #[tokio::test]
246    async fn codec__write_response_is_backwards_compatible_with_v1() {
247        // Given
248        let response = V2ResponseMessage::SealedHeaders(Err(
249            ResponseMessageErrorCode::ProtocolV1EmptyResponse,
250        ));
251        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
252            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
253        let mut buf = Vec::with_capacity(1024);
254
255        // When
256        codec
257            .write_response(&RequestResponseProtocol::V1, &mut buf, response.clone())
258            .await
259            .expect("Valid Vec<SealedBlockHeader> is serialized using v1");
260
261        let deserialized_as_v1 =
262            // We cannot access the codec trait from an old node here, 
263            // so we deserialize directly using the `V1ResponseMessage` type.
264            codec.codec.decode(&buf).expect("Deserialization as V1ResponseMessage should succeed");
265
266        // Then
267        assert!(matches!(
268            deserialized_as_v1,
269            V1ResponseMessage::SealedHeaders(None)
270        ));
271    }
272
273    #[tokio::test]
274    async fn codec__read_response_is_backwards_compatible_with_v1() {
275        // Given
276        let response = V1ResponseMessage::SealedHeaders(None);
277        let mut codec: RequestResponseMessageHandler<PostcardCodec> =
278            RequestResponseMessageHandler::new(MAX_REQUEST_SIZE);
279
280        // When
281        let buf = codec
282            .codec
283            .encode(&response)
284            .expect("Serialization as V1ResponseMessage should succeed");
285        let deserialized = codec
286            .read_response(&RequestResponseProtocol::V1, &mut &*buf)
287            .await
288            .expect("Valid Vec<SealedBlockHeader> is deserialized using v1");
289
290        // Then
291        assert!(matches!(
292            deserialized,
293            V2ResponseMessage::SealedHeaders(Err(
294                ResponseMessageErrorCode::ProtocolV1EmptyResponse
295            ))
296        ));
297    }
298}