From 96caf9f4abc7337c7cfe351054e6c7e93f2d42b2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 16 Dec 2020 13:48:42 +1030 Subject: [PATCH] fetchinvoice: return the next period for recurring offers. This is useful for the caller to know when to call again. Signed-off-by: Rusty Russell --- plugins/fetchinvoice.c | 47 ++++++++++++++++++++++++++++++++++++++++++ tests/test_pay.py | 15 ++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 8f7886f73..cf315791b 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -200,6 +200,15 @@ static struct command_result *recv_onion_message(struct command *cmd, } else expected_amount = NULL; + /* BOLT-offers #12: + * - if the offer contained `recurrence`: + * - MUST reject the invoice if `recurrence_basetime` is not set. + */ + if (sent->invreq->recurrence_counter && !inv->recurrence_basetime) { + badfield = "recurrence_basetime"; + goto badinv; + } + /* BOLT-offers #12: * - SHOULD confirm authorization if the `description` does not exactly * match the `offer` @@ -252,6 +261,44 @@ static struct command_result *recv_onion_message(struct command *cmd, amount_msat(*inv->amount)); json_object_end(out); + /* We tell them about next period at this point, if any. */ + if (sent->offer->recurrence) { + u64 next_counter, next_period_idx; + u64 paywindow_start, paywindow_end; + + next_counter = *sent->invreq->recurrence_counter + 1; + if (sent->invreq->recurrence_start) + next_period_idx = *sent->invreq->recurrence_start + + next_counter; + else + next_period_idx = next_counter; + + /* If this was the last, don't tell them about a next! */ + if (!sent->offer->recurrence_limit + || next_period_idx <= *sent->offer->recurrence_limit) { + json_object_start(out, "next_period"); + json_add_u64(out, "counter", next_counter); + json_add_u64(out, "starttime", + offer_period_start(*inv->recurrence_basetime, + next_period_idx, + sent->offer->recurrence)); + json_add_u64(out, "endtime", + offer_period_start(*inv->recurrence_basetime, + next_period_idx + 1, + sent->offer->recurrence) - 1); + + offer_period_paywindow(sent->offer->recurrence, + sent->offer->recurrence_paywindow, + sent->offer->recurrence_base, + *inv->recurrence_basetime, + next_period_idx, + &paywindow_start, &paywindow_end); + json_add_u64(out, "paywindow_start", paywindow_start); + json_add_u64(out, "paywindow_end", paywindow_end); + json_object_end(out); + } + } + discard_result(command_finished(sent->cmd, out)); return command_hook_success(cmd); diff --git a/tests/test_pay.py b/tests/test_pay.py index e36a485f4..cf1efd552 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3856,6 +3856,8 @@ def test_fetchinvoice(node_factory, bitcoind): inv1 = l1.rpc.call('fetchinvoice', {'offer': offer}) inv2 = l1.rpc.call('fetchinvoice', {'offer': offer}) assert inv1 != inv2 + assert 'next_period' not in inv1 + assert 'next_period' not in inv2 l1.rpc.pay(inv1['invoice']) l1.rpc.pay(inv2['invoice']) @@ -3868,6 +3870,8 @@ def test_fetchinvoice(node_factory, bitcoind): inv1 = l1.rpc.call('fetchinvoice', {'offer': offer}) inv2 = l1.rpc.call('fetchinvoice', {'offer': offer}) assert inv1 != inv2 + assert 'next_period' not in inv1 + assert 'next_period' not in inv2 l1.rpc.pay(inv1['invoice']) @@ -3890,6 +3894,11 @@ def test_fetchinvoice(node_factory, bitcoind): 'recurrence_counter': 0, 'recurrence_label': 'test recurrence'}) print(ret) + period1 = ret['next_period'] + assert period1['counter'] == 1 + assert period1['endtime'] == period1['starttime'] + 59 + assert period1['paywindow_start'] == period1['starttime'] - 60 + assert period1['paywindow_end'] == period1['endtime'] l1.rpc.pay(ret['invoice'], label='test recurrence') @@ -3897,5 +3906,11 @@ def test_fetchinvoice(node_factory, bitcoind): 'recurrence_counter': 1, 'recurrence_label': 'test recurrence'}) print(ret) + period2 = ret['next_period'] + assert period2['counter'] == 2 + assert period2['starttime'] == period1['endtime'] + 1 + assert period2['endtime'] == period2['starttime'] + 59 + assert period2['paywindow_start'] == period2['starttime'] - 60 + assert period2['paywindow_end'] == period2['endtime'] l1.rpc.pay(ret['invoice'], label='test recurrence')