Skip to content

Commit 9d9411f

Browse files
fixup! feat(offers): add BOLT 12 payer proof primitives
1 parent 5caca42 commit 9d9411f

2 files changed

Lines changed: 51 additions & 20 deletions

File tree

lightning/src/offers/invoice.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
15891589
});
15901590

15911591
/// Valid type range for experimental invoice TLV records.
1592-
pub(super) const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
1592+
pub(super) const EXPERIMENTAL_INVOICE_TYPES: core::ops::Range<u64> = 3_000_000_000..4_000_000_000;
15931593

15941594
#[cfg(not(test))]
15951595
tlv_stream!(

lightning/src/offers/merkle.rs

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//! Tagged hashes for use in signature calculation and verification.
1111
1212
use crate::io;
13+
use crate::offers::invoice::{EXPERIMENTAL_INVOICE_TYPES, INVOICE_TYPES};
14+
use crate::offers::offer::{EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES};
1315
use crate::util::ser::{BigSize, Readable, Writeable, Writer};
1416
use bitcoin::hashes::{sha256, Hash, HashEngine};
1517
use bitcoin::secp256k1::schnorr::Signature;
@@ -396,39 +398,40 @@ pub(super) fn compute_selective_disclosure<'a>(
396398

397399
/// Compute omitted markers per BOLT 12 payer proof spec.
398400
///
399-
/// Each omitted TLV gets a marker that is one greater than the previous included
400-
/// TLV type or the previous marker, except that a marker of 239 (the top of the
401-
/// low marker range) is followed by 1_000_000_000 (the start of the high range)
402-
/// to skip the signature type range. TLV type 0 is implicitly omitted (never
403-
/// assigned a marker).
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).
404406
fn compute_omitted_markers<'a>(
405407
tlv_data: impl Iterator<Item = &'a TlvMerkleData> + 'a,
406408
) -> impl Iterator<Item = u64> + 'a {
407409
tlv_data
408410
.filter(|data| data.tlv_type != 0)
409-
.scan((0u64, false), |(prev_value, prev_included), data| {
411+
.scan(0u64, |prev_value, data| {
410412
if data.is_included {
411413
*prev_value = data.tlv_type;
412-
*prev_included = true;
413414
Some(None)
414415
} else {
415-
// Per BOLT 12 PR 1295, omitted-TLV markers live in either `1..=239`
416-
// or `1_000_000_000..=3_999_999_999`. When the previous marker was
417-
// 239 the next one jumps to the start of the high range rather than
418-
// entering the signature type range.
419-
let marker = if !*prev_included && *prev_value == 239 {
420-
1_000_000_000
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
421424
} else {
422-
*prev_value + 1
425+
next
423426
};
424427
*prev_value = marker;
425-
*prev_included = false;
426428
// Real BOLT 12 invoices have far fewer than 239 non-signature TLVs,
427-
// so the high range is never reached in practice; this assert
428-
// documents and guards the invariant.
429+
// so the experimental range is never reached in practice; this
430+
// assert documents and guards the invariant.
429431
debug_assert!(
430-
(1..=239).contains(&marker)
431-
|| (1_000_000_000..=3_999_999_999).contains(&marker),
432+
(OFFER_TYPES.start..INVOICE_TYPES.end).contains(&marker)
433+
|| (EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_TYPES.end)
434+
.contains(&marker),
432435
"compute_omitted_markers produced marker {} outside spec ranges",
433436
marker,
434437
);
@@ -1101,6 +1104,34 @@ mod tests {
11011104
assert_eq!(markers, expected);
11021105
}
11031106

1107+
/// An *included* TLV at the top of the low range (type 239) followed by an
1108+
/// omitted TLV: the marker must skip the signature/payer-proof gap and jump
1109+
/// to the start of the experimental range, not land on 240.
1110+
#[test]
1111+
fn compute_omitted_markers_jumps_after_included_at_top_of_low_range() {
1112+
let dummy_hash = sha256::Hash::all_zeros();
1113+
let tlv_data = vec![
1114+
TlvMerkleData { tlv_type: 239, per_tlv_hash: dummy_hash, is_included: true },
1115+
TlvMerkleData { tlv_type: 1_500_000_000, per_tlv_hash: dummy_hash, is_included: false },
1116+
];
1117+
let markers: Vec<u64> = compute_omitted_markers(tlv_data.iter()).collect();
1118+
assert_eq!(markers, vec![1_000_000_000]);
1119+
}
1120+
1121+
/// After a jump into the experimental range, subsequent omitted markers
1122+
/// continue sequentially within that range.
1123+
#[test]
1124+
fn compute_omitted_markers_continue_in_experimental_range_after_jump() {
1125+
let dummy_hash = sha256::Hash::all_zeros();
1126+
let tlv_data = vec![
1127+
TlvMerkleData { tlv_type: 239, per_tlv_hash: dummy_hash, is_included: true },
1128+
TlvMerkleData { tlv_type: 3_000_000_000, per_tlv_hash: dummy_hash, is_included: false },
1129+
TlvMerkleData { tlv_type: 3_000_000_001, per_tlv_hash: dummy_hash, is_included: false },
1130+
];
1131+
let markers: Vec<u64> = compute_omitted_markers(tlv_data.iter()).collect();
1132+
assert_eq!(markers, vec![1_000_000_000, 1_000_000_001]);
1133+
}
1134+
11041135
#[test]
11051136
fn test_tlv_record_read_value_rejects_trailing_bytes() {
11061137
use bitcoin::secp256k1::PublicKey;

0 commit comments

Comments
 (0)