Skip to main content

rings_core/message/payload/
mod.rs

1#![deny(missing_docs)]
2
3use std::fmt;
4use std::io::Write;
5use std::sync::Arc;
6
7use async_trait::async_trait;
8use bytes::Bytes;
9use flate2::write::GzDecoder;
10use flate2::write::GzEncoder;
11use flate2::Compression;
12use serde::de::DeserializeOwned;
13use serde::Deserialize;
14use serde::Serialize;
15
16use super::encoder::Decoder;
17use super::encoder::Encoded;
18use super::encoder::Encoder;
19use super::protocols::MessageRelay;
20use super::protocols::MessageVerification;
21use super::protocols::MessageVerificationExt;
22use super::protocols::ReportReturnPolicy;
23use crate::dht::Chord;
24use crate::dht::Did;
25use crate::dht::PeerRing;
26use crate::dht::PeerRingAction;
27use crate::ecc::keccak256;
28use crate::error::Error;
29use crate::error::Result;
30use crate::session::SessionSk;
31
32/// Compresses the given data byte slice using the gzip algorithm with the specified compression level.
33pub fn encode_data_gzip(data: &Bytes, level: u8) -> Result<Bytes> {
34    let mut ec = GzEncoder::new(Vec::new(), Compression::new(level as u32));
35    ec.write_all(data).map_err(|_| Error::GzipEncode)?;
36    ec.finish().map(Bytes::from).map_err(|_| Error::GzipEncode)
37}
38
39/// Serializes the given data using JSON and compresses it with gzip using the specified compression level.
40pub fn gzip_data<T>(data: &T, level: u8) -> Result<Bytes>
41where T: Serialize {
42    let json_bytes = serde_json::to_vec(data).map_err(|_| Error::SerializeToString)?;
43    encode_data_gzip(&json_bytes.into(), level)
44}
45
46/// Decompresses the given gzip-compressed byte slice and returns the decompressed byte slice.
47pub fn decode_gzip_data(data: &Bytes) -> Result<Bytes> {
48    let mut writer = Vec::new();
49    let mut decoder = GzDecoder::new(writer);
50    decoder.write_all(data).map_err(|_| Error::GzipDecode)?;
51    decoder.try_finish().map_err(|_| Error::GzipDecode)?;
52    writer = decoder.finish().map_err(|_| Error::GzipDecode)?;
53    Ok(writer.into())
54}
55
56/// From gzip data to deserialized
57pub fn from_gzipped_data<T>(data: &Bytes) -> Result<T>
58where T: DeserializeOwned {
59    let data = decode_gzip_data(data)?;
60    let m = serde_json::from_slice(&data).map_err(Error::Deserialize)?;
61    Ok(m)
62}
63
64fn hash_transaction(
65    destination: Did,
66    tx_id: uuid::Uuid,
67    report_return: ReportReturnPolicy,
68    data: &[u8],
69) -> [u8; 32] {
70    let mut msg = vec![];
71
72    msg.extend_from_slice(destination.as_bytes());
73    msg.extend_from_slice(tx_id.as_bytes());
74    match report_return {
75        ReportReturnPolicy::Path => msg.push(0),
76        ReportReturnPolicy::Routed { destination } => {
77            msg.push(1);
78            msg.extend_from_slice(destination.as_bytes());
79        }
80    }
81    msg.extend_from_slice(data);
82
83    keccak256(&msg)
84}
85
86/// All messages transmitted in RingsNetwork should be wrapped by `Transaction`.
87/// It additionally offer destination, tx_id and verification.
88///
89/// To transmit `Transaction` in RingsNetwork, user should build
90/// [MessagePayload] and use [PayloadSender] to send.
91#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
92pub struct Transaction {
93    /// The destination of this message.
94    pub destination: Did,
95    /// The transaction ID.
96    /// Remote peer should use same tx_id when response.
97    pub tx_id: uuid::Uuid,
98    /// data
99    pub data: Vec<u8>,
100    /// Return policy used by reports for this transaction.
101    #[serde(default)]
102    pub report_return: ReportReturnPolicy,
103    /// This field holds a signature from a node,
104    /// which is used to prove that the transaction was created by that node.
105    pub verification: MessageVerification,
106}
107
108impl fmt::Debug for Transaction {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("Transaction")
111            .field("destination", &self.destination)
112            .field("tx_id", &self.tx_id)
113            .field("data_bytes", &self.data.len())
114            .field("report_return", &self.report_return)
115            .finish()
116    }
117}
118
119/// `MessagePayload` is used to transmit data between nodes.
120/// The data should be packed by [Transaction].
121#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
122pub struct MessagePayload {
123    /// Payload data
124    pub transaction: Transaction,
125    /// Relay records the transport path of message.
126    /// And can also help message sender to find the next hop.
127    pub relay: MessageRelay,
128    /// This field holds a signature from a node,
129    /// which is used to prove that payload was created by that node.
130    pub verification: MessageVerification,
131}
132
133impl fmt::Debug for MessagePayload {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        f.debug_struct("MessagePayload")
136            .field("transaction", &self.transaction)
137            .field("relay", &self.relay)
138            .finish()
139    }
140}
141
142impl Transaction {
143    /// Wrap data. Will serialize by [rings_codec::serialize]
144    /// then sign [MessageVerification] by session_sk.
145    pub fn new<T>(
146        destination: Did,
147        tx_id: uuid::Uuid,
148        data: T,
149        session_sk: &SessionSk,
150    ) -> Result<Self>
151    where
152        T: Serialize,
153    {
154        Self::new_with_report_return(
155            destination,
156            tx_id,
157            data,
158            ReportReturnPolicy::Path,
159            session_sk,
160        )
161    }
162
163    /// Wrap data with an explicit report-return policy.
164    pub fn new_with_report_return<T>(
165        destination: Did,
166        tx_id: uuid::Uuid,
167        data: T,
168        report_return: ReportReturnPolicy,
169        session_sk: &SessionSk,
170    ) -> Result<Self>
171    where
172        T: Serialize,
173    {
174        report_return.validate_authorized_by(session_sk.account_did())?;
175        let data = rings_codec::serialize(&data).map_err(Error::CodecSerialize)?;
176        let msg_hash = hash_transaction(destination, tx_id, report_return, &data);
177        let verification = MessageVerification::new(&msg_hash, session_sk)?;
178        Ok(Self {
179            destination,
180            tx_id,
181            data,
182            report_return,
183            verification,
184        })
185    }
186
187    /// Deserializes the data field into a `T` instance.
188    pub fn data<T>(&self) -> Result<T>
189    where T: DeserializeOwned {
190        rings_codec::deserialize(&self.data).map_err(Error::CodecDeserialize)
191    }
192}
193
194impl MessagePayload {
195    /// Create new `MessagePayload`.
196    /// Need [Transaction], [SessionSk] and [MessageRelay].
197    pub fn new(
198        transaction: Transaction,
199        session_sk: &SessionSk,
200        relay: MessageRelay,
201    ) -> Result<Self> {
202        let msg_hash = hash_transaction(
203            transaction.destination,
204            transaction.tx_id,
205            transaction.report_return,
206            &transaction.data,
207        );
208        let verification = MessageVerification::new(&msg_hash, session_sk)?;
209        Ok(Self {
210            transaction,
211            relay,
212            verification,
213        })
214    }
215
216    /// Helps to create sending message from data.
217    pub fn new_send<T>(
218        data: T,
219        session_sk: &SessionSk,
220        next_hop: Did,
221        destination: Did,
222    ) -> Result<Self>
223    where
224        T: Serialize,
225    {
226        let tx_id = crate::utils::new_uuid();
227        let transaction = Transaction::new(destination, tx_id, data, session_sk)?;
228        let relay = MessageRelay::new(
229            vec![session_sk.account_did()],
230            next_hop,
231            transaction.destination,
232        );
233        Self::new(transaction, session_sk, relay)
234    }
235
236    /// Deserializes a `MessagePayload` instance from the Rings wire encoding.
237    pub fn from_wire(data: &[u8]) -> Result<Self> {
238        rings_codec::deserialize(data).map_err(Error::CodecDeserialize)
239    }
240
241    /// Serializes the `MessagePayload` instance into the Rings wire encoding.
242    pub fn to_wire(&self) -> Result<Bytes> {
243        rings_codec::serialize(self)
244            .map(Bytes::from)
245            .map_err(Error::CodecSerialize)
246    }
247
248    /// Return the exact Rings wire size without allocating the wire buffer.
249    pub(crate) fn wire_size(&self) -> Result<usize> {
250        let bytes = rings_codec::serialized_size(self).map_err(Error::CodecSerialize)?;
251        usize::try_from(bytes).map_err(|_| Error::MessageSizeOverflow)
252    }
253
254    /// Returns whether `local` is the relay destination of this payload.
255    pub(crate) fn is_relay_destination_for(&self, local: Did) -> bool {
256        self.relay.destination == local
257    }
258
259    /// Returns whether `local` should forward this payload to another node.
260    pub(crate) fn should_forward_from(&self, local: Did) -> bool {
261        !self.is_relay_destination_for(local)
262    }
263}
264
265impl MessageVerificationExt for Transaction {
266    fn verification_data(&self) -> Result<Vec<u8>> {
267        self.report_return.validate_authorized_by(self.signer())?;
268        Ok(hash_transaction(self.destination, self.tx_id, self.report_return, &self.data).to_vec())
269    }
270
271    fn verification(&self) -> &MessageVerification {
272        &self.verification
273    }
274}
275
276impl MessageVerificationExt for MessagePayload {
277    fn verification_data(&self) -> Result<Vec<u8>> {
278        self.transaction.verification_data()
279    }
280
281    fn verification(&self) -> &MessageVerification {
282        &self.verification
283    }
284}
285
286impl Encoder for MessagePayload {
287    fn encode(&self) -> Result<Encoded> {
288        self.to_wire()?.encode()
289    }
290}
291
292impl Decoder for MessagePayload {
293    fn from_encoded(encoded: &Encoded) -> Result<Self> {
294        let v: Bytes = encoded.decode()?;
295        Self::from_wire(&v)
296    }
297}
298
299/// Trait of PayloadSender
300#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
301#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
302pub trait PayloadSender {
303    /// Get the session sk
304    fn session_sk(&self) -> &SessionSk;
305
306    /// Get access to DHT.
307    fn dht(&self) -> Arc<PeerRing>;
308
309    /// Used to check if destination is already connected when `infer_next_hop`
310    fn is_connected(&self, did: Did) -> bool;
311
312    /// Send a message payload to a specified DID.
313    async fn do_send_payload(&self, did: Did, payload: MessagePayload) -> Result<()>;
314
315    /// Infer the next hop for a message by calling `dht.find_successor()`.
316    fn infer_next_hop(&self, destination: Did, next_hop: Option<Did>) -> Result<Did> {
317        if self.is_connected(destination) {
318            return Ok(destination);
319        }
320
321        if let Some(next_hop) = next_hop {
322            return Ok(next_hop);
323        }
324
325        match self.dht().find_successor(destination)? {
326            PeerRingAction::Some(did) => Ok(did),
327            PeerRingAction::RemoteAction(did, _) => Ok(did),
328            _ => Err(Error::NoNextHop),
329        }
330    }
331
332    /// Alias for `do_send_payload` that sets the next hop to `payload.relay.next_hop`.
333    async fn send_payload(&self, payload: MessagePayload) -> Result<()> {
334        self.do_send_payload(payload.relay.next_hop, payload).await
335    }
336
337    /// Send a message to a specified destination by specified next hop.
338    async fn send_message_by_hop<T>(
339        &self,
340        msg: T,
341        destination: Did,
342        next_hop: Did,
343    ) -> Result<uuid::Uuid>
344    where
345        T: Serialize + Send,
346    {
347        let payload = MessagePayload::new_send(msg, self.session_sk(), next_hop, destination)?;
348        let tx_id = payload.transaction.tx_id;
349        self.send_payload(payload).await?;
350        Ok(tx_id)
351    }
352
353    /// Send a message to a specified destination by specified next hop with an explicit report policy.
354    async fn send_message_by_hop_with_report_return<T>(
355        &self,
356        msg: T,
357        destination: Did,
358        next_hop: Did,
359        report_return: ReportReturnPolicy,
360    ) -> Result<uuid::Uuid>
361    where
362        T: Serialize + Send,
363    {
364        let tx_id = crate::utils::new_uuid();
365        let transaction = Transaction::new_with_report_return(
366            destination,
367            tx_id,
368            msg,
369            report_return,
370            self.session_sk(),
371        )?;
372        let relay = MessageRelay::new(
373            vec![self.session_sk().account_did()],
374            next_hop,
375            transaction.destination,
376        );
377        let payload = MessagePayload::new(transaction, self.session_sk(), relay)?;
378        self.send_payload(payload).await?;
379        Ok(tx_id)
380    }
381
382    /// Send a message to a specified destination.
383    async fn send_message<T>(&self, msg: T, destination: Did) -> Result<uuid::Uuid>
384    where T: Serialize + Send {
385        let next_hop = self.infer_next_hop(destination, None)?;
386        self.send_message_by_hop(msg, destination, next_hop).await
387    }
388
389    /// Send a message to a specified destination with an explicit report policy.
390    async fn send_message_with_report_return<T>(
391        &self,
392        msg: T,
393        destination: Did,
394        report_return: ReportReturnPolicy,
395    ) -> Result<uuid::Uuid>
396    where
397        T: Serialize + Send,
398    {
399        let next_hop = self.infer_next_hop(destination, None)?;
400        self.send_message_by_hop_with_report_return(msg, destination, next_hop, report_return)
401            .await
402    }
403
404    /// Send a direct message to a specified destination.
405    async fn send_direct_message<T>(&self, msg: T, destination: Did) -> Result<uuid::Uuid>
406    where T: Serialize + Send {
407        self.send_message_by_hop(msg, destination, destination)
408            .await
409    }
410
411    /// Send a report message to a specified destination.
412    async fn send_report_message<T>(&self, payload: &MessagePayload, msg: T) -> Result<()>
413    where T: Serialize + Send {
414        let policy = payload.transaction.report_return;
415        // Keep this send-boundary check even though transaction verification
416        // enforces the same authorization when the request is received.
417        policy.validate_authorized_by(payload.transaction.signer())?;
418        let routed_next_hop = match policy {
419            ReportReturnPolicy::Path => None,
420            ReportReturnPolicy::Routed { destination } => {
421                Some(self.infer_next_hop(destination, None)?)
422            }
423        };
424        let relay = payload
425            .relay
426            .report(self.dht().did, policy, routed_next_hop)?;
427
428        let transaction = Transaction::new(
429            relay.destination,
430            payload.transaction.tx_id,
431            msg,
432            self.session_sk(),
433        )?;
434
435        let pl = MessagePayload::new(transaction, self.session_sk(), relay)?;
436        self.send_payload(pl).await
437    }
438
439    /// Forward a payload message by relay.
440    /// It just create a new payload, cloned data, resigned with session and send
441    async fn forward_by_relay(&self, payload: &MessagePayload, relay: MessageRelay) -> Result<()> {
442        let new_pl = MessagePayload::new(payload.transaction.clone(), self.session_sk(), relay)?;
443        self.send_payload(new_pl).await
444    }
445
446    /// Forward a payload message, with the next hop inferred by the DHT.
447    async fn forward_payload(&self, payload: &MessagePayload, next_hop: Option<Did>) -> Result<()> {
448        let next_hop = self.infer_next_hop(payload.relay.destination, next_hop)?;
449        let relay = payload.relay.forward(self.dht().did, next_hop)?;
450        self.forward_by_relay(payload, relay).await
451    }
452
453    /// Reset the destination to a secp DID.
454    async fn reset_destination(&self, payload: &MessagePayload, next_hop: Did) -> Result<()> {
455        let relay = payload
456            .relay
457            .reset_destination(next_hop)
458            .forward(self.dht().did, next_hop)?;
459        self.forward_by_relay(payload, relay).await
460    }
461}
462
463#[cfg(test)]
464pub mod test_payload;