Skip to content

Commit

Permalink
Use explicit WithoutLength for BOLT 12 features
Browse files Browse the repository at this point in the history
Most BOLT 12 features are used as the value of a TLV record and thus
don't use an explicit length. One exception is the features inside the
blinded payinfo subtype since the TLV record contains a list of them.
However, these features are also used in the BOLT 4 encrypted_data_tlv
TLV stream as a single record, where the length is implicit.

Implement Readable and Writeable for Features wrapped in WithoutLength
such that either serialization can be used where required.
  • Loading branch information
jkczyz committed Dec 20, 2022
1 parent fd483ae commit 005566a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
42 changes: 35 additions & 7 deletions lightning/src/ln/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ use core::marker::PhantomData;
use bitcoin::bech32;
use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32};
use crate::ln::msgs::DecodeError;
use crate::util::ser::{Readable, Writeable, Writer};
use crate::util::ser::{Readable, WithoutLength, Writeable, Writer};

mod sealed {
use crate::prelude::*;
Expand Down Expand Up @@ -725,26 +725,40 @@ macro_rules! impl_feature_tlv_write {
($features: ident) => {
impl Writeable for $features {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.write_be(w)
WithoutLength(self).write(w)
}
}
impl Readable for $features {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let v = io_extras::read_to_end(r)?;
Ok(Self::from_be_bytes(v))
Ok(WithoutLength::<Self>::read(r)?.0)
}
}
}
}

impl_feature_tlv_write!(ChannelTypeFeatures);
impl_feature_tlv_write!(OfferFeatures);
impl_feature_tlv_write!(InvoiceRequestFeatures);

// Some features may appear both in a TLV record and as part of a TLV subtype sequence. The latter
// requires a length but the former does not.

impl<T: sealed::Context> Writeable for WithoutLength<&Features<T>> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.write_be(w)
}
}

impl<T: sealed::Context> Readable for WithoutLength<Features<T>> {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let v = io_extras::read_to_end(r)?;
Ok(WithoutLength(Features::<T>::from_be_bytes(v)))
}
}

#[cfg(test)]
mod tests {
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures, sealed};
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures, OfferFeatures, sealed};
use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5};
use crate::util::ser::{Readable, WithoutLength, Writeable};

#[test]
fn sanity_test_unknown_bits() {
Expand Down Expand Up @@ -838,6 +852,20 @@ mod tests {
assert!(features.supports_payment_secret());
}

#[test]
fn encodes_features_without_length() {
let features = OfferFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]);
assert_eq!(features.flags.len(), 8);

let mut serialized_features = Vec::new();
WithoutLength(&features).write(&mut serialized_features).unwrap();
assert_eq!(serialized_features.len(), 8);

let deserialized_features =
WithoutLength::<OfferFeatures>::read(&mut &serialized_features[..]).unwrap().0;
assert_eq!(features, deserialized_features);
}

#[test]
fn invoice_features_encoding() {
let features_as_u5s = vec![
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/offers/invoice_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ impl Writeable for InvoiceRequestContents {
tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, 80..160, {
(80, chain: ChainHash),
(82, amount: (u64, HighZeroBytesDroppedBigSize)),
(84, features: InvoiceRequestFeatures),
(84, features: (InvoiceRequestFeatures, WithoutLength)),
(86, quantity: (u64, HighZeroBytesDroppedBigSize)),
(88, payer_id: PublicKey),
(89, payer_note: (String, WithoutLength)),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/offers/offer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef, 1..80, {
(6, currency: CurrencyCode),
(8, amount: (u64, HighZeroBytesDroppedBigSize)),
(10, description: (String, WithoutLength)),
(12, features: OfferFeatures),
(12, features: (OfferFeatures, WithoutLength)),
(14, absolute_expiry: (u64, HighZeroBytesDroppedBigSize)),
(16, paths: (Vec<BlindedPath>, WithoutLength)),
(18, issuer: (String, WithoutLength)),
Expand Down

0 comments on commit 005566a

Please sign in to comment.