Skip to content

offers: fix recurrence proportional amount - #9413

Open
Lagrang3 wants to merge 1 commit into
ElementsProject:masterfrom
Lagrang3:fix-bolt12-prop-recurrence
Open

offers: fix recurrence proportional amount#9413
Lagrang3 wants to merge 1 commit into
ElementsProject:masterfrom
Lagrang3:fix-bolt12-prop-recurrence

Conversation

@Lagrang3

Copy link
Copy Markdown
Collaborator

offers: recurrence with proportional_amount now computes the correct invoice amount based on the time remaining

Reported-by: Vincenzo Palazzo (Bitcoin Security Council finding 2026-08-11)

Changelog-Fixed: offers: recurrence with proportional_amount now computes the correct invoice amount based on the time remaining

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
@Lagrang3
Lagrang3 force-pushed the fix-bolt12-prop-recurrence branch from 9ba7b32 to c0e2e19 Compare August 13, 2026 11:43

@nGoline nGoline left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two comments

Comment on lines +567 to +568
if (*ir->inv->invoice_created_at >= end) {
*ir->inv->invoice_amount = 1;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paywindow_end is pstart + seconds_after (common/bolt12.c:513) and seconds_after is an unconstrained u32, so an offer whose paywindow outlives its period reaches this branch. Past the period end the time remaining is zero, so the request should be rejected as too late rather than priced
at 1 msat.

invreq_recurrence(ir->invreq));

if (*ir->inv->invoice_created_at > start) {
assert(end > start);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a fail_invreq() rather than an assert(). offer_period_start() gives no guarantee that end > start. param_recurrence() (plugins/offers_offer.c:96) has no non-zero check on period, and the period == 0 guard at common/bolt12.c:290 is in offer_decode(), not invrequest_decode(), so a recurrence=0seconds offer reaches here with start == end. period_idx is also invreq_recurrence_start + invreq_recurrence_counter, i.e. remote-chosen, and offer_period_start() truncates period*n into time_change()'s u32 number (common/bolt12.c:449), so the sequence is not monotone across that range either. offers is a builtin and so important, and NDEBUG is never defined, so an abort here
takes the node down.

Suggested change
assert(end > start);
if (end <= start)
return fail_invreq(cmd, ir,
"period_index %"PRIu64" bad period",
period_idx);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants