offers: handle re-fetching the same invoice twice.

We get a label clash: easy, just re-serve:

```
2021-02-18T04:29:37.474Z **BROKEN** plugin-offers: Failed invoice_request lnr1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcyyqwtp0rmsgquvuacqcl5cdfzwzmu3v8tqgvpqs8e80dlmxm7ey4xwrqdsqqqqqqqqqqqqqqqq2pqqfqpqynzqzx9rylzy40ernj4jzc3p2dwy3n8x6lqeaywwk725ghx4kx63pcfxgg2z3nsn80jzge06nt3ks8pr6rvnujq48376lpmrr3cq04nurpy783eyr0awh5773lrlmjek07rjf0nx4g9235ulkcs7jp2h5gumjyquhadh846da3jptxm9g0qz5lne4hjhag for offer 1cb0bc7b8201c673b8063f4c352270b7c8b0eb02181040f93bdbfd9b7ec92a67: Got JSON error: {\"code\":900,\"message\":\"Duplicate label\",\"data\":{\"label\":\"1cb0bc7b8201c673b8063f4c352270b7c8b0eb02181040f93bdbfd9b7ec92a67-08c5193e2255f91ce5590b110a9ae2466736be0cf48e75bcaa22e6ad8da88709-1\",\"bolt12\":\"lni1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcyyqwtp0rmsgquvuacqcl5cdfzwzmu3v8tqgvpqs8e80dlmxm7ey4xwzqrw4lauzsc2ajk26mv0ysxxmmxvejk2grxdaezqun4wd68jggvpkqqqqqqqqqqqqqqqqpgyqq7ypymf9efe2jj5r2mzunlqz67d75ht3ukxk0x9ftkcuknrgepsgupwfqpqynzqzx9rylzy40ernj4jzc3p2dwy3n8x6lqeaywwk725ghx4kx63pcf9qzxqt0dxq4zqwtz2qu44gzx7nzczc494cce2tgph5xgu5sn7vh8frky9z5n08xj9sp3yaxe9cqs5vss59r8pxwlyy3jl4xhrdqwz85xe9qqgcpda590qs9khxdx5qpetlx0j6ap0wsxagssmy2qjvhjp2kc3na54pht3pp76c405upne360lh8rzye32xxq6l0phpkk9pu9lwxnqkxuwt2nqqr9u\",\"payment_hash\":\"396250395aa046f4c58162a5ae31952d01bd0c8e5213f32e748ec428a9379cd2\",\"msatoshi\":7700446,\"amount_msat\":\"7700446msat\",\"status\":\"unpaid\",\"description\":\"Weekly coffee for rusty!\",\"expires_at\":1614832137,\"local_offer_id\":\"1cb0bc7b8201c673b8063f4c352270b7c8b0eb02181040f93bdbfd9b7ec92a67\"}}
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2021-02-18 15:37:43 +10:30
parent 874ca99c32
commit f0fa5d1401
2 changed files with 32 additions and 1 deletions

View File

@ -6,6 +6,7 @@
#include <common/bolt12_merkle.h>
#include <common/iso4217.h>
#include <common/json_stream.h>
#include <common/jsonrpc_errors.h>
#include <common/overflows.h>
#include <common/type_to_string.h>
#include <plugins/offers.h>
@ -160,6 +161,7 @@ static struct command_result *error(struct command *cmd,
json_tok_full(buf, err));
}
/* We can fail to create the invoice if we've already done so. */
static struct command_result *createinvoice_done(struct command *cmd,
const char *buf,
const jsmntok_t *result,
@ -182,6 +184,23 @@ static struct command_result *createinvoice_done(struct command *cmd,
return send_onion_reply(cmd, ir->buf, ir->replytok, "invoice", rawinv);
}
static struct command_result *createinvoice_error(struct command *cmd,
const char *buf,
const jsmntok_t *err,
struct invreq *ir)
{
u32 code;
/* If it already exists, we can reuse its bolt12 directly. */
if (json_scan(tmpctx, buf, err,
"{code:%}", JSON_SCAN(json_to_u32, &code)) == NULL
&& code == INVOICE_LABEL_ALREADY_EXISTS) {
return createinvoice_done(cmd, buf,
json_get_member(buf, err, "data"), ir);
}
return error(cmd, buf, err, ir);
}
static struct command_result *create_invoicereq(struct command *cmd,
struct invreq *ir)
{
@ -189,7 +208,7 @@ static struct command_result *create_invoicereq(struct command *cmd,
/* Now, write invoice to db (returns the signed version) */
req = jsonrpc_request_start(cmd->plugin, cmd, "createinvoice",
createinvoice_done, error, ir);
createinvoice_done, createinvoice_error, ir);
json_add_string(req->js, "invstring", invoice_encode(tmpctx, ir->inv));
json_add_preimage(req->js, "preimage", &ir->preimage);

View File

@ -4061,6 +4061,18 @@ def test_fetchinvoice(node_factory, bitcoind):
assert period3['paywindow_end'] == period3['starttime']
l1.rpc.pay(ret['invoice'], label='test paywindow')
# We can get another invoice, as many times as we want.
# (It may return the same one!).
while int(time.time()) <= period3['paywindow_start']:
time.sleep(1)
l1.rpc.call('fetchinvoice', {'offer': offer,
'recurrence_counter': 1,
'recurrence_label': 'test paywindow'})
l1.rpc.call('fetchinvoice', {'offer': offer,
'recurrence_counter': 1,
'recurrence_label': 'test paywindow'})
# Wait until too late!
while int(time.time()) <= period3['paywindow_end']:
time.sleep(1)