invoice: hack in merkle of invoice as "payment_secret" (EXPERIMENTAL_FEATURES)

This lets actually pay the invoice that fetchinvoice returns.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2020-12-16 13:48:42 +10:30
committed by Christian Decker
parent 2baa410e2b
commit 19af1d10e6
3 changed files with 70 additions and 0 deletions

View File

@@ -135,6 +135,31 @@ static void invoice_secret(const struct preimage *payment_preimage,
memcpy(payment_secret->data, secret.u.u8, sizeof(secret.u.u8));
}
#if EXPERIMENTAL_FEATURES
/* FIXME: This is a hack. The real secret should be a signature of some
* onion key, using the payer_id */
static void invoice_secret_bolt12(struct lightningd *ld,
const char *invstring,
struct secret *payment_secret)
{
char *fail;
struct tlv_invoice *inv;
struct sha256 merkle;
inv = invoice_decode(tmpctx, invstring, strlen(invstring),
NULL, NULL, &fail);
if (!inv) {
log_broken(ld->log, "Unable to decode our invoice %s",
invstring);
return;
}
merkle_tlv(inv->fields, &merkle);
BUILD_ASSERT(sizeof(*payment_secret) == sizeof(merkle));
memcpy(payment_secret, &merkle, sizeof(merkle));
}
#endif /* EXPERIMENTAL_FEATURES */
struct invoice_payment_hook_payload {
struct lightningd *ld;
/* Set to NULL if it is deleted while waiting for plugin */
@@ -348,6 +373,11 @@ invoice_check_payment(const tal_t *ctx,
if (payment_secret) {
struct secret expected;
#if EXPERIMENTAL_FEATURES
if (details->invstring && strstarts(details->invstring, "lni1"))
invoice_secret_bolt12(ld, details->invstring, &expected);
else
#endif /* EXPERIMENTAL_FEATURES */
invoice_secret(&details->r, &expected);
if (!secret_eq_consttime(payment_secret, &expected)) {
log_debug(ld->log, "Attept to pay %s with wrong secret",

View File

@@ -760,6 +760,13 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx UNNEEDED,
/* Generated stub for invoice_encode */
char *invoice_encode(const tal_t *ctx UNNEEDED, const struct tlv_invoice *bolt12_tlv UNNEEDED)
{ fprintf(stderr, "invoice_encode called!\n"); abort(); }
/* Generated stub for invoice_decode */
struct tlv_invoice *invoice_decode(const tal_t *ctx UNNEEDED,
const char *b12 UNNEEDED, size_t b12len UNNEEDED,
const struct feature_set *our_features UNNEEDED,
const struct chainparams *must_be_chain UNNEEDED,
char **fail UNNEEDED)
{ fprintf(stderr, "invoice_decode called!\n"); abort(); }
#endif
static void add_candidate(struct routehint_candidate **candidates, int n,

View File

@@ -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
l1.rpc.pay(inv1['invoice'])
l1.rpc.pay(inv2['invoice'])
# Single-use invoice can be fetched multiple times, only paid once.
offer = l3.rpc.call('offer', {'amount': '1msat',
@@ -3866,3 +3868,34 @@ def test_fetchinvoice(node_factory, bitcoind):
inv1 = l1.rpc.call('fetchinvoice', {'offer': offer})
inv2 = l1.rpc.call('fetchinvoice', {'offer': offer})
assert inv1 != inv2
l1.rpc.pay(inv1['invoice'])
# FIXME: We don't report failure yet.
# # We can't pay the other one now.
# with pytest.raises(RpcError, match='???'):
# l1.rpc.pay(inv2['invoice'])
#
# # We can't reuse the offer, either.
# with pytest.raises(RpcError, match='???'):
# l1.rpc.call('fetchinvoice', {'offer': offer})
# Recurring offer.
offer = l2.rpc.call('offer', {'amount': '1msat',
'description': 'recurring test',
'recurrence': '1minutes'})['bolt12']
print(offer)
ret = l1.rpc.call('fetchinvoice', {'offer': offer,
'recurrence_counter': 0,
'recurrence_label': 'test recurrence'})
print(ret)
l1.rpc.pay(ret['invoice'], label='test recurrence')
ret = l1.rpc.call('fetchinvoice', {'offer': offer,
'recurrence_counter': 1,
'recurrence_label': 'test recurrence'})
print(ret)
l1.rpc.pay(ret['invoice'], label='test recurrence')