Support sending onion messages
[rust-lightning] / lightning / src / onion_message / packet.rs
index 37f178f5521ccfcc6f5f26738f949bc69945872b..ed0206a2adbd2c2f883902c215a94872a1a6116e 100644 (file)
@@ -13,12 +13,19 @@ use bitcoin::secp256k1::PublicKey;
 
 use ln::msgs::DecodeError;
 use ln::onion_utils;
+use super::blinded_route::{ForwardTlvs, ReceiveTlvs};
+use util::chacha20poly1305rfc::ChaChaPolyWriteAdapter;
 use util::ser::{LengthRead, LengthReadable, Readable, Writeable, Writer};
 
 use core::cmp;
 use io;
 use prelude::*;
 
+// Per the spec, an onion message packet's `hop_data` field length should be
+// SMALL_PACKET_HOP_DATA_LEN if it fits, else BIG_PACKET_HOP_DATA_LEN if it fits.
+pub(super) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
+pub(super) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
+
 #[derive(Clone, Debug, PartialEq)]
 pub(crate) struct Packet {
        version: u8,
@@ -80,3 +87,72 @@ impl LengthReadable for Packet {
                })
        }
 }
+
+/// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route
+/// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion
+/// message content itself, such as an invoice request.
+pub(super) enum Payload {
+       /// This payload is for an intermediate hop.
+       Forward(ForwardControlTlvs),
+       /// This payload is for the final hop.
+       Receive {
+               control_tlvs: ReceiveControlTlvs,
+               // Coming soon:
+               // reply_path: Option<BlindedRoute>,
+               // message: Message,
+       }
+}
+
+// Coming soon:
+// enum Message {
+//     InvoiceRequest(InvoiceRequest),
+//     Invoice(Invoice),
+//     InvoiceError(InvoiceError),
+//     CustomMessage<T>,
+// }
+
+/// Forward control TLVs in their blinded and unblinded form.
+pub(super) enum ForwardControlTlvs {
+       /// If we're sending to a blinded route, the node that constructed the blinded route has provided
+       /// this hop's control TLVs, already encrypted into bytes.
+       Blinded(Vec<u8>),
+       /// If we're constructing an onion message hop through an intermediate unblinded node, we'll need
+       /// to construct the intermediate hop's control TLVs in their unblinded state to avoid encoding
+       /// them into an intermediate Vec. See [`super::blinded_route::ForwardTlvs`] for more info.
+       Unblinded(ForwardTlvs),
+}
+
+/// Receive control TLVs in their blinded and unblinded form.
+pub(super) enum ReceiveControlTlvs {
+       /// See [`ForwardControlTlvs::Blinded`].
+       Blinded(Vec<u8>),
+       /// See [`ForwardControlTlvs::Unblinded`] and [`super::blinded_route::ReceiveTlvs`].
+       Unblinded(ReceiveTlvs),
+}
+
+// Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs.
+impl Writeable for (Payload, [u8; 32]) {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               match &self.0 {
+                       Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) |
+                       Payload::Receive { control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes)} => {
+                               encode_varint_length_prefixed_tlv!(w, {
+                                       (4, encrypted_bytes, vec_type)
+                               })
+                       },
+                       Payload::Forward(ForwardControlTlvs::Unblinded(control_tlvs)) => {
+                               let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
+                               encode_varint_length_prefixed_tlv!(w, {
+                                       (4, write_adapter, required)
+                               })
+                       },
+                       Payload::Receive { control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs)} => {
+                               let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
+                               encode_varint_length_prefixed_tlv!(w, {
+                                       (4, write_adapter, required)
+                               })
+                       },
+               }
+               Ok(())
+       }
+}