mirror of
https://github.com/aljazceru/lightning.git
synced 2026-01-03 22:34:21 +01:00
decode: new generic API to decode bolt11 and bolt12.
This is experimental for now, but can eventually deprecated 'decodepay' and even decode other kinds of messages. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
561
plugins/offers.c
561
plugins/offers.c
@@ -1,5 +1,13 @@
|
||||
/* This plugin covers both sending and receiving offers */
|
||||
#include <bitcoin/chainparams.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/bech32.h>
|
||||
#include <common/bolt11.h>
|
||||
#include <common/bolt11_json.h>
|
||||
#include <common/bolt12.h>
|
||||
#include <common/bolt12_merkle.h>
|
||||
#include <common/iso4217.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <plugins/libplugin.h>
|
||||
#include <plugins/offers.h>
|
||||
@@ -111,6 +119,552 @@ static const struct plugin_hook hooks[] = {
|
||||
},
|
||||
};
|
||||
|
||||
struct decodable {
|
||||
const char *type;
|
||||
struct bolt11 *b11;
|
||||
struct tlv_offer *offer;
|
||||
struct tlv_invoice *invoice;
|
||||
struct tlv_invoice_request *invreq;
|
||||
};
|
||||
|
||||
static struct command_result *param_decodable(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *token,
|
||||
struct decodable *decodable)
|
||||
{
|
||||
char *likely_fail = NULL, *fail;
|
||||
jsmntok_t tok;
|
||||
|
||||
/* BOLT #11:
|
||||
*
|
||||
* If a URI scheme is desired, the current recommendation is to either
|
||||
* use 'lightning:' as a prefix before the BOLT-11 encoding
|
||||
*/
|
||||
tok = *token;
|
||||
if (json_tok_startswith(buffer, &tok, "lightning:")
|
||||
|| json_tok_startswith(buffer, &tok, "LIGHTNING:"))
|
||||
tok.start += strlen("lightning:");
|
||||
|
||||
decodable->offer = offer_decode(cmd, buffer + tok.start,
|
||||
tok.end - tok.start,
|
||||
plugin_feature_set(cmd->plugin), NULL,
|
||||
json_tok_startswith(buffer, &tok, "lno1")
|
||||
? &likely_fail : &fail);
|
||||
if (decodable->offer) {
|
||||
decodable->type = "bolt12 offer";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decodable->invoice = invoice_decode(cmd, buffer + tok.start,
|
||||
tok.end - tok.start,
|
||||
plugin_feature_set(cmd->plugin),
|
||||
NULL,
|
||||
json_tok_startswith(buffer, &tok,
|
||||
"lni1")
|
||||
? &likely_fail : &fail);
|
||||
if (decodable->invoice) {
|
||||
decodable->type = "bolt12 invoice";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decodable->invreq = invrequest_decode(cmd, buffer + tok.start,
|
||||
tok.end - tok.start,
|
||||
plugin_feature_set(cmd->plugin),
|
||||
NULL,
|
||||
json_tok_startswith(buffer, &tok,
|
||||
"lnr1")
|
||||
? &likely_fail : &fail);
|
||||
if (decodable->invreq) {
|
||||
decodable->type = "bolt12 invoice_request";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If no other was likely, bolt11 decoder gives us failure string. */
|
||||
decodable->b11 = bolt11_decode(cmd,
|
||||
tal_strndup(tmpctx, buffer + tok.start,
|
||||
tok.end - tok.start),
|
||||
plugin_feature_set(cmd->plugin),
|
||||
NULL, NULL,
|
||||
likely_fail ? &fail : &likely_fail);
|
||||
if (decodable->b11) {
|
||||
decodable->type = "bolt11 invoice";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return failure message from most likely parsing candidate */
|
||||
return command_fail_badparam(cmd, name, buffer, &tok, likely_fail);
|
||||
}
|
||||
|
||||
static void json_add_chains(struct json_stream *js,
|
||||
const struct bitcoin_blkid *chains)
|
||||
{
|
||||
json_array_start(js, "chains");
|
||||
for (size_t i = 0; i < tal_count(chains); i++)
|
||||
json_add_sha256(js, NULL, &chains[i].shad.sha);
|
||||
json_array_end(js);
|
||||
}
|
||||
|
||||
static void json_add_onionmsg_path(struct json_stream *js,
|
||||
const char *fieldname,
|
||||
const struct onionmsg_path *path,
|
||||
const struct blinded_payinfo *payinfo)
|
||||
{
|
||||
json_object_start(js, fieldname);
|
||||
json_add_pubkey(js, "node_id", &path->node_id);
|
||||
json_add_hex_talarr(js, "enctlv", path->enctlv);
|
||||
if (payinfo) {
|
||||
json_add_u32(js, "fee_base_msat", payinfo->fee_base_msat);
|
||||
json_add_u32(js, "fee_proportional_millionths",
|
||||
payinfo->fee_proportional_millionths);
|
||||
json_add_u32(js, "cltv_expiry_delta",
|
||||
payinfo->cltv_expiry_delta);
|
||||
json_add_hex_talarr(js, "features", payinfo->features);
|
||||
}
|
||||
json_object_end(js);
|
||||
}
|
||||
|
||||
static void json_add_blinded_paths(struct json_stream *js,
|
||||
struct blinded_path **paths,
|
||||
struct blinded_payinfo **blindedpay)
|
||||
{
|
||||
size_t n = 0;
|
||||
json_array_start(js, "paths");
|
||||
for (size_t i = 0; i < tal_count(paths); i++) {
|
||||
json_object_start(js, NULL);
|
||||
json_add_pubkey(js, "blinding", &paths[i]->blinding);
|
||||
json_array_start(js, "path");
|
||||
for (size_t j = 0; j < tal_count(paths[i]->path); j++) {
|
||||
json_add_onionmsg_path(js, NULL, paths[i]->path[j],
|
||||
n < tal_count(blindedpay)
|
||||
? blindedpay[n] : NULL);
|
||||
n++;
|
||||
}
|
||||
json_array_end(js);
|
||||
json_object_end(js);
|
||||
}
|
||||
json_array_end(js);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST reject the invoice if `blinded_payinfo` does not contain
|
||||
* exactly as many `payinfo` as total `onionmsg_path` in
|
||||
* `blinded_path`.
|
||||
*/
|
||||
if (blindedpay && n != tal_count(blindedpay))
|
||||
json_add_string(js, "warning_invoice_invalid_blinded_payinfo",
|
||||
"invoice does not have correct number of blinded_payinfo");
|
||||
}
|
||||
|
||||
static const char *recurrence_time_unit_name(u8 time_unit)
|
||||
{
|
||||
/* BOLT-offers #12:
|
||||
* `time_unit` defining 0 (seconds), 1 (days), 2 (months), 3 (years).
|
||||
*/
|
||||
switch (time_unit) {
|
||||
case 0:
|
||||
return "seconds";
|
||||
case 1:
|
||||
return "days";
|
||||
case 2:
|
||||
return "months";
|
||||
case 3:
|
||||
return "years";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer)
|
||||
{
|
||||
struct sha256 offer_id;
|
||||
|
||||
merkle_tlv(offer->fields, &offer_id);
|
||||
json_add_sha256(js, "offer_id", &offer_id);
|
||||
if (offer->chains)
|
||||
json_add_chains(js, offer->chains);
|
||||
if (offer->currency) {
|
||||
const struct iso4217_name_and_divisor *iso4217;
|
||||
json_add_stringn(js, "currency",
|
||||
offer->currency, tal_bytelen(offer->currency));
|
||||
if (offer->amount)
|
||||
json_add_u64(js, "amount", *offer->amount);
|
||||
iso4217 = find_iso4217(offer->currency,
|
||||
tal_bytelen(offer->currency));
|
||||
if (iso4217)
|
||||
json_add_num(js, "minor_unit", iso4217->minor_unit);
|
||||
else
|
||||
json_add_string(js, "warning_offer_unknown_currency",
|
||||
"unknown currency code");
|
||||
} else if (offer->amount)
|
||||
json_add_amount_msat_only(js, "amount_msat",
|
||||
amount_msat(*offer->amount));
|
||||
if (offer->send_invoice)
|
||||
json_add_bool(js, "send_invoice", true);
|
||||
if (offer->refund_for)
|
||||
json_add_sha256(js, "refund_for", offer->refund_for);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* A reader of an offer:
|
||||
*...
|
||||
* - if `node_id`, `description` or `signature` is not set:
|
||||
* - MUST NOT respond to the offer.
|
||||
*/
|
||||
if (offer->description)
|
||||
json_add_stringn(js, "description",
|
||||
offer->description,
|
||||
tal_bytelen(offer->description));
|
||||
else
|
||||
json_add_string(js, "warning_offer_missing_description",
|
||||
"offers without a description are invalid");
|
||||
|
||||
if (offer->vendor)
|
||||
json_add_stringn(js, "vendor", offer->vendor,
|
||||
tal_bytelen(offer->vendor));
|
||||
if (offer->features)
|
||||
json_add_hex_talarr(js, "features", offer->features);
|
||||
if (offer->absolute_expiry)
|
||||
json_add_u64(js, "absolute_expiry",
|
||||
*offer->absolute_expiry);
|
||||
if (offer->paths)
|
||||
json_add_blinded_paths(js, offer->paths, NULL);
|
||||
|
||||
if (offer->quantity_min)
|
||||
json_add_u64(js, "quantity_min", *offer->quantity_min);
|
||||
if (offer->quantity_max)
|
||||
json_add_u64(js, "quantity_max", *offer->quantity_max);
|
||||
if (offer->recurrence) {
|
||||
const char *name;
|
||||
json_object_start(js, "recurrence");
|
||||
json_add_num(js, "time_unit", offer->recurrence->time_unit);
|
||||
name = recurrence_time_unit_name(offer->recurrence->time_unit);
|
||||
if (name)
|
||||
json_add_string(js, "time_unit_name", name);
|
||||
json_add_num(js, "period", offer->recurrence->period);
|
||||
if (offer->recurrence_base) {
|
||||
json_add_u64(js, "basetime",
|
||||
offer->recurrence_base->basetime);
|
||||
if (offer->recurrence_base->start_any_period)
|
||||
json_add_bool(js, "start_any_period", true);
|
||||
}
|
||||
if (offer->recurrence_limit)
|
||||
json_add_u32(js, "limit", *offer->recurrence_limit);
|
||||
if (offer->recurrence_paywindow) {
|
||||
json_object_start(js, "paywindow");
|
||||
json_add_u32(js, "seconds_before",
|
||||
offer->recurrence_paywindow->seconds_before);
|
||||
json_add_u32(js, "seconds_after",
|
||||
offer->recurrence_paywindow->seconds_after);
|
||||
if (offer->recurrence_paywindow->proportional_amount)
|
||||
json_add_bool(js, "proportional_amount", true);
|
||||
json_object_end(js);
|
||||
}
|
||||
json_object_end(js);
|
||||
}
|
||||
|
||||
/* offer_decode fails if node_id or signature not set */
|
||||
json_add_pubkey32(js, "node_id", offer->node_id);
|
||||
json_add_bip340sig(js, "signature", offer->signature);
|
||||
}
|
||||
|
||||
static void json_add_fallback_address(struct json_stream *js,
|
||||
const struct chainparams *chain,
|
||||
u8 version, const u8 *address)
|
||||
{
|
||||
char out[73 + strlen(chain->bip173_name)];
|
||||
|
||||
/* Does extra checks, in particular checks v0 sizes */
|
||||
if (segwit_addr_encode(out, chain->bip173_name, version,
|
||||
address, tal_bytelen(address)))
|
||||
json_add_string(js, "address", out);
|
||||
else
|
||||
json_add_string(js,
|
||||
"warning_invoice_fallbacks_address_invalid",
|
||||
"invalid fallback address for this version");
|
||||
}
|
||||
|
||||
static void json_add_fallbacks(struct json_stream *js,
|
||||
const struct bitcoin_blkid *chains,
|
||||
struct fallback_address **fallbacks)
|
||||
{
|
||||
const struct chainparams *chain;
|
||||
|
||||
/* Present address as first chain mentioned. */
|
||||
if (tal_count(chains) != 0)
|
||||
chain = chainparams_by_chainhash(&chains[0]);
|
||||
else
|
||||
chain = chainparams_for_network("bitcoin");
|
||||
|
||||
json_array_start(js, "fallbacks");
|
||||
for (size_t i = 0; i < tal_count(fallbacks); i++) {
|
||||
size_t addrlen = tal_bytelen(fallbacks[i]->address);
|
||||
|
||||
json_object_start(js, NULL);
|
||||
json_add_u32(js, "version", fallbacks[i]->version);
|
||||
json_add_hex_talarr(js, "hex", fallbacks[i]->address);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - for the bitcoin chain, if the invoice specifies `fallbacks`:
|
||||
* - MUST ignore any `fallback_address` for which `version` is
|
||||
* greater than 16.
|
||||
* - MUST ignore any `fallback_address` for which `address` is
|
||||
* less than 2 or greater than 40 bytes.
|
||||
* - MUST ignore any `fallback_address` for which `address` does
|
||||
* not meet known requirements for the given `version`
|
||||
*/
|
||||
if (fallbacks[i]->version > 16) {
|
||||
json_add_string(js,
|
||||
"warning_invoice_fallbacks_version_invalid",
|
||||
"invoice fallback version > 16");
|
||||
} else if (addrlen < 2 || addrlen > 40) {
|
||||
json_add_string(js,
|
||||
"warning_invoice_fallbacks_address_invalid",
|
||||
"invoice fallback address bad length");
|
||||
} else if (chain) {
|
||||
json_add_fallback_address(js, chain,
|
||||
fallbacks[i]->version,
|
||||
fallbacks[i]->address);
|
||||
}
|
||||
json_object_end(js);
|
||||
}
|
||||
json_array_end(js);
|
||||
}
|
||||
|
||||
static void json_add_b12_invoice(struct json_stream *js,
|
||||
const struct tlv_invoice *invoice)
|
||||
{
|
||||
if (invoice->chains)
|
||||
json_add_chains(js, invoice->chains);
|
||||
if (invoice->offer_id)
|
||||
json_add_sha256(js, "offer_id", invoice->offer_id);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST reject the invoice if `msat` is not present.
|
||||
*/
|
||||
if (invoice->amount)
|
||||
json_add_amount_msat_only(js, "amount_msat",
|
||||
amount_msat(*invoice->amount));
|
||||
else
|
||||
json_add_string(js, "warning_invoice_missing_amount",
|
||||
"invoices without an amount are invalid");
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST reject the invoice if `description` is not present.
|
||||
*/
|
||||
if (invoice->description)
|
||||
json_add_stringn(js, "description", invoice->description,
|
||||
tal_bytelen(invoice->description));
|
||||
else
|
||||
json_add_string(js, "warning_invoice_missing_description",
|
||||
"invoices without a description are invalid");
|
||||
if (invoice->vendor)
|
||||
json_add_stringn(js, "vendor", invoice->vendor,
|
||||
tal_bytelen(invoice->vendor));
|
||||
if (invoice->features)
|
||||
json_add_hex_talarr(js, "features", invoice->features);
|
||||
if (invoice->paths) {
|
||||
/* BOLT-offers #12:
|
||||
* - if `blinded_path` is present:
|
||||
* - MUST reject the invoice if `blinded_payinfo` is not
|
||||
* present.
|
||||
* - MUST reject the invoice if `blinded_payinfo` does not
|
||||
* contain exactly as many `payinfo` as total `onionmsg_path`
|
||||
* in `blinded_path`.
|
||||
*/
|
||||
if (!invoice->blindedpay)
|
||||
json_add_string(js, "warning_invoice_missing_blinded_payinfo",
|
||||
"invoices with blinded_path without blinded_payindo are invalid");
|
||||
json_add_blinded_paths(js, invoice->paths, invoice->blindedpay);
|
||||
}
|
||||
if (invoice->quantity)
|
||||
json_add_u64(js, "quantity", *invoice->quantity);
|
||||
if (invoice->send_invoice)
|
||||
json_add_bool(js, "send_invoice", true);
|
||||
if (invoice->refund_for)
|
||||
json_add_sha256(js, "refund_for", invoice->refund_for);
|
||||
if (invoice->recurrence_counter) {
|
||||
json_add_u32(js, "recurrence_counter",
|
||||
*invoice->recurrence_counter);
|
||||
if (invoice->recurrence_start)
|
||||
json_add_u32(js, "recurrence_start",
|
||||
*invoice->recurrence_start);
|
||||
/* BOLT-offers #12:
|
||||
* - if the offer contained `recurrence`:
|
||||
* - MUST reject the invoice if `recurrence_basetime` is not
|
||||
* set.
|
||||
*/
|
||||
if (invoice->recurrence_basetime)
|
||||
json_add_u64(js, "recurrence_basetime",
|
||||
*invoice->recurrence_basetime);
|
||||
else
|
||||
json_add_string(js, "warning_invoice_missing_recurrence_basetime",
|
||||
"recurring invoices without a recurrence_basetime are invalid");
|
||||
}
|
||||
|
||||
if (invoice->payer_key)
|
||||
json_add_pubkey32(js, "payer_key", invoice->payer_key);
|
||||
if (invoice->payer_info)
|
||||
json_add_hex_talarr(js, "payer_info", invoice->payer_info);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST reject the invoice if `timestamp` is not present.
|
||||
*/
|
||||
if (invoice->timestamp)
|
||||
json_add_u64(js, "timestamp", *invoice->timestamp);
|
||||
else
|
||||
json_add_string(js, "warning_invoice_missing_timestamp",
|
||||
"invoices without a timestamp are invalid");
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST reject the invoice if `payment_hash` is not present.
|
||||
*/
|
||||
if (invoice->payment_hash)
|
||||
json_add_sha256(js, "payment_hash", invoice->payment_hash);
|
||||
else
|
||||
json_add_string(js, "warning_invoice_missing_payment_hash",
|
||||
"invoices without a payment_hash are invalid");
|
||||
|
||||
/* BOLT-offers #12:
|
||||
*
|
||||
* - if the expiry for accepting payment is not 7200 seconds after
|
||||
* `timestamp`:
|
||||
* - MUST set `relative_expiry`
|
||||
*/
|
||||
if (invoice->relative_expiry)
|
||||
json_add_u32(js, "relative_expiry", *invoice->relative_expiry);
|
||||
else
|
||||
json_add_u32(js, "relative_expiry", 7200);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - if the `min_final_cltv_expiry` for the last HTLC in the route is
|
||||
* not 18:
|
||||
* - MUST set `min_final_cltv_expiry`.
|
||||
*/
|
||||
if (invoice->cltv)
|
||||
json_add_u32(js, "min_final_cltv_expiry", *invoice->cltv);
|
||||
else
|
||||
json_add_u32(js, "min_final_cltv_expiry", 18);
|
||||
|
||||
if (invoice->fallbacks)
|
||||
json_add_fallbacks(js, invoice->chains,
|
||||
invoice->fallbacks->fallbacks);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - if the offer contained `refund_for`:
|
||||
* - MUST reject the invoice if `payer_key` does not match the invoice
|
||||
* whose `payment_hash` is equal to `refund_for`
|
||||
* `refunded_payment_hash`
|
||||
* - MUST reject the invoice if `refund_signature` is not set.
|
||||
* - MUST reject the invoice if `refund_signature` is not a valid
|
||||
* signature using `payer_key` as described in
|
||||
* [Signature Calculation](#signature-calculation).
|
||||
*/
|
||||
if (invoice->refund_signature) {
|
||||
json_add_bip340sig(js, "refund_signature",
|
||||
invoice->refund_signature);
|
||||
if (!invoice->payer_key)
|
||||
json_add_string(js, "warning_invoice_refund_signature_missing_payer_key",
|
||||
"Can't have refund_signature without payer key");
|
||||
else if (!bolt12_check_signature(invoice->fields,
|
||||
"invoice",
|
||||
"refund_signature",
|
||||
invoice->payer_key,
|
||||
invoice->refund_signature))
|
||||
json_add_string(js, "warning_invoice_refund_signature_invalid",
|
||||
"refund_signature does not match");
|
||||
} else if (invoice->refund_for)
|
||||
json_add_string(js, "warning_invoice_refund_missing_signature",
|
||||
"refund_for requires refund_signature");
|
||||
|
||||
/* invoice_decode checked these */
|
||||
json_add_pubkey32(js, "node_id", invoice->node_id);
|
||||
json_add_bip340sig(js, "signature", invoice->signature);
|
||||
}
|
||||
|
||||
static void json_add_invoice_request(struct json_stream *js,
|
||||
const struct tlv_invoice_request *invreq)
|
||||
{
|
||||
if (invreq->chains)
|
||||
json_add_chains(js, invreq->chains);
|
||||
/* BOLT-offers #12:
|
||||
* - MUST fail the request if `payer_key` is not present.
|
||||
* - MUST fail the request if `chains` does not include (or imply) a supported chain.
|
||||
* - MUST fail the request if `features` contains unknown even bits.
|
||||
* - MUST fail the request if `offer_id` is not present.
|
||||
*/
|
||||
if (invreq->offer_id)
|
||||
json_add_sha256(js, "offer_id", invreq->offer_id);
|
||||
else
|
||||
json_add_string(js, "warning_invoice_request_missing_offer_id",
|
||||
"invoice_request requires offer_id");
|
||||
if (invreq->amount)
|
||||
json_add_amount_msat_only(js, "amount_msat",
|
||||
amount_msat(*invreq->amount));
|
||||
if (invreq->features)
|
||||
json_add_hex_talarr(js, "features", invreq->features);
|
||||
if (invreq->quantity)
|
||||
json_add_u64(js, "quantity", *invreq->quantity);
|
||||
|
||||
if (invreq->recurrence_counter)
|
||||
json_add_u32(js, "recurrence_counter",
|
||||
*invreq->recurrence_counter);
|
||||
if (invreq->recurrence_start)
|
||||
json_add_u32(js, "recurrence_start",
|
||||
*invreq->recurrence_start);
|
||||
if (invreq->payer_key)
|
||||
json_add_pubkey32(js, "payer_key", invreq->payer_key);
|
||||
else
|
||||
json_add_string(js, "warning_invoice_request_missing_payer_key",
|
||||
"invoice_request requires payer_key");
|
||||
if (invreq->payer_info)
|
||||
json_add_hex_talarr(js, "payer_info", invreq->payer_info);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - if the offer had a `recurrence`:
|
||||
* - MUST fail the request if there is no `recurrence_counter` field.
|
||||
* - MUST fail the request if there is no `recurrence_signature` field.
|
||||
* - MUST fail the request if `recurrence_signature` is not correct.
|
||||
*/
|
||||
if (invreq->recurrence_signature) {
|
||||
json_add_bip340sig(js, "recurrence_signature",
|
||||
invreq->recurrence_signature);
|
||||
if (invreq->payer_key
|
||||
&& !bolt12_check_signature(invreq->fields,
|
||||
"invoice_request",
|
||||
"recurrence_signature",
|
||||
invreq->payer_key,
|
||||
invreq->recurrence_signature))
|
||||
json_add_string(js, "warning_invoice_request_invalid_recurrence_signature",
|
||||
"Bad recurrence_signature");
|
||||
} else if (invreq->recurrence_counter) {
|
||||
json_add_string(js, "warning_invoice_request_missing_recurrence_signature",
|
||||
"invoice_request requires recurrence_signature");
|
||||
}
|
||||
}
|
||||
|
||||
static struct command_result *json_decode(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct decodable *decodable = talz(cmd, struct decodable);
|
||||
struct json_stream *response;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("string", param_decodable, decodable),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
json_add_string(response, "type", decodable->type);
|
||||
if (decodable->offer)
|
||||
json_add_offer(response, decodable->offer);
|
||||
if (decodable->invreq)
|
||||
json_add_invoice_request(response, decodable->invreq);
|
||||
if (decodable->invoice)
|
||||
json_add_b12_invoice(response, decodable->invoice);
|
||||
if (decodable->b11)
|
||||
json_add_bolt11(response, decodable->b11);
|
||||
return command_finished(cmd, response);
|
||||
}
|
||||
|
||||
static void init(struct plugin *p,
|
||||
const char *buf UNUSED,
|
||||
const jsmntok_t *config UNUSED)
|
||||
@@ -144,6 +698,13 @@ static const struct plugin_command commands[] = {
|
||||
"Create an offer to pay invoices of {amount} with {description}, optional {vendor}, internal {label}, {absolute_expiry} and {refund_for}",
|
||||
json_offerout
|
||||
},
|
||||
{
|
||||
"decode",
|
||||
"utility",
|
||||
"Decode {string} message, returning {type} and information.",
|
||||
NULL,
|
||||
json_decode,
|
||||
},
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
||||
Reference in New Issue
Block a user