@@ -368,6 +368,11 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
368368}
369369
370370/// A semantically valid [`Bolt12Invoice`] that hasn't been signed.
371+ ///
372+ /// # Serialization
373+ ///
374+ /// This is serialized as a TLV stream, which includes TLV records from the originating message. As
375+ /// such, it may include unknown, odd TLV records.
371376pub struct UnsignedBolt12Invoice {
372377 bytes : Vec < u8 > ,
373378 contents : InvoiceContents ,
@@ -396,7 +401,9 @@ impl UnsignedBolt12Invoice {
396401 self . contents . fields ( ) . signing_pubkey
397402 }
398403
399- /// Signs the invoice using the given function.
404+ /// Signs the [`TaggedHash`] of the invoice using the given function.
405+ ///
406+ /// Note: The hash computation may have included unknown, odd TLV records.
400407 ///
401408 /// This is not exported to bindings users as functions aren't currently mapped.
402409 pub fn sign < F , E > ( mut self , sign : F ) -> Result < Bolt12Invoice , SignError < E > >
@@ -733,6 +740,12 @@ impl InvoiceFields {
733740 }
734741}
735742
743+ impl Writeable for UnsignedBolt12Invoice {
744+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
745+ WithoutLength ( & self . bytes ) . write ( writer)
746+ }
747+ }
748+
736749impl Writeable for Bolt12Invoice {
737750 fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
738751 WithoutLength ( & self . bytes ) . write ( writer)
@@ -745,6 +758,25 @@ impl Writeable for InvoiceContents {
745758 }
746759}
747760
761+ impl TryFrom < Vec < u8 > > for UnsignedBolt12Invoice {
762+ type Error = Bolt12ParseError ;
763+
764+ fn try_from ( bytes : Vec < u8 > ) -> Result < Self , Self :: Error > {
765+ let invoice = ParsedMessage :: < PartialInvoiceTlvStream > :: try_from ( bytes) ?;
766+ let ParsedMessage { bytes, tlv_stream } = invoice;
767+ let (
768+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
769+ ) = tlv_stream;
770+ let contents = InvoiceContents :: try_from (
771+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
772+ ) ?;
773+
774+ let tagged_hash = TaggedHash :: new ( SIGNATURE_TAG , & bytes) ;
775+
776+ Ok ( UnsignedBolt12Invoice { bytes, contents, tagged_hash } )
777+ }
778+ }
779+
748780impl TryFrom < Vec < u8 > > for Bolt12Invoice {
749781 type Error = Bolt12ParseError ;
750782
@@ -857,6 +889,17 @@ type PartialInvoiceTlvStreamRef<'a> = (
857889 InvoiceTlvStreamRef < ' a > ,
858890) ;
859891
892+ impl SeekReadable for PartialInvoiceTlvStream {
893+ fn read < R : io:: Read + io:: Seek > ( r : & mut R ) -> Result < Self , DecodeError > {
894+ let payer = SeekReadable :: read ( r) ?;
895+ let offer = SeekReadable :: read ( r) ?;
896+ let invoice_request = SeekReadable :: read ( r) ?;
897+ let invoice = SeekReadable :: read ( r) ?;
898+
899+ Ok ( ( payer, offer, invoice_request, invoice) )
900+ }
901+ }
902+
860903impl TryFrom < ParsedMessage < FullInvoiceTlvStream > > for Bolt12Invoice {
861904 type Error = Bolt12ParseError ;
862905
@@ -961,7 +1004,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
9611004
9621005#[ cfg( test) ]
9631006mod tests {
964- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , FallbackAddress , FullInvoiceTlvStreamRef , InvoiceTlvStreamRef , SIGNATURE_TAG } ;
1007+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , FallbackAddress , FullInvoiceTlvStreamRef , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
9651008
9661009 use bitcoin:: blockdata:: script:: Script ;
9671010 use bitcoin:: hashes:: Hash ;
@@ -1007,15 +1050,27 @@ mod tests {
10071050 let payment_paths = payment_paths ( ) ;
10081051 let payment_hash = payment_hash ( ) ;
10091052 let now = now ( ) ;
1010- let invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
1053+ let unsigned_invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
10111054 . amount_msats ( 1000 )
10121055 . build ( ) . unwrap ( )
10131056 . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
10141057 . build ( ) . unwrap ( )
10151058 . sign ( payer_sign) . unwrap ( )
10161059 . respond_with_no_std ( payment_paths. clone ( ) , payment_hash, now) . unwrap ( )
1017- . build ( ) . unwrap ( )
1018- . sign ( recipient_sign) . unwrap ( ) ;
1060+ . build ( ) . unwrap ( ) ;
1061+
1062+ let mut buffer = Vec :: new ( ) ;
1063+ unsigned_invoice. write ( & mut buffer) . unwrap ( ) ;
1064+
1065+ match UnsignedBolt12Invoice :: try_from ( buffer) {
1066+ Err ( e) => panic ! ( "error parsing unsigned invoice: {:?}" , e) ,
1067+ Ok ( parsed) => {
1068+ assert_eq ! ( parsed. bytes, unsigned_invoice. bytes) ;
1069+ assert_eq ! ( parsed. tagged_hash, unsigned_invoice. tagged_hash) ;
1070+ } ,
1071+ }
1072+
1073+ let invoice = unsigned_invoice. sign ( recipient_sign) . unwrap ( ) ;
10191074
10201075 let mut buffer = Vec :: new ( ) ;
10211076 invoice. write ( & mut buffer) . unwrap ( ) ;
0 commit comments