@@ -83,11 +83,12 @@ use crate::ln::features::InvoiceRequestFeatures;
8383use  crate :: ln:: inbound_payment:: { ExpandedKey ,  Nonce } ; 
8484use  crate :: ln:: msgs:: { DecodeError ,  MAX_VALUE_MSAT } ; 
8585use  crate :: offers:: invoice:: { BlindedPayInfo ,  InvoiceBuilder } ; 
86- use  crate :: offers:: invoice_request:: { InvoiceRequestTlvStream ,  InvoiceRequestTlvStreamRef } ; 
87- use  crate :: offers:: offer:: { OfferTlvStream ,  OfferTlvStreamRef } ; 
86+ use  crate :: offers:: invoice_request:: { INVOICE_REQUEST_PAYER_ID_TYPE ,  INVOICE_REQUEST_TYPES ,  InvoiceRequestTlvStream ,  InvoiceRequestTlvStreamRef } ; 
87+ use  crate :: offers:: merkle:: TlvStream ; 
88+ use  crate :: offers:: offer:: { OFFER_TYPES ,  OfferTlvStream ,  OfferTlvStreamRef } ; 
8889use  crate :: offers:: parse:: { Bech32Encode ,  ParseError ,  ParsedMessage ,  SemanticError } ; 
89- use  crate :: offers:: payer:: { PayerContents ,  PayerTlvStream ,  PayerTlvStreamRef } ; 
90- use  crate :: offers:: signer:: { MetadataMaterial ,  DerivedPubkey } ; 
90+ use  crate :: offers:: payer:: { PAYER_METADATA_TYPE ,   PayerContents ,  PayerTlvStream ,  PayerTlvStreamRef } ; 
91+ use  crate :: offers:: signer:: { MetadataMaterial ,  DerivedPubkey ,   self } ; 
9192use  crate :: onion_message:: BlindedPath ; 
9293use  crate :: util:: ser:: { SeekReadable ,  WithoutLength ,  Writeable ,  Writer } ; 
9394use  crate :: util:: string:: PrintableString ; 
@@ -461,6 +462,20 @@ impl RefundContents {
461462		ChainHash :: using_genesis_block ( Network :: Bitcoin ) 
462463	} 
463464
465+ 	/// Verifies that the payer metadata was produced from the refund in the TLV stream. 
466+ pub ( super )  fn  verify ( & self ,  tlv_stream :  TlvStream < ' _ > ,  key :  & ExpandedKey )  -> bool  { 
467+ 		let  offer_records = tlv_stream. clone ( ) . range ( OFFER_TYPES ) ; 
468+ 		let  invreq_records = tlv_stream. range ( INVOICE_REQUEST_TYPES ) . filter ( |record| { 
469+ 			match  record. r#type  { 
470+ 				PAYER_METADATA_TYPE  => false ,  // Should be outside range 
471+ 				INVOICE_REQUEST_PAYER_ID_TYPE  => false , 
472+ 				_ => true , 
473+ 			} 
474+ 		} ) ; 
475+ 		let  tlv_stream = offer_records. chain ( invreq_records) ; 
476+ 		signer:: verify_metadata ( & self . payer . 0 ,  key,  tlv_stream) 
477+ 	} 
478+ 
464479	pub ( super )  fn  as_tlv_stream ( & self )  -> RefundTlvStreamRef  { 
465480		let  payer = PayerTlvStreamRef  { 
466481			metadata :  Some ( & self . payer . 0 ) , 
@@ -638,12 +653,15 @@ mod tests {
638653	use  bitcoin:: secp256k1:: { KeyPair ,  Secp256k1 ,  SecretKey } ; 
639654	use  core:: convert:: TryFrom ; 
640655	use  core:: time:: Duration ; 
656+ 	use  crate :: chain:: keysinterface:: KeyMaterial ; 
641657	use  crate :: ln:: features:: { InvoiceRequestFeatures ,  OfferFeatures } ; 
658+ 	use  crate :: ln:: inbound_payment:: { ExpandedKey ,  Nonce } ; 
642659	use  crate :: ln:: msgs:: { DecodeError ,  MAX_VALUE_MSAT } ; 
643660	use  crate :: offers:: invoice_request:: InvoiceRequestTlvStreamRef ; 
644661	use  crate :: offers:: offer:: OfferTlvStreamRef ; 
645662	use  crate :: offers:: parse:: { ParseError ,  SemanticError } ; 
646663	use  crate :: offers:: payer:: PayerTlvStreamRef ; 
664+ 	use  crate :: offers:: signer:: DerivedPubkey ; 
647665	use  crate :: offers:: test_utils:: * ; 
648666	use  crate :: onion_message:: { BlindedHop ,  BlindedPath } ; 
649667	use  crate :: util:: ser:: { BigSize ,  Writeable } ; 
@@ -724,6 +742,87 @@ mod tests {
724742		} 
725743	} 
726744
745+ 	#[ test]  
746+ 	fn  builds_refund_with_metadata_derived ( )  { 
747+ 		let  expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ;  32 ] ) ) ; 
748+ 		let  nonce = Nonce ( [ 42 ;  Nonce :: LENGTH ] ) ; 
749+ 
750+ 		let  refund = RefundBuilder :: new ( "foo" . into ( ) ,  vec ! [ 1 ;  32 ] ,  payer_pubkey ( ) ,  1000 ) . unwrap ( ) 
751+ 			. metadata_derived ( & expanded_key,  nonce) . unwrap ( ) 
752+ 			. build ( ) . unwrap ( ) ; 
753+ 		assert_eq ! ( refund. metadata( ) [ ..Nonce :: LENGTH ] ,  nonce. 0 ) ; 
754+ 		assert_eq ! ( refund. payer_id( ) ,  payer_pubkey( ) ) ; 
755+ 
756+ 		let  invoice = refund
757+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
758+ 			. unwrap ( ) 
759+ 			. build ( ) . unwrap ( ) 
760+ 			. sign ( recipient_sign) . unwrap ( ) ; 
761+ 		assert ! ( invoice. verify( & expanded_key) ) ; 
762+ 
763+ 		let  mut  tlv_stream = refund. as_tlv_stream ( ) ; 
764+ 		tlv_stream. 2 . amount  = Some ( 2000 ) ; 
765+ 
766+ 		let  mut  encoded_refund = Vec :: new ( ) ; 
767+ 		tlv_stream. write ( & mut  encoded_refund) . unwrap ( ) ; 
768+ 
769+ 		let  invoice = Refund :: try_from ( encoded_refund) . unwrap ( ) 
770+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
771+ 			. unwrap ( ) 
772+ 			. build ( ) . unwrap ( ) 
773+ 			. sign ( recipient_sign) . unwrap ( ) ; 
774+ 		assert ! ( !invoice. verify( & expanded_key) ) ; 
775+ 
776+ 		match  RefundBuilder :: new ( "foo" . into ( ) ,  vec ! [ 1 ;  32 ] ,  payer_pubkey ( ) ,  1000 ) . unwrap ( ) 
777+ 			. metadata_derived ( & expanded_key,  nonce) . unwrap ( ) 
778+ 			. metadata_derived ( & expanded_key,  nonce) 
779+ 		{ 
780+ 			Ok ( _)  => panic ! ( "expected error" ) , 
781+ 			Err ( e)  => assert_eq ! ( e,  SemanticError :: UnexpectedMetadata ) , 
782+ 		} 
783+ 	} 
784+ 
785+ 	#[ test]  
786+ 	fn  builds_refund_with_derived_payer_id ( )  { 
787+ 		let  expanded_key = ExpandedKey :: new ( & KeyMaterial ( [ 42 ;  32 ] ) ) ; 
788+ 		let  nonce = Nonce ( [ 42 ;  Nonce :: LENGTH ] ) ; 
789+ 		let  keys = expanded_key. signing_keypair_for_offer ( nonce) ; 
790+ 
791+ 		let  payer_pubkey = DerivedPubkey :: new ( & expanded_key,  nonce) ; 
792+ 		let  refund = RefundBuilder :: deriving_payer_id ( "foo" . into ( ) ,  payer_pubkey,  1000 ) . unwrap ( ) 
793+ 			. build ( ) . unwrap ( ) ; 
794+ 		assert_eq ! ( refund. metadata( ) [ ..Nonce :: LENGTH ] ,  nonce. 0 ) ; 
795+ 		assert_eq ! ( refund. payer_id( ) ,  keys. public_key( ) ) ; 
796+ 
797+ 		let  invoice = refund
798+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
799+ 			. unwrap ( ) 
800+ 			. build ( ) . unwrap ( ) 
801+ 			. sign ( recipient_sign) . unwrap ( ) ; 
802+ 		assert ! ( invoice. verify( & expanded_key) ) ; 
803+ 
804+ 		let  mut  tlv_stream = refund. as_tlv_stream ( ) ; 
805+ 		tlv_stream. 2 . amount  = Some ( 2000 ) ; 
806+ 
807+ 		let  mut  encoded_refund = Vec :: new ( ) ; 
808+ 		tlv_stream. write ( & mut  encoded_refund) . unwrap ( ) ; 
809+ 
810+ 		let  invoice = Refund :: try_from ( encoded_refund) . unwrap ( ) 
811+ 			. respond_with_no_std ( payment_paths ( ) ,  payment_hash ( ) ,  recipient_pubkey ( ) ,  now ( ) ) 
812+ 			. unwrap ( ) 
813+ 			. build ( ) . unwrap ( ) 
814+ 			. sign ( recipient_sign) . unwrap ( ) ; 
815+ 		assert ! ( !invoice. verify( & expanded_key) ) ; 
816+ 
817+ 		let  payer_pubkey = DerivedPubkey :: new ( & expanded_key,  nonce) ; 
818+ 		match  RefundBuilder :: deriving_payer_id ( "foo" . into ( ) ,  payer_pubkey,  1000 ) . unwrap ( ) 
819+ 			. metadata_derived ( & expanded_key,  nonce) 
820+ 		{ 
821+ 			Ok ( _)  => panic ! ( "expected error" ) , 
822+ 			Err ( e)  => assert_eq ! ( e,  SemanticError :: UnexpectedMetadata ) , 
823+ 		} 
824+ 	} 
825+ 
727826	#[ test]  
728827	fn  builds_refund_with_absolute_expiry ( )  { 
729828		let  future_expiry = Duration :: from_secs ( u64:: max_value ( ) ) ; 
0 commit comments