Skip to content

Commit 28f1312

Browse files
committed
f - Offer raw byte encoding and decoding
Offers are typically encoded as bech32 strings for use in QR codes. However, a more compact encoding is useful when storing offers long-term. Implement Writeable for Offer and have a corresponding TryFrom implementation for decoding the raw bytes.
1 parent b44cde7 commit 28f1312

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

lightning/src/offers/offer.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
//! extern crate core;
1919
//! extern crate lightning;
2020
//!
21+
//! use core::convert::TryFrom;
2122
//! use core::num::NonZeroU64;
2223
//! use core::time::Duration;
2324
//!
2425
//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
2526
//! use lightning::offers::offer::{Amount, Offer, OfferBuilder};
2627
//! use lightning::offers::parse::ParseError;
28+
//! use lightning::util::ser::{Readable, Writeable};
2729
//!
2830
//! # use lightning::onion_message::BlindedPath;
2931
//! # #[cfg(feature = "std")]
@@ -55,6 +57,13 @@
5557
//!
5658
//! // Parse from a bech32 string after scanning from a QR code.
5759
//! let offer = encoded_offer.parse::<Offer>()?;
60+
//!
61+
//! // Encode offer as raw bytes.
62+
//! let mut bytes = Vec::new();
63+
//! offer.write(&mut bytes).unwrap();
64+
//!
65+
//! // Decode raw bytes into an offer.
66+
//! let offer = Offer::try_from(bytes)?;
5867
//! # Ok(())
5968
//! # }
6069
//! ```
@@ -72,7 +81,7 @@ use ln::features::OfferFeatures;
7281
use ln::msgs::MAX_VALUE_MSAT;
7382
use offers::parse::{Bech32Encode, ParseError, SemanticError};
7483
use onion_message::BlindedPath;
75-
use util::ser::{HighZeroBytesDroppedBigSize, WithoutLength, Writeable, Writer};
84+
use util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
7685
use util::string::PrintableString;
7786

7887
use prelude::*;
@@ -411,12 +420,27 @@ impl OfferContents {
411420
}
412421
}
413422

423+
impl Writeable for Offer {
424+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
425+
WithoutLength(&self.bytes).write(writer)
426+
}
427+
}
428+
414429
impl Writeable for OfferContents {
415430
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
416431
self.as_tlv_stream().write(writer)
417432
}
418433
}
419434

435+
impl TryFrom<Vec<u8>> for Offer {
436+
type Error = ParseError;
437+
438+
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
439+
let tlv_stream: OfferTlvStream = Readable::read(&mut &bytes[..])?;
440+
Offer::try_from((bytes, tlv_stream))
441+
}
442+
}
443+
420444
/// The minimum amount required for an item in an [`Offer`], denominated in either bitcoin or
421445
/// another currency.
422446
#[derive(Clone, Debug, PartialEq)]
@@ -467,6 +491,8 @@ impl Bech32Encode for Offer {
467491
const BECH32_HRP: &'static str = "lno";
468492
}
469493

494+
type ParsedOffer = (Vec<u8>, OfferTlvStream);
495+
470496
impl FromStr for Offer {
471497
type Err = ParseError;
472498

@@ -475,6 +501,16 @@ impl FromStr for Offer {
475501
}
476502
}
477503

504+
impl TryFrom<ParsedOffer> for Offer {
505+
type Error = ParseError;
506+
507+
fn try_from(offer: ParsedOffer) -> Result<Self, Self::Error> {
508+
let (bytes, tlv_stream) = offer;
509+
let contents = OfferContents::try_from(tlv_stream)?;
510+
Ok(Offer { bytes, contents })
511+
}
512+
}
513+
478514
impl TryFrom<OfferTlvStream> for OfferContents {
479515
type Error = SemanticError;
480516

@@ -543,11 +579,12 @@ impl core::fmt::Display for Offer {
543579

544580
#[cfg(test)]
545581
mod tests {
546-
use super::{Amount, OfferBuilder};
582+
use super::{Amount, Offer, OfferBuilder};
547583

548584
use bitcoin::blockdata::constants::ChainHash;
549585
use bitcoin::network::constants::Network;
550586
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
587+
use core::convert::TryFrom;
551588
use core::num::NonZeroU64;
552589
use core::time::Duration;
553590
use ln::features::OfferFeatures;
@@ -570,7 +607,7 @@ mod tests {
570607
let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
571608
let tlv_stream = offer.as_tlv_stream();
572609
let mut buffer = Vec::new();
573-
offer.contents.write(&mut buffer).unwrap();
610+
offer.write(&mut buffer).unwrap();
574611

575612
assert_eq!(offer.bytes, buffer.as_slice());
576613
assert_eq!(offer.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
@@ -599,6 +636,10 @@ mod tests {
599636
assert_eq!(tlv_stream.quantity_min, None);
600637
assert_eq!(tlv_stream.quantity_max, None);
601638
assert_eq!(tlv_stream.node_id, Some(&pubkey(42)));
639+
640+
if let Err(e) = Offer::try_from(buffer) {
641+
panic!("error parsing offer: {:?}", e);
642+
}
602643
}
603644

604645
#[test]

0 commit comments

Comments
 (0)