Skip to content

Commit 562c33a

Browse files
committed
Unsigned BOLT 12 message parsing and serialization
1 parent fb6ba0b commit 562c33a

File tree

3 files changed

+94
-8
lines changed

3 files changed

+94
-8
lines changed

lightning/src/offers/invoice.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,12 @@ impl InvoiceFields {
733733
}
734734
}
735735

736+
impl Writeable for UnsignedBolt12Invoice {
737+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
738+
WithoutLength(&self.bytes).write(writer)
739+
}
740+
}
741+
736742
impl Writeable for Bolt12Invoice {
737743
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
738744
WithoutLength(&self.bytes).write(writer)
@@ -745,6 +751,25 @@ impl Writeable for InvoiceContents {
745751
}
746752
}
747753

754+
impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
755+
type Error = Bolt12ParseError;
756+
757+
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
758+
let invoice = ParsedMessage::<PartialInvoiceTlvStream>::try_from(bytes)?;
759+
let ParsedMessage { bytes, tlv_stream } = invoice;
760+
let (
761+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
762+
) = tlv_stream;
763+
let contents = InvoiceContents::try_from(
764+
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
765+
)?;
766+
767+
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
768+
769+
Ok(UnsignedBolt12Invoice { bytes, contents, tagged_hash })
770+
}
771+
}
772+
748773
impl TryFrom<Vec<u8>> for Bolt12Invoice {
749774
type Error = Bolt12ParseError;
750775

@@ -857,6 +882,17 @@ type PartialInvoiceTlvStreamRef<'a> = (
857882
InvoiceTlvStreamRef<'a>,
858883
);
859884

885+
impl SeekReadable for PartialInvoiceTlvStream {
886+
fn read<R: io::Read + io::Seek>(r: &mut R) -> Result<Self, DecodeError> {
887+
let payer = SeekReadable::read(r)?;
888+
let offer = SeekReadable::read(r)?;
889+
let invoice_request = SeekReadable::read(r)?;
890+
let invoice = SeekReadable::read(r)?;
891+
892+
Ok((payer, offer, invoice_request, invoice))
893+
}
894+
}
895+
860896
impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
861897
type Error = Bolt12ParseError;
862898

@@ -961,7 +997,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
961997

962998
#[cfg(test)]
963999
mod tests {
964-
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, InvoiceTlvStreamRef, SIGNATURE_TAG};
1000+
use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
9651001

9661002
use bitcoin::blockdata::script::Script;
9671003
use bitcoin::hashes::Hash;
@@ -1007,15 +1043,27 @@ mod tests {
10071043
let payment_paths = payment_paths();
10081044
let payment_hash = payment_hash();
10091045
let now = now();
1010-
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
1046+
let unsigned_invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
10111047
.amount_msats(1000)
10121048
.build().unwrap()
10131049
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
10141050
.build().unwrap()
10151051
.sign(payer_sign).unwrap()
10161052
.respond_with_no_std(payment_paths.clone(), payment_hash, now).unwrap()
1017-
.build().unwrap()
1018-
.sign(recipient_sign).unwrap();
1053+
.build().unwrap();
1054+
1055+
let mut buffer = Vec::new();
1056+
unsigned_invoice.write(&mut buffer).unwrap();
1057+
1058+
match UnsignedBolt12Invoice::try_from(buffer) {
1059+
Err(e) => panic!("error parsing unsigned invoice: {:?}", e),
1060+
Ok(parsed) => {
1061+
assert_eq!(parsed.bytes, unsigned_invoice.bytes);
1062+
assert_eq!(parsed.tagged_hash, unsigned_invoice.tagged_hash);
1063+
},
1064+
}
1065+
1066+
let invoice = unsigned_invoice.sign(recipient_sign).unwrap();
10191067

10201068
let mut buffer = Vec::new();
10211069
invoice.write(&mut buffer).unwrap();

lightning/src/offers/invoice_request.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,12 @@ impl InvoiceRequestContentsWithoutPayerId {
664664
}
665665
}
666666

667+
impl Writeable for UnsignedInvoiceRequest {
668+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
669+
WithoutLength(&self.bytes).write(writer)
670+
}
671+
}
672+
667673
impl Writeable for InvoiceRequest {
668674
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
669675
WithoutLength(&self.bytes).write(writer)
@@ -723,6 +729,25 @@ type PartialInvoiceRequestTlvStreamRef<'a> = (
723729
InvoiceRequestTlvStreamRef<'a>,
724730
);
725731

732+
impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
733+
type Error = Bolt12ParseError;
734+
735+
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
736+
let invoice_request = ParsedMessage::<PartialInvoiceRequestTlvStream>::try_from(bytes)?;
737+
let ParsedMessage { bytes, tlv_stream } = invoice_request;
738+
let (
739+
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
740+
) = tlv_stream;
741+
let contents = InvoiceRequestContents::try_from(
742+
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
743+
)?;
744+
745+
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
746+
747+
Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash })
748+
}
749+
}
750+
726751
impl TryFrom<Vec<u8>> for InvoiceRequest {
727752
type Error = Bolt12ParseError;
728753

@@ -792,7 +817,7 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
792817

793818
#[cfg(test)]
794819
mod tests {
795-
use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG};
820+
use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest};
796821

797822
use bitcoin::blockdata::constants::ChainHash;
798823
use bitcoin::network::constants::Network;
@@ -816,12 +841,24 @@ mod tests {
816841

817842
#[test]
818843
fn builds_invoice_request_with_defaults() {
819-
let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
844+
let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
820845
.amount_msats(1000)
821846
.build().unwrap()
822847
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
823-
.build().unwrap()
824-
.sign(payer_sign).unwrap();
848+
.build().unwrap();
849+
850+
let mut buffer = Vec::new();
851+
unsigned_invoice_request.write(&mut buffer).unwrap();
852+
853+
match UnsignedInvoiceRequest::try_from(buffer) {
854+
Err(e) => panic!("error parsing unsigned invoice request: {:?}", e),
855+
Ok(parsed) => {
856+
assert_eq!(parsed.bytes, unsigned_invoice_request.bytes);
857+
assert_eq!(parsed.tagged_hash, unsigned_invoice_request.tagged_hash);
858+
},
859+
}
860+
861+
let invoice_request = unsigned_invoice_request.sign(payer_sign).unwrap();
825862

826863
let mut buffer = Vec::new();
827864
invoice_request.write(&mut buffer).unwrap();

lightning/src/offers/merkle.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
3030
///
3131
/// [BIP 340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
3232
/// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md#signature-calculation
33+
#[derive(Debug, PartialEq)]
3334
pub struct TaggedHash(Message);
3435

3536
impl TaggedHash {

0 commit comments

Comments
 (0)