@@ -797,6 +797,12 @@ impl TryFrom<FullPayerProofTlvStream> for ParsedPayerProofFields {
797797 let preimage = proof_preimage. ok_or ( Bolt12ParseError :: Decode ( DecodeError :: InvalidValue ) ) ?;
798798 let proof_signature = proof_signature
799799 . ok_or ( Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: MissingSignature ) ) ?;
800+ // Per BOLT 12 PR 1295, both `proof_missing_hashes` and `proof_leaf_hashes`
801+ // TLVs MUST be present. `proof_omitted_markers` MAY be omitted when empty.
802+ let missing_hashes =
803+ proof_missing_hashes. ok_or ( Bolt12ParseError :: Decode ( DecodeError :: InvalidValue ) ) ?;
804+ let leaf_hashes =
805+ proof_leaf_hashes. ok_or ( Bolt12ParseError :: Decode ( DecodeError :: InvalidValue ) ) ?;
800806
801807 Ok ( Self {
802808 contents : PayerProofContents {
@@ -819,8 +825,8 @@ impl TryFrom<FullPayerProofTlvStream> for ParsedPayerProofFields {
819825 . into_iter ( )
820826 . map ( |marker| marker. 0 )
821827 . collect ( ) ,
822- missing_hashes : proof_missing_hashes . unwrap_or_default ( ) ,
823- leaf_hashes : proof_leaf_hashes . unwrap_or_default ( ) ,
828+ missing_hashes,
829+ leaf_hashes,
824830 } )
825831 }
826832}
@@ -1793,6 +1799,37 @@ mod tests {
17931799 assert ! ( result. is_err( ) , "payer_signature with len < 64 should be rejected" ) ;
17941800 }
17951801
1802+ /// Helper: serialize a payer_proof's bytes minus any TLV record matching `drop_type`.
1803+ fn proof_bytes_without_tlv ( proof : & PayerProof , drop_type : u64 ) -> Vec < u8 > {
1804+ let mut out = Vec :: new ( ) ;
1805+ for record in TlvStream :: new ( proof. bytes ( ) ) {
1806+ if record. r#type != drop_type {
1807+ out. extend_from_slice ( record. record_bytes ) ;
1808+ }
1809+ }
1810+ out
1811+ }
1812+
1813+ /// Per BOLT 12 PR 1295: the reader MUST reject a payer_proof if `proof_missing_hashes`
1814+ /// (TLV 1003) is missing.
1815+ #[ test]
1816+ fn test_parsing_rejects_missing_proof_missing_hashes ( ) {
1817+ let proof = build_round_trip_proof_with_disclosed_fields ( ) ;
1818+ let stripped = proof_bytes_without_tlv ( & proof, PAYER_PROOF_MISSING_HASHES_TYPE ) ;
1819+ let result = PayerProof :: try_from ( stripped) ;
1820+ assert ! ( result. is_err( ) , "missing proof_missing_hashes TLV must be rejected" ) ;
1821+ }
1822+
1823+ /// Per BOLT 12 PR 1295: the reader MUST reject a payer_proof if `proof_leaf_hashes`
1824+ /// (TLV 1004) is missing.
1825+ #[ test]
1826+ fn test_parsing_rejects_missing_proof_leaf_hashes ( ) {
1827+ let proof = build_round_trip_proof_with_disclosed_fields ( ) ;
1828+ let stripped = proof_bytes_without_tlv ( & proof, PAYER_PROOF_LEAF_HASHES_TYPE ) ;
1829+ let result = PayerProof :: try_from ( stripped) ;
1830+ assert ! ( result. is_err( ) , "missing proof_leaf_hashes TLV must be rejected" ) ;
1831+ }
1832+
17961833 #[ test]
17971834 fn test_round_trip_with_trailing_experimental_tlvs ( ) {
17981835 use core:: convert:: TryFrom ;
0 commit comments