mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
createinvoice: make a minimal blinded "path" in bolt12 invoice if none presented.
The "path" is just a message to ourselves. This meets the minimal requirement for bolt12 invoices: that there be a blinded path (at least so we can use the path_id inside in place of "payment_secret"). We expose the method to make this path_id to a common routine: offers will need this for generating more sophisticated paths. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
Christian Decker
parent
a5471a405b
commit
595fbd2a19
@@ -47,6 +47,7 @@ COMMON_SRC_NOGEN := \
|
||||
common/interactivetx.c \
|
||||
common/initial_channel.c \
|
||||
common/initial_commit_tx.c \
|
||||
common/invoice_path_id.c \
|
||||
common/iso4217.c \
|
||||
common/json_filter.c \
|
||||
common/json_param.c \
|
||||
|
||||
19
common/invoice_path_id.c
Normal file
19
common/invoice_path_id.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "config.h"
|
||||
#include <bitcoin/privkey.h>
|
||||
#include <ccan/crypto/sha256/sha256.h>
|
||||
#include <common/invoice_path_id.h>
|
||||
|
||||
u8 *invoice_path_id(const tal_t *ctx,
|
||||
const struct secret *base_secret,
|
||||
const struct sha256 *payment_hash)
|
||||
{
|
||||
struct sha256_ctx shactx;
|
||||
struct sha256 secret;
|
||||
|
||||
sha256_init(&shactx);
|
||||
sha256_update(&shactx, base_secret, sizeof(*base_secret));
|
||||
sha256_update(&shactx, payment_hash, sizeof(*payment_hash));
|
||||
sha256_done(&shactx, &secret);
|
||||
|
||||
return (u8 *)tal_dup(ctx, struct sha256, &secret);
|
||||
}
|
||||
29
common/invoice_path_id.h
Normal file
29
common/invoice_path_id.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef LIGHTNING_COMMON_INVOICE_PATH_ID_H
|
||||
#define LIGHTNING_COMMON_INVOICE_PATH_ID_H
|
||||
#include "config.h"
|
||||
#include <ccan/short_types/short_types.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
|
||||
struct secret;
|
||||
struct sha256;
|
||||
|
||||
/* String to use with makesecret to get the invoice base secret */
|
||||
#define INVOICE_PATH_BASE_STRING "bolt12-invoice-base"
|
||||
|
||||
/**
|
||||
* invoice_path_id: generate the "path_id" field for the tlv_encrypted_data_tlv
|
||||
* @ctx: tal context
|
||||
* @payment_hash: the invoice payment hash
|
||||
* @base_secret: the node-specific secret makesecret("bolt12-invoice-base")
|
||||
*
|
||||
* Receiving a blinded, encrypted tlv_encrypted_data_tlv containing
|
||||
* the correct path_id is how we know this blinded path is the correct
|
||||
* one for this invoice payment.
|
||||
*
|
||||
* It's exposed here as plugins may want to generate blinded paths.
|
||||
*/
|
||||
u8 *invoice_path_id(const tal_t *ctx,
|
||||
const struct secret *base_secret,
|
||||
const struct sha256 *payment_hash);
|
||||
|
||||
#endif /* LIGHTNING_COMMON_INVOICE_PATH_ID_H */
|
||||
@@ -99,6 +99,7 @@ LIGHTNINGD_COMMON_OBJS := \
|
||||
common/htlc_state.o \
|
||||
common/htlc_trim.o \
|
||||
common/htlc_wire.o \
|
||||
common/invoice_path_id.o \
|
||||
common/key_derive.o \
|
||||
common/keyset.o \
|
||||
common/json_filter.o \
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <common/errcode.h>
|
||||
#include <common/hsm_encryption.h>
|
||||
#include <common/hsm_version.h>
|
||||
#include <common/invoice_path_id.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_param.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
@@ -140,6 +141,17 @@ struct ext_key *hsm_init(struct lightningd *ld)
|
||||
"HSM gave invalid v1 bolt12_base");
|
||||
}
|
||||
|
||||
/* This is equivalent to makesecret("bolt12-invoice-base") */
|
||||
msg = towire_hsmd_derive_secret(NULL, tal_dup_arr(tmpctx, u8,
|
||||
(const u8 *)INVOICE_PATH_BASE_STRING,
|
||||
strlen(INVOICE_PATH_BASE_STRING), 0));
|
||||
if (!wire_sync_write(ld->hsm_fd, take(msg)))
|
||||
err(EXITCODE_HSM_GENERIC_ERROR, "Writing derive_secret msg to hsm");
|
||||
|
||||
msg = wire_sync_read(tmpctx, ld->hsm_fd);
|
||||
if (!fromwire_hsmd_derive_secret_reply(msg, &ld->invoicesecret_base))
|
||||
err(EXITCODE_HSM_GENERIC_ERROR, "Bad derive_secret_reply");
|
||||
|
||||
return bip32_base;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <ccan/json_escape/json_escape.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/blindedpath.h>
|
||||
#include <common/bolt11_json.h>
|
||||
#include <common/bolt12_merkle.h>
|
||||
#include <common/configdir.h>
|
||||
#include <common/invoice_path_id.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_param.h>
|
||||
#include <common/overflows.h>
|
||||
@@ -1641,6 +1643,69 @@ static struct command_result *fail_exists(struct command *cmd,
|
||||
return command_failed(cmd, data);
|
||||
}
|
||||
|
||||
/* This is only if we're a public node; otherwise, the offers plugin
|
||||
* will have populated a real blinded path */
|
||||
static struct tlv_invoice *add_stub_blindedpath(const tal_t *ctx,
|
||||
struct lightningd *ld,
|
||||
struct tlv_invoice *inv STEALS)
|
||||
{
|
||||
struct blinded_path *path;
|
||||
struct privkey blinding;
|
||||
struct tlv_encrypted_data_tlv *tlv;
|
||||
u8 *wire;
|
||||
size_t dlen;
|
||||
|
||||
path = tal(NULL, struct blinded_path);
|
||||
if (!pubkey_from_node_id(&path->first_node_id, &ld->id))
|
||||
abort();
|
||||
randombytes_buf(&blinding, sizeof(blinding));
|
||||
if (!pubkey_from_privkey(&blinding, &path->blinding))
|
||||
abort();
|
||||
path->path = tal_arr(path, struct onionmsg_hop *, 1);
|
||||
path->path[0] = tal(path->path, struct onionmsg_hop);
|
||||
|
||||
/* A message in a bottle to ourselves: match it with
|
||||
* the invoice: we assume the payment_hash is unique! */
|
||||
tlv = tlv_encrypted_data_tlv_new(tmpctx);
|
||||
tlv->path_id = invoice_path_id(inv,
|
||||
&ld->invoicesecret_base,
|
||||
inv->payment_hash);
|
||||
|
||||
path->path[0]->encrypted_recipient_data
|
||||
= encrypt_tlv_encrypted_data(path->path[0],
|
||||
&blinding,
|
||||
&path->first_node_id,
|
||||
tlv,
|
||||
NULL,
|
||||
&path->path[0]->blinded_node_id);
|
||||
|
||||
inv->paths = tal_arr(inv, struct blinded_path *, 1);
|
||||
inv->paths[0] = tal_steal(inv->paths, path);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST include `invoice_paths` containing one or more paths to the node.
|
||||
* - MUST specify `invoice_paths` in order of most-preferred to least-preferred if it has a preference.
|
||||
* - MUST include `invoice_blindedpay` with exactly one `blinded_payinfo` for each `blinded_path` in `paths`, in order.
|
||||
*/
|
||||
inv->blindedpay = tal_arr(inv, struct blinded_payinfo *, 1);
|
||||
inv->blindedpay[0] = tal(inv->blindedpay, struct blinded_payinfo);
|
||||
inv->blindedpay[0]->fee_base_msat = 0;
|
||||
inv->blindedpay[0]->fee_proportional_millionths = 0;
|
||||
inv->blindedpay[0]->cltv_expiry_delta = ld->config.cltv_final;
|
||||
inv->blindedpay[0]->htlc_minimum_msat = AMOUNT_MSAT(0);
|
||||
inv->blindedpay[0]->htlc_maximum_msat = AMOUNT_MSAT(21000000 * MSAT_PER_BTC);
|
||||
inv->blindedpay[0]->features = NULL;
|
||||
|
||||
/* But we need to update ->fields, so re-linearize */
|
||||
wire = tal_arr(tmpctx, u8, 0);
|
||||
towire_tlv_invoice(&wire, inv);
|
||||
tal_free(inv);
|
||||
|
||||
dlen = tal_bytelen(wire);
|
||||
return fromwire_tlv_invoice(ctx,
|
||||
cast_const2(const u8 **, &wire), &dlen);
|
||||
}
|
||||
|
||||
static struct command_result *json_createinvoice(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
@@ -1717,6 +1782,12 @@ static struct command_result *json_createinvoice(struct command *cmd,
|
||||
"Unparsable invoice '%s': %s",
|
||||
invstring, fail);
|
||||
|
||||
/* If they don't create a blinded path, add a simple one so we
|
||||
* can recognize payments (bolt12 doesn't use
|
||||
* payment_secret) */
|
||||
if (!inv->paths)
|
||||
inv = add_stub_blindedpath(cmd, cmd->ld, inv);
|
||||
|
||||
if (inv->signature)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"invoice already signed");
|
||||
|
||||
@@ -124,6 +124,9 @@ struct lightningd {
|
||||
/* The public base for our payer_id keys */
|
||||
struct pubkey bolt12_base;
|
||||
|
||||
/* Secret base for our invoices */
|
||||
struct secret invoicesecret_base;
|
||||
|
||||
/* Feature set we offer. */
|
||||
struct feature_set *our_features;
|
||||
|
||||
|
||||
@@ -182,6 +182,15 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED,
|
||||
const struct chainparams *chainparams UNNEEDED,
|
||||
const u8 *scriptPubkey UNNEEDED)
|
||||
{ fprintf(stderr, "encode_scriptpubkey_to_addr called!\n"); abort(); }
|
||||
/* Generated stub for encrypt_tlv_encrypted_data */
|
||||
u8 *encrypt_tlv_encrypted_data(const tal_t *ctx UNNEEDED,
|
||||
const struct privkey *blinding UNNEEDED,
|
||||
const struct pubkey *node UNNEEDED,
|
||||
const struct tlv_encrypted_data_tlv *tlv UNNEEDED,
|
||||
struct privkey *next_blinding UNNEEDED,
|
||||
struct pubkey *node_alias)
|
||||
|
||||
{ fprintf(stderr, "encrypt_tlv_encrypted_data called!\n"); abort(); }
|
||||
/* Generated stub for failmsg_incorrect_or_unknown_ */
|
||||
const u8 *failmsg_incorrect_or_unknown_(const tal_t *ctx UNNEEDED,
|
||||
struct lightningd *ld UNNEEDED,
|
||||
@@ -306,6 +315,11 @@ 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_path_id */
|
||||
u8 *invoice_path_id(const tal_t *ctx UNNEEDED,
|
||||
const struct secret *base_secret UNNEEDED,
|
||||
const struct sha256 *payment_hash UNNEEDED)
|
||||
{ fprintf(stderr, "invoice_path_id called!\n"); abort(); }
|
||||
/* Generated stub for json_add_address */
|
||||
void json_add_address(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED,
|
||||
const struct wireaddr *addr UNNEEDED)
|
||||
@@ -687,6 +701,9 @@ bool plugin_hook_call_(struct lightningd *ld UNNEEDED,
|
||||
void plugin_request_send(struct plugin *plugin UNNEEDED,
|
||||
struct jsonrpc_request *req TAKES UNNEEDED)
|
||||
{ fprintf(stderr, "plugin_request_send called!\n"); abort(); }
|
||||
/* Generated stub for pubkey_from_node_id */
|
||||
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)
|
||||
{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); }
|
||||
/* Generated stub for report_subd_memleak */
|
||||
void report_subd_memleak(struct leak_detect *leak_detect UNNEEDED, struct subd *leaker UNNEEDED)
|
||||
{ fprintf(stderr, "report_subd_memleak called!\n"); abort(); }
|
||||
|
||||
@@ -4665,7 +4665,8 @@ def test_fetchinvoice(node_factory, bitcoind):
|
||||
l1.rpc.pay(inv1['invoice'])
|
||||
|
||||
# We can't pay the other one now.
|
||||
with pytest.raises(RpcError, match="INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS.*'erring_node': '{}'".format(l3.info['id'])):
|
||||
# FIXME: Even dummy blinded paths always return WIRE_INVALID_ONION_BLINDING!
|
||||
with pytest.raises(RpcError, match="INVALID_ONION_BLINDING.*'erring_node': '{}'".format(l3.info['id'])):
|
||||
l1.rpc.pay(inv2['invoice'])
|
||||
|
||||
# We can't reuse the offer, either.
|
||||
|
||||
Reference in New Issue
Block a user