Browse Source
The real work is done in a plugin, but provide enough API that we can manipulate the db.ppa
Rusty Russell
4 years ago
committed by
Christian Decker
3 changed files with 221 additions and 0 deletions
@ -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…
Reference in new issue