mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-01 17:47:30 +01:00
lightningd: add low-level offer interfaces.
The real work is done in a plugin, but provide enough API that we can manipulate the db.
This commit is contained in:
parent
3f4683e3f8
commit
d66dbd473a
3 changed files with 221 additions and 0 deletions
|
@ -76,4 +76,8 @@ static const errcode_t INVOICE_STATUS_UNEXPECTED = 906;
|
|||
/* Errors from HSM crypto operations. */
|
||||
static const errcode_t HSM_ECDH_FAILED = 800;
|
||||
|
||||
/* Errors from `offer` commands */
|
||||
static const errcode_t OFFER_ALREADY_EXISTS = 1000;
|
||||
static const errcode_t OFFER_ALREADY_DISABLED = 1001;
|
||||
|
||||
#endif /* LIGHTNING_COMMON_JSONRPC_ERRORS_H */
|
||||
|
|
|
@ -47,6 +47,10 @@ endif
|
|||
LIGHTNINGD_SRC_NOHDR := \
|
||||
lightningd/signmessage.c
|
||||
|
||||
ifeq ($(EXPERIMENTAL_FEATURES),1)
|
||||
LIGHTNINGD_SRC_NOHDR += lightningd/offer.c
|
||||
endif
|
||||
|
||||
LIGHTNINGD_HEADERS := \
|
||||
$(LIGHTNINGD_SRC:.c=.h) \
|
||||
lightningd/channel_state.h \
|
||||
|
@ -122,6 +126,10 @@ LIGHTNINGD_COMMON_OBJS := \
|
|||
common/wire_error.o \
|
||||
common/wireaddr.o \
|
||||
|
||||
ifeq ($(EXPERIMENTAL_FEATURES),1)
|
||||
LIGHTNINGD_COMMON_OBJS += common/bolt12.o common/bolt12_merkle.o
|
||||
endif
|
||||
|
||||
include wallet/Makefile
|
||||
|
||||
# All together in one convenient var
|
||||
|
|
209
lightningd/offer.c
Normal file
209
lightningd/offer.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
#include <common/bolt12.h>
|
||||
#include <common/bolt12_merkle.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/param.h>
|
||||
#include <hsmd/hsmd_wiregen.h>
|
||||
#include <lightningd/jsonrpc.h>
|
||||
#include <lightningd/lightningd.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wire/wire_sync.h>
|
||||
|
||||
static void json_populate_offer(struct json_stream *response,
|
||||
const struct sha256 *offer_id,
|
||||
const char *b12,
|
||||
const struct json_escape *label,
|
||||
enum offer_status status)
|
||||
{
|
||||
json_add_sha256(response, "offer_id", offer_id);
|
||||
json_add_bool(response, "active", offer_status_active(status));
|
||||
json_add_bool(response, "single_use", offer_status_single(status));
|
||||
json_add_string(response, "bolt12", b12);
|
||||
if (status == OFFER_USED)
|
||||
json_add_bool(response, "used", true);
|
||||
if (label)
|
||||
json_add_escaped_string(response, "label", label);
|
||||
}
|
||||
|
||||
static struct command_result *param_b12_offer(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
struct tlv_offer **offer)
|
||||
{
|
||||
char *fail;
|
||||
*offer = offer_decode_nosig(cmd, buffer + tok->start,
|
||||
tok->end - tok->start,
|
||||
cmd->ld->our_features, chainparams, &fail);
|
||||
if (!*offer)
|
||||
return command_fail_badparam(cmd, name, buffer, tok, fail);
|
||||
if ((*offer)->signature)
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"must be unsigned offer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hsm_sign_b12_offer(struct lightningd *ld,
|
||||
const struct sha256 *merkle,
|
||||
struct bip340sig *sig)
|
||||
{
|
||||
u8 *msg;
|
||||
|
||||
msg = towire_hsmd_sign_bolt12(NULL, "offer", "signature", merkle);
|
||||
|
||||
if (!wire_sync_write(ld->hsm_fd, take(msg)))
|
||||
fatal("Could not write to HSM: %s", strerror(errno));
|
||||
|
||||
msg = wire_sync_read(tmpctx, ld->hsm_fd);
|
||||
if (!fromwire_hsmd_sign_bolt12_reply(msg, sig))
|
||||
fatal("HSM gave bad sign_offer_reply %s",
|
||||
tal_hex(msg, msg));
|
||||
}
|
||||
|
||||
static struct command_result *json_createoffer(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct json_stream *response;
|
||||
struct json_escape *label;
|
||||
struct tlv_offer *offer;
|
||||
struct sha256 merkle;
|
||||
const char *b12str;
|
||||
bool *single_use;
|
||||
enum offer_status status;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("bolt12", param_b12_offer, &offer),
|
||||
p_opt("label", param_label, &label),
|
||||
p_opt_def("single_use", param_bool, &single_use, false),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
if (*single_use)
|
||||
status = OFFER_SINGLE_USE;
|
||||
else
|
||||
status = OFFER_MULTIPLE_USE;
|
||||
merkle_tlv(offer->fields, &merkle);
|
||||
offer->signature = tal(offer, struct bip340sig);
|
||||
hsm_sign_b12_offer(cmd->ld, &merkle, offer->signature);
|
||||
b12str = offer_encode(cmd, offer);
|
||||
if (!wallet_offer_create(cmd->ld->wallet, &merkle, b12str, label,
|
||||
status)) {
|
||||
return command_fail(cmd,
|
||||
OFFER_ALREADY_EXISTS,
|
||||
"Duplicate offer");
|
||||
}
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
json_populate_offer(response, &merkle, b12str, label, status);
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
static const struct json_command createoffer_command = {
|
||||
"createoffer",
|
||||
"payment",
|
||||
json_createoffer,
|
||||
"Create and sign an offer {bolt12} with and optional {label}."
|
||||
};
|
||||
AUTODATA(json_command, &createoffer_command);
|
||||
|
||||
static struct command_result *json_listoffers(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct sha256 *offer_id;
|
||||
struct json_stream *response;
|
||||
struct wallet *wallet = cmd->ld->wallet;
|
||||
const char *b12;
|
||||
const struct json_escape *label;
|
||||
bool *active_only;
|
||||
enum offer_status status;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_opt("offer_id", param_sha256, &offer_id),
|
||||
p_opt_def("active_only", param_bool, &active_only, false),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
json_array_start(response, "offers");
|
||||
if (offer_id) {
|
||||
b12 = wallet_offer_find(tmpctx, wallet, offer_id, &label,
|
||||
&status);
|
||||
if (b12 && offer_status_active(status) >= *active_only) {
|
||||
json_object_start(response, NULL);
|
||||
json_populate_offer(response,
|
||||
offer_id, b12, label, status);
|
||||
json_object_end(response);
|
||||
}
|
||||
} else {
|
||||
struct db_stmt *stmt;
|
||||
struct sha256 id;
|
||||
|
||||
for (stmt = wallet_offer_first(cmd->ld->wallet, &id);
|
||||
stmt;
|
||||
stmt = wallet_offer_next(cmd->ld->wallet, stmt, &id)) {
|
||||
b12 = wallet_offer_find(tmpctx, wallet, &id,
|
||||
&label, &status);
|
||||
if (offer_status_active(status) >= *active_only) {
|
||||
json_object_start(response, NULL);
|
||||
json_populate_offer(response,
|
||||
&id, b12, label, status);
|
||||
json_object_end(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
json_array_end(response);
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
static const struct json_command listoffers_command = {
|
||||
"listoffers",
|
||||
"payment",
|
||||
json_listoffers,
|
||||
"If {offer_id} is set, show that."
|
||||
" Otherwise, if {showdisabled} is true, list all, otherwise just non-disabled ones."
|
||||
};
|
||||
AUTODATA(json_command, &listoffers_command);
|
||||
|
||||
static struct command_result *json_disableoffer(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct json_stream *response;
|
||||
struct sha256 *offer_id;
|
||||
struct wallet *wallet = cmd->ld->wallet;
|
||||
const char *b12;
|
||||
const struct json_escape *label;
|
||||
enum offer_status status;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("offer_id", param_sha256, &offer_id),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
b12 = wallet_offer_find(tmpctx, wallet, offer_id, &label, &status);
|
||||
if (!b12)
|
||||
return command_fail(cmd, LIGHTNINGD, "Unknown offer");
|
||||
|
||||
if (!offer_status_active(status))
|
||||
return command_fail(cmd, OFFER_ALREADY_DISABLED,
|
||||
"offer is not active");
|
||||
status = wallet_offer_disable(wallet, offer_id, status);
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
json_populate_offer(response, offer_id, b12, label, status);
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
static const struct json_command disableoffer_command = {
|
||||
"disableoffer",
|
||||
"payment",
|
||||
json_disableoffer,
|
||||
"Disable offer {offer_id}",
|
||||
};
|
||||
AUTODATA(json_command, &disableoffer_command);
|
Loading…
Add table
Reference in a new issue