mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-01 09:40:19 +01:00
fetchinvoice: check we're in the period before attempting to fetch.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
dae477175c
commit
bdfcec3eb6
4 changed files with 105 additions and 13 deletions
|
@ -229,7 +229,8 @@ AUTODATA(json_command, &disableoffer_command);
|
||||||
* but our main purpose is to fill in invreq->payer_info tweak. */
|
* but our main purpose is to fill in invreq->payer_info tweak. */
|
||||||
static struct command_result *prev_payment(struct command *cmd,
|
static struct command_result *prev_payment(struct command *cmd,
|
||||||
const char *label,
|
const char *label,
|
||||||
struct tlv_invoice_request *invreq)
|
struct tlv_invoice_request *invreq,
|
||||||
|
u64 **prev_basetime)
|
||||||
{
|
{
|
||||||
const struct wallet_payment **payments;
|
const struct wallet_payment **payments;
|
||||||
bool prev_paid = false;
|
bool prev_paid = false;
|
||||||
|
@ -294,9 +295,15 @@ static struct command_result *prev_payment(struct command *cmd,
|
||||||
prev_paid = true;
|
prev_paid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inv->payer_info)
|
if (inv->payer_info) {
|
||||||
invreq->payer_info
|
invreq->payer_info
|
||||||
= tal_dup_talarr(invreq, u8, inv->payer_info);
|
= tal_dup_talarr(invreq, u8, inv->payer_info);
|
||||||
|
*prev_basetime = tal_dup(cmd, u64,
|
||||||
|
inv->recurrence_basetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_paid && inv->payer_info)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!invreq->payer_info)
|
if (!invreq->payer_info)
|
||||||
|
@ -363,6 +370,7 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
|
||||||
struct tlv_invoice_request *invreq;
|
struct tlv_invoice_request *invreq;
|
||||||
const char *label;
|
const char *label;
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
|
u64 *prev_basetime = NULL;
|
||||||
|
|
||||||
if (!param(cmd, buffer, params,
|
if (!param(cmd, buffer, params,
|
||||||
p_req("bolt12", param_b12_invreq, &invreq),
|
p_req("bolt12", param_b12_invreq, &invreq),
|
||||||
|
@ -377,7 +385,8 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
|
||||||
|
|
||||||
if (*invreq->recurrence_counter != 0) {
|
if (*invreq->recurrence_counter != 0) {
|
||||||
struct command_result *err
|
struct command_result *err
|
||||||
= prev_payment(cmd, label, invreq);
|
= prev_payment(cmd, label, invreq,
|
||||||
|
&prev_basetime);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -430,6 +439,8 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
|
||||||
if (label)
|
if (label)
|
||||||
json_add_escaped_string(response, "recurrence_label",
|
json_add_escaped_string(response, "recurrence_label",
|
||||||
take(json_escape(NULL, label)));
|
take(json_escape(NULL, label)));
|
||||||
|
if (prev_basetime)
|
||||||
|
json_add_u64(response, "previous_basetime", *prev_basetime);
|
||||||
return command_success(cmd, response);
|
return command_success(cmd, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -682,6 +682,70 @@ static struct command_result *invreq_done(struct command *cmd,
|
||||||
json_tok_full(buf, t),
|
json_tok_full(buf, t),
|
||||||
fail);
|
fail);
|
||||||
|
|
||||||
|
/* Now that's given us the previous base, check this is an OK time
|
||||||
|
* to request an invoice. */
|
||||||
|
if (sent->invreq->recurrence_counter) {
|
||||||
|
u64 *base;
|
||||||
|
const jsmntok_t *pbtok;
|
||||||
|
u64 period_idx = *sent->invreq->recurrence_counter;
|
||||||
|
|
||||||
|
if (sent->invreq->recurrence_start)
|
||||||
|
period_idx += *sent->invreq->recurrence_start;
|
||||||
|
|
||||||
|
/* BOLT-offers #12:
|
||||||
|
* - if the offer contained `recurrence_limit`:
|
||||||
|
* - MUST NOT send an `invoice_request` for a period greater
|
||||||
|
* than `max_period`
|
||||||
|
*/
|
||||||
|
if (sent->offer->recurrence_limit
|
||||||
|
&& period_idx > *sent->offer->recurrence_limit)
|
||||||
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
|
"Can't send invreq for period %"
|
||||||
|
PRIu64" (limit %u)",
|
||||||
|
period_idx,
|
||||||
|
*sent->offer->recurrence_limit);
|
||||||
|
|
||||||
|
/* BOLT-offers #12:
|
||||||
|
* - SHOULD NOT send an `invoice_request` for a period which has
|
||||||
|
* already passed.
|
||||||
|
*/
|
||||||
|
/* If there's no recurrence_base, we need a previous payment
|
||||||
|
* for this: fortunately createinvoicerequest does that
|
||||||
|
* lookup. */
|
||||||
|
pbtok = json_get_member(buf, result, "previous_basetime");
|
||||||
|
if (pbtok) {
|
||||||
|
base = tal(tmpctx, u64);
|
||||||
|
json_to_u64(buf, pbtok, base);
|
||||||
|
} else if (sent->offer->recurrence_base)
|
||||||
|
base = &sent->offer->recurrence_base->basetime;
|
||||||
|
else {
|
||||||
|
/* happens with *recurrence_base == 0 */
|
||||||
|
assert(*sent->invreq->recurrence_counter == 0);
|
||||||
|
base = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base) {
|
||||||
|
u64 period_start, period_end, now = time_now().ts.tv_sec;
|
||||||
|
offer_period_paywindow(sent->offer->recurrence,
|
||||||
|
sent->offer->recurrence_paywindow,
|
||||||
|
sent->offer->recurrence_base,
|
||||||
|
*base, period_idx,
|
||||||
|
&period_start, &period_end);
|
||||||
|
if (now < period_start)
|
||||||
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
|
"Too early: can't send until time %"
|
||||||
|
PRIu64" (in %"PRIu64" secs)",
|
||||||
|
period_start,
|
||||||
|
period_start - now);
|
||||||
|
if (now > period_end)
|
||||||
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
|
"Too late: expired time %"
|
||||||
|
PRIu64" (%"PRIu64" secs ago)",
|
||||||
|
period_end,
|
||||||
|
now - period_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rawinvreq = tal_arr(tmpctx, u8, 0);
|
rawinvreq = tal_arr(tmpctx, u8, 0);
|
||||||
towire_invoice_request(&rawinvreq, sent->invreq);
|
towire_invoice_request(&rawinvreq, sent->invreq);
|
||||||
return send_message(cmd, sent, "invoice_request", rawinvreq,
|
return send_message(cmd, sent, "invoice_request", rawinvreq,
|
||||||
|
@ -826,14 +890,6 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||||
if (!rec_label)
|
if (!rec_label)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"needs recurrence_label");
|
"needs recurrence_label");
|
||||||
|
|
||||||
/* FIXME! */
|
|
||||||
/* BOLT-offers #12:
|
|
||||||
* - SHOULD NOT send an `invoice_request` for a period which has
|
|
||||||
* already passed.
|
|
||||||
*/
|
|
||||||
/* If there's no recurrence_base, we need the initial payment
|
|
||||||
* for this... */
|
|
||||||
} else {
|
} else {
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - otherwise:
|
* - otherwise:
|
||||||
|
|
|
@ -134,7 +134,7 @@ static const struct plugin_command commands[] = {
|
||||||
"offer",
|
"offer",
|
||||||
"payment",
|
"payment",
|
||||||
"Create an offer",
|
"Create an offer",
|
||||||
"Create an offer for invoices of {amount} with {destination}, optional {vendor}, {quantity_min}, {quantity_max}, {absolute_expiry}, {recurrence}, {recurrence_base}, {recurrence_paywindow}, {recurrence_limit} and {single_use}",
|
"Create an offer for invoices of {amount} with {description}, optional {vendor}, {quantity_min}, {quantity_max}, {absolute_expiry}, {recurrence}, {recurrence_base}, {recurrence_paywindow}, {recurrence_limit} and {single_use}",
|
||||||
json_offer
|
json_offer
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3917,7 +3917,7 @@ def test_fetchinvoice(node_factory, bitcoind):
|
||||||
l1.rpc.pay(ret['invoice'], label='test recurrence')
|
l1.rpc.pay(ret['invoice'], label='test recurrence')
|
||||||
|
|
||||||
# Now we can, but it's too early:
|
# Now we can, but it's too early:
|
||||||
with pytest.raises(RpcError, match='Remote node sent failure message.*too early'):
|
with pytest.raises(RpcError, match="Too early: can't send until time {}".format(period1['starttime'])):
|
||||||
l1.rpc.call('fetchinvoice', {'offer': offer3,
|
l1.rpc.call('fetchinvoice', {'offer': offer3,
|
||||||
'recurrence_counter': 2,
|
'recurrence_counter': 2,
|
||||||
'recurrence_label': 'test recurrence'})
|
'recurrence_label': 'test recurrence'})
|
||||||
|
@ -3935,6 +3935,31 @@ def test_fetchinvoice(node_factory, bitcoind):
|
||||||
with pytest.raises(RpcError, match='Timeout waiting for response'):
|
with pytest.raises(RpcError, match='Timeout waiting for response'):
|
||||||
l1.rpc.call('fetchinvoice', {'offer': offer1, 'timeout': 10})
|
l1.rpc.call('fetchinvoice', {'offer': offer1, 'timeout': 10})
|
||||||
|
|
||||||
|
# Now try an offer with a more complex paywindow (only 10 seconds before)
|
||||||
|
offer = l2.rpc.call('offer', {'amount': '1msat',
|
||||||
|
'description': 'paywindow test',
|
||||||
|
'recurrence': '20seconds',
|
||||||
|
'recurrence_paywindow': '-10+0'})['bolt12']
|
||||||
|
|
||||||
|
ret = l1.rpc.call('fetchinvoice', {'offer': offer,
|
||||||
|
'recurrence_counter': 0,
|
||||||
|
'recurrence_label': 'test paywindow'})
|
||||||
|
period3 = ret['next_period']
|
||||||
|
assert period3['counter'] == 1
|
||||||
|
assert period3['endtime'] == period3['starttime'] + 19
|
||||||
|
assert period3['paywindow_start'] == period3['starttime'] - 10
|
||||||
|
assert period3['paywindow_end'] == period3['starttime']
|
||||||
|
l1.rpc.pay(ret['invoice'], label='test paywindow')
|
||||||
|
|
||||||
|
# Wait until too late!
|
||||||
|
while int(time.time()) <= period3['paywindow_end']:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
with pytest.raises(RpcError, match="Too late: expired time {}".format(period3['paywindow_end'])):
|
||||||
|
l1.rpc.call('fetchinvoice', {'offer': offer,
|
||||||
|
'recurrence_counter': 1,
|
||||||
|
'recurrence_label': 'test paywindow'})
|
||||||
|
|
||||||
|
|
||||||
def test_pay_waitblockheight_timeout(node_factory, bitcoind):
|
def test_pay_waitblockheight_timeout(node_factory, bitcoind):
|
||||||
plugin = os.path.join(os.path.dirname(__file__), 'plugins', 'endlesswaitblockheight.py')
|
plugin = os.path.join(os.path.dirname(__file__), 'plugins', 'endlesswaitblockheight.py')
|
||||||
|
|
Loading…
Add table
Reference in a new issue