@@ -396,13 +396,28 @@ pub(super) fn compute_selective_disclosure<'a>(
396396 Ok ( SelectiveDisclosure { leaf_hashes, omitted_markers, missing_hashes, merkle_root } )
397397}
398398
399+ /// Returns the marker number that follows `prev` (an included TLV type or a
400+ /// previous marker) per BOLT 12 PR 1295.
401+ ///
402+ /// A marker is one greater than the previous value, except that a value landing
403+ /// in the gap between the invoice TLV range and the experimental range (the
404+ /// signature/payer-proof range) jumps to the start of the experimental range.
405+ /// The producer and the readers all go through this so their marker sequences
406+ /// stay in agreement.
407+ pub ( super ) fn next_marker ( prev : u64 ) -> u64 {
408+ let next = prev. saturating_add ( 1 ) ;
409+ if ( INVOICE_TYPES . end ..EXPERIMENTAL_OFFER_TYPES . start ) . contains ( & next) {
410+ EXPERIMENTAL_OFFER_TYPES . start
411+ } else {
412+ next
413+ }
414+ }
415+
399416/// Compute omitted markers per BOLT 12 payer proof spec.
400417///
401- /// Each omitted TLV gets a marker one greater than the previous included TLV
402- /// type or the previous marker. If that value would land in the gap between the
403- /// invoice TLV range and the experimental range, it jumps to the start of the
404- /// experimental range instead. TLV type 0 is implicitly omitted (never assigned
405- /// a marker).
418+ /// Each omitted TLV gets the marker number following the previous included TLV
419+ /// type or the previous marker (see [`next_marker`]). TLV type 0 is implicitly
420+ /// omitted (never assigned a marker).
406421fn compute_omitted_markers < ' a > (
407422 tlv_data : impl Iterator < Item = & ' a TlvMerkleData > + ' a ,
408423) -> impl Iterator < Item = u64 > + ' a {
@@ -413,17 +428,7 @@ fn compute_omitted_markers<'a>(
413428 * prev_value = data. tlv_type ;
414429 Some ( None )
415430 } else {
416- // Per BOLT 12 PR 1295, omitted-TLV markers live in either the
417- // invoice TLV range or the experimental range. A marker that would
418- // land in the gap between them (the signature/payer-proof range)
419- // jumps to the start of the experimental range.
420- let next = prev_value. saturating_add ( 1 ) ;
421- let marker = if ( INVOICE_TYPES . end ..EXPERIMENTAL_OFFER_TYPES . start ) . contains ( & next)
422- {
423- EXPERIMENTAL_OFFER_TYPES . start
424- } else {
425- next
426- } ;
431+ let marker = next_marker ( * prev_value) ;
427432 * prev_value = marker;
428433 // Real BOLT 12 invoices have far fewer than 239 non-signature TLVs,
429434 // so the experimental range is never reached in practice; this
@@ -526,7 +531,7 @@ pub(super) fn reconstruct_merkle_root(
526531 } else {
527532 let marker = omitted_markers[ mrk_idx] ;
528533 let inc_type = included_records[ inc_idx] . r#type ;
529- if marker == prev_marker + 1 {
534+ if marker == next_marker ( prev_marker) {
530535 hashes. push ( None ) ;
531536 prev_marker = marker;
532537 mrk_idx += 1 ;
@@ -615,7 +620,7 @@ fn validate_omitted_markers(markers: &[u64]) -> Result<(), SelectiveDisclosureEr
615620/// - After included type X, the next marker in that run equals X + 1
616621///
617622/// The algorithm tracks `prev_marker` to detect continuations vs jumps:
618- /// - If `marker == prev_marker + 1 `: continuation → omitted position
623+ /// - If `marker == next_marker( prev_marker) `: continuation → omitted position
619624/// - Otherwise: jump → included position comes first, then process marker as continuation
620625///
621626/// Example: included=[10, 40], markers=[11, 12, 41, 42]
@@ -652,7 +657,7 @@ fn reconstruct_positions(included_types: &[u64], omitted_markers: &[u64]) -> Vec
652657 let marker = omitted_markers[ mrk_idx] ;
653658 let inc_type = included_types[ inc_idx] ;
654659
655- if marker == prev_marker + 1 {
660+ if marker == next_marker ( prev_marker) {
656661 // Continuation of current run → this position is omitted
657662 positions. push ( false ) ;
658663 prev_marker = marker;
@@ -1132,6 +1137,18 @@ mod tests {
11321137 assert_eq ! ( markers, vec![ 1_000_000_000 , 1_000_000_001 ] ) ;
11331138 }
11341139
1140+ /// [`next_marker`] increments by one within a range but jumps over the
1141+ /// signature/payer-proof gap, so producer and readers stay in agreement.
1142+ #[ test]
1143+ fn next_marker_jumps_the_gap ( ) {
1144+ assert_eq ! ( super :: next_marker( 0 ) , 1 ) ;
1145+ assert_eq ! ( super :: next_marker( 5 ) , 6 ) ;
1146+ assert_eq ! ( super :: next_marker( 238 ) , 239 ) ;
1147+ // 240 would land in the signature range, so it jumps to the experimental range.
1148+ assert_eq ! ( super :: next_marker( 239 ) , 1_000_000_000 ) ;
1149+ assert_eq ! ( super :: next_marker( 1_000_000_000 ) , 1_000_000_001 ) ;
1150+ }
1151+
11351152 #[ test]
11361153 fn test_tlv_record_read_value_rejects_trailing_bytes ( ) {
11371154 use bitcoin:: secp256k1:: PublicKey ;
0 commit comments