mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-23 06:55:13 +01:00
common/bolt12: encode/decode for bolt12 offer, invoice_request and invoice
Note the collapse of '+\s*' and the test cases from the spec. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
d3ef871833
commit
4d086e9939
4 changed files with 610 additions and 0 deletions
|
@ -9,6 +9,7 @@ COMMON_SRC_NOGEN := \
|
||||||
common/bip32.c \
|
common/bip32.c \
|
||||||
common/blinding.c \
|
common/blinding.c \
|
||||||
common/bolt11.c \
|
common/bolt11.c \
|
||||||
|
common/bolt12.c \
|
||||||
common/channel_config.c \
|
common/channel_config.c \
|
||||||
common/channel_id.c \
|
common/channel_id.c \
|
||||||
common/coin_mvt.c \
|
common/coin_mvt.c \
|
||||||
|
@ -87,6 +88,7 @@ endif
|
||||||
COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c
|
COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c
|
||||||
|
|
||||||
ifeq ($(EXPERIMENTAL_FEATURES),1)
|
ifeq ($(EXPERIMENTAL_FEATURES),1)
|
||||||
|
COMMON_SRC_NOGEN += common/bolt12.c
|
||||||
COMMON_SRC_NOGEN += common/bolt12_merkle.c
|
COMMON_SRC_NOGEN += common/bolt12_merkle.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
299
common/bolt12.c
Normal file
299
common/bolt12.c
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
#include <bitcoin/block.h>
|
||||||
|
#include <bitcoin/chainparams.h>
|
||||||
|
#include <ccan/cast/cast.h>
|
||||||
|
#include <ccan/crypto/sha256/sha256.h>
|
||||||
|
#include <ccan/mem/mem.h>
|
||||||
|
#include <common/bech32.h>
|
||||||
|
#include <common/bech32_util.h>
|
||||||
|
#include <common/bolt12.h>
|
||||||
|
#include <common/bolt12_merkle.h>
|
||||||
|
#include <common/features.h>
|
||||||
|
#include <secp256k1_schnorrsig.h>
|
||||||
|
|
||||||
|
bool bolt12_chains_match(const struct bitcoin_blkid *chains,
|
||||||
|
const struct chainparams *must_be_chain)
|
||||||
|
{
|
||||||
|
size_t num_chains;
|
||||||
|
|
||||||
|
/* BOLT-offers #12:
|
||||||
|
* - if the chain for the invoice is not solely bitcoin:
|
||||||
|
* - MUST specify `chains` the offer is valid for.
|
||||||
|
* - otherwise:
|
||||||
|
* - the bitcoin chain is implied as the first and only entry.
|
||||||
|
*/
|
||||||
|
/* BOLT-offers #12:
|
||||||
|
* The reader of an invoice_request:
|
||||||
|
*...
|
||||||
|
* - MUST fail the request if `chains` does not include (or
|
||||||
|
* imply) a supported chain.
|
||||||
|
*/
|
||||||
|
/* BOLT-offers #12:
|
||||||
|
*
|
||||||
|
* - if the chain for the invoice is not solely bitcoin:
|
||||||
|
* - MUST specify `chains` the invoice is valid for.
|
||||||
|
* - otherwise:
|
||||||
|
* - the bitcoin chain is implied as the first and only entry.
|
||||||
|
*/
|
||||||
|
num_chains = tal_count(chains);
|
||||||
|
if (num_chains == 0) {
|
||||||
|
num_chains = 1;
|
||||||
|
chains = &chainparams_for_network("bitcoin")->genesis_blockhash;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_chains; i++) {
|
||||||
|
if (bitcoin_blkid_eq(&chains[i],
|
||||||
|
&must_be_chain->genesis_blockhash))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *check_features_and_chain(const tal_t *ctx,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
const u8 *features,
|
||||||
|
const struct bitcoin_blkid *chains)
|
||||||
|
{
|
||||||
|
if (must_be_chain) {
|
||||||
|
if (!bolt12_chains_match(chains, must_be_chain))
|
||||||
|
return tal_fmt(ctx, "wrong chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (our_features) {
|
||||||
|
int badf = features_unsupported(our_features, features,
|
||||||
|
BOLT11_FEATURE);
|
||||||
|
if (badf != -1)
|
||||||
|
return tal_fmt(ctx, "unknown feature bit %i", badf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *check_signature(const tal_t *ctx,
|
||||||
|
const struct tlv_field *fields,
|
||||||
|
const char *messagename,
|
||||||
|
const char *fieldname,
|
||||||
|
const struct pubkey32 *node_id,
|
||||||
|
const struct bip340sig *sig)
|
||||||
|
{
|
||||||
|
struct sha256 m, shash;
|
||||||
|
|
||||||
|
if (!node_id)
|
||||||
|
return tal_fmt(ctx, "Missing node_id");
|
||||||
|
if (!sig)
|
||||||
|
return tal_fmt(ctx, "Missing signature");
|
||||||
|
|
||||||
|
merkle_tlv(fields, &m);
|
||||||
|
sighash_from_merkle(messagename, fieldname, &m, &shash);
|
||||||
|
if (secp256k1_schnorrsig_verify(secp256k1_ctx,
|
||||||
|
sig->u8,
|
||||||
|
shash.u.u8,
|
||||||
|
&node_id->pubkey) != 1)
|
||||||
|
return tal_fmt(ctx, "Invalid signature");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const u8 *string_to_data(const tal_t *ctx,
|
||||||
|
const char *str,
|
||||||
|
size_t str_len,
|
||||||
|
const char *hrp_expected,
|
||||||
|
size_t *dlen,
|
||||||
|
char **fail)
|
||||||
|
{
|
||||||
|
char *hrp;
|
||||||
|
u8 *data;
|
||||||
|
char *bech32;
|
||||||
|
size_t bech32_len;
|
||||||
|
bool have_plus = false;
|
||||||
|
|
||||||
|
/* First we collapse +\s*, except at start/end. */
|
||||||
|
bech32 = tal_arr(tmpctx, char, str_len);
|
||||||
|
bech32_len = 0;
|
||||||
|
for (size_t i = 0; i < str_len; i++) {
|
||||||
|
if (i != 0 && i+1 != str_len && !have_plus && str[i] == '+') {
|
||||||
|
have_plus = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (have_plus && cisspace(str[i]))
|
||||||
|
continue;
|
||||||
|
have_plus = false;
|
||||||
|
bech32[bech32_len++] = str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_plus) {
|
||||||
|
*fail = tal_fmt(ctx, "unfinished string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!from_bech32_charset(ctx, bech32, bech32_len, &hrp, &data)) {
|
||||||
|
*fail = tal_fmt(ctx, "invalid bech32 string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!streq(hrp, hrp_expected)) {
|
||||||
|
*fail = tal_fmt(ctx, "unexpected prefix %s", hrp);
|
||||||
|
data = tal_free(data);
|
||||||
|
} else
|
||||||
|
*dlen = tal_bytelen(data);
|
||||||
|
|
||||||
|
tal_free(hrp);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *offer_encode(const tal_t *ctx, const struct tlv_offer *offer_tlv)
|
||||||
|
{
|
||||||
|
u8 *wire;
|
||||||
|
|
||||||
|
wire = tal_arr(tmpctx, u8, 0);
|
||||||
|
towire_offer(&wire, offer_tlv);
|
||||||
|
|
||||||
|
return to_bech32_charset(ctx, "lno", wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tlv_offer *offer_decode(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail)
|
||||||
|
{
|
||||||
|
struct tlv_offer *offer;
|
||||||
|
|
||||||
|
offer = offer_decode_nosig(ctx, b12, b12len,
|
||||||
|
our_features, must_be_chain, fail);
|
||||||
|
|
||||||
|
if (offer) {
|
||||||
|
*fail = check_signature(ctx, offer->fields,
|
||||||
|
"offer", "signature",
|
||||||
|
offer->node_id, offer->signature);
|
||||||
|
if (*fail)
|
||||||
|
offer = tal_free(offer);
|
||||||
|
}
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tlv_offer *offer_decode_nosig(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail)
|
||||||
|
{
|
||||||
|
struct tlv_offer *offer = tlv_offer_new(ctx);
|
||||||
|
const u8 *data;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
data = string_to_data(tmpctx, b12, b12len, "lno", &dlen, fail);
|
||||||
|
if (!data)
|
||||||
|
return tal_free(offer);
|
||||||
|
|
||||||
|
if (!fromwire_offer(&data, &dlen, offer)) {
|
||||||
|
*fail = tal_fmt(ctx, "invalid offer data");
|
||||||
|
return tal_free(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
*fail = check_features_and_chain(ctx,
|
||||||
|
our_features, must_be_chain,
|
||||||
|
offer->features,
|
||||||
|
offer->chains);
|
||||||
|
if (*fail)
|
||||||
|
return tal_free(offer);
|
||||||
|
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *invrequest_encode(const tal_t *ctx, const struct tlv_invoice_request *invrequest_tlv)
|
||||||
|
{
|
||||||
|
u8 *wire;
|
||||||
|
|
||||||
|
wire = tal_arr(tmpctx, u8, 0);
|
||||||
|
towire_invoice_request(&wire, invrequest_tlv);
|
||||||
|
|
||||||
|
return to_bech32_charset(ctx, "lnr", wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tlv_invoice_request *invrequest_decode(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail)
|
||||||
|
{
|
||||||
|
struct tlv_invoice_request *invrequest = tlv_invoice_request_new(ctx);
|
||||||
|
const u8 *data;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
data = string_to_data(tmpctx, b12, b12len, "lnr", &dlen, fail);
|
||||||
|
if (!data)
|
||||||
|
return tal_free(invrequest);
|
||||||
|
|
||||||
|
if (!fromwire_invoice_request(&data, &dlen, invrequest)) {
|
||||||
|
*fail = tal_fmt(ctx, "invalid invoice_request data");
|
||||||
|
return tal_free(invrequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
*fail = check_features_and_chain(ctx,
|
||||||
|
our_features, must_be_chain,
|
||||||
|
invrequest->features,
|
||||||
|
invrequest->chains);
|
||||||
|
if (*fail)
|
||||||
|
return tal_free(invrequest);
|
||||||
|
|
||||||
|
return invrequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *invoice_encode(const tal_t *ctx, const struct tlv_invoice *invoice_tlv)
|
||||||
|
{
|
||||||
|
u8 *wire;
|
||||||
|
|
||||||
|
wire = tal_arr(tmpctx, u8, 0);
|
||||||
|
towire_invoice(&wire, invoice_tlv);
|
||||||
|
|
||||||
|
return to_bech32_charset(ctx, "lni", wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail)
|
||||||
|
{
|
||||||
|
struct tlv_invoice *invoice = tlv_invoice_new(ctx);
|
||||||
|
const u8 *data;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
data = string_to_data(tmpctx, b12, b12len, "lni", &dlen, fail);
|
||||||
|
if (!data)
|
||||||
|
return tal_free(invoice);
|
||||||
|
|
||||||
|
if (!fromwire_invoice(&data, &dlen, invoice)) {
|
||||||
|
*fail = tal_fmt(ctx, "invalid invoice data");
|
||||||
|
return tal_free(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
*fail = check_features_and_chain(ctx,
|
||||||
|
our_features, must_be_chain,
|
||||||
|
invoice->features,
|
||||||
|
invoice->chains);
|
||||||
|
if (*fail)
|
||||||
|
return tal_free(invoice);
|
||||||
|
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tlv_invoice *invoice_decode(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail)
|
||||||
|
{
|
||||||
|
struct tlv_invoice *invoice;
|
||||||
|
|
||||||
|
invoice = invoice_decode_nosig(ctx, b12, b12len, our_features,
|
||||||
|
must_be_chain, fail);
|
||||||
|
if (invoice) {
|
||||||
|
*fail = check_signature(ctx, invoice->fields,
|
||||||
|
"invoice", "signature",
|
||||||
|
invoice->node_id, invoice->signature);
|
||||||
|
if (*fail)
|
||||||
|
invoice = tal_free(invoice);
|
||||||
|
}
|
||||||
|
return invoice;
|
||||||
|
}
|
92
common/bolt12.h
Normal file
92
common/bolt12.h
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef LIGHTNING_COMMON_BOLT12_H
|
||||||
|
#define LIGHTNING_COMMON_BOLT12_H
|
||||||
|
#include "config.h"
|
||||||
|
#include <wire/bolt12_exp_wiregen.h>
|
||||||
|
|
||||||
|
struct feature_set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* offer_encode - encode this complete bolt12 offer TLV into text.
|
||||||
|
*/
|
||||||
|
char *offer_encode(const tal_t *ctx, const struct tlv_offer *bolt12_tlv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* offer_decode - decode this complete bolt12 text into a TLV.
|
||||||
|
* @ctx: the context to allocate return or *@fail off.
|
||||||
|
* @b12: the offer string
|
||||||
|
* @b12len: the offer string length
|
||||||
|
* @our_features: if non-NULL, feature set to check against.
|
||||||
|
* @must_be_chain: if non-NULL, chain to enforce.
|
||||||
|
* @fail: pointer to descriptive error string, set if this returns NULL.
|
||||||
|
*
|
||||||
|
* Note: checks signature!
|
||||||
|
*/
|
||||||
|
struct tlv_offer *offer_decode(const tal_t *ctx, const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail);
|
||||||
|
|
||||||
|
/* Variant which does not check signature */
|
||||||
|
struct tlv_offer *offer_decode_nosig(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invrequest_encode - encode this complete bolt12 invreq TLV into text.
|
||||||
|
*/
|
||||||
|
char *invrequest_encode(const tal_t *ctx,
|
||||||
|
const struct tlv_invoice_request *bolt12_tlv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invrequest_decode - decode this complete bolt12 text into a TLV.
|
||||||
|
* @ctx: the context to allocate return or *@fail off.
|
||||||
|
* @b12: the invoice_request string
|
||||||
|
* @b12len: the invoice_request string length
|
||||||
|
* @our_features: if non-NULL, feature set to check against.
|
||||||
|
* @must_be_chain: if non-NULL, chain to enforce.
|
||||||
|
* @fail: pointer to descriptive error string, set if this returns NULL.
|
||||||
|
*
|
||||||
|
* Note: invoice_request doesn't always have a signature, so no checking is done!
|
||||||
|
*/
|
||||||
|
struct tlv_invoice_request *invrequest_decode(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoice_encode - encode this complete bolt12 invoice TLV into text.
|
||||||
|
*/
|
||||||
|
char *invoice_encode(const tal_t *ctx, const struct tlv_invoice *bolt12_tlv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoice_decode - decode this complete bolt12 text into a TLV.
|
||||||
|
* @ctx: the context to allocate return or *@fail off.
|
||||||
|
* @b12: the invoice string
|
||||||
|
* @b12len: the invoice string length
|
||||||
|
* @our_features: if non-NULL, feature set to check against.
|
||||||
|
* @must_be_chain: if non-NULL, chain to enforce.
|
||||||
|
* @fail: pointer to descriptive error string, set if this returns NULL.
|
||||||
|
*
|
||||||
|
* Note: checks signature!
|
||||||
|
*/
|
||||||
|
struct tlv_invoice *invoice_decode(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail);
|
||||||
|
|
||||||
|
/* Variant which does not check signature */
|
||||||
|
struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx,
|
||||||
|
const char *b12, size_t b12len,
|
||||||
|
const struct feature_set *our_features,
|
||||||
|
const struct chainparams *must_be_chain,
|
||||||
|
char **fail);
|
||||||
|
|
||||||
|
/* Given a tal_arr of chains, does it contain this chain? */
|
||||||
|
bool bolt12_chains_match(const struct bitcoin_blkid *chains,
|
||||||
|
const struct chainparams *must_be_chain);
|
||||||
|
|
||||||
|
#endif /* LIGHTNING_COMMON_BOLT12_H */
|
217
common/test/exp-run-bolt12_decode.c
Normal file
217
common/test/exp-run-bolt12_decode.c
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
#include "config.h"
|
||||||
|
#include "../bolt12.c"
|
||||||
|
#include "../bech32_util.c"
|
||||||
|
#include "../bech32.c"
|
||||||
|
#include "../json.c"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ccan/array_size/array_size.h>
|
||||||
|
#include <ccan/tal/grab_file/grab_file.h>
|
||||||
|
#include <ccan/tal/path/path.h>
|
||||||
|
|
||||||
|
/* AUTOGENERATED MOCKS START */
|
||||||
|
/* Generated stub for amount_asset_is_main */
|
||||||
|
bool amount_asset_is_main(struct amount_asset *asset UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_asset_to_sat */
|
||||||
|
struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_sat */
|
||||||
|
struct amount_sat amount_sat(u64 satoshis UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_sat called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_sat_add */
|
||||||
|
bool amount_sat_add(struct amount_sat *val UNNEEDED,
|
||||||
|
struct amount_sat a UNNEEDED,
|
||||||
|
struct amount_sat b UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_sat_add called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_sat_eq */
|
||||||
|
bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_sat_greater_eq */
|
||||||
|
bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_sat_sub */
|
||||||
|
bool amount_sat_sub(struct amount_sat *val UNNEEDED,
|
||||||
|
struct amount_sat a UNNEEDED,
|
||||||
|
struct amount_sat b UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_sat_to_asset */
|
||||||
|
struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); }
|
||||||
|
/* Generated stub for amount_tx_fee */
|
||||||
|
struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED)
|
||||||
|
{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); }
|
||||||
|
/* Generated stub for features_unsupported */
|
||||||
|
int features_unsupported(const struct feature_set *our_features UNNEEDED,
|
||||||
|
const u8 *their_features UNNEEDED,
|
||||||
|
enum feature_place p UNNEEDED)
|
||||||
|
{ fprintf(stderr, "features_unsupported called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire */
|
||||||
|
const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_amount_sat */
|
||||||
|
struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_bool */
|
||||||
|
bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_bool called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_fail */
|
||||||
|
void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_fail called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_invoice */
|
||||||
|
bool fromwire_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
|
||||||
|
struct tlv_invoice * record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_invoice called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_invoice_request */
|
||||||
|
bool fromwire_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
|
||||||
|
struct tlv_invoice_request * record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_invoice_request called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_offer */
|
||||||
|
bool fromwire_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
|
||||||
|
struct tlv_offer * record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_offer called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_secp256k1_ecdsa_signature */
|
||||||
|
void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
|
||||||
|
secp256k1_ecdsa_signature *signature UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_sha256 */
|
||||||
|
void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_tal_arrn */
|
||||||
|
u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED,
|
||||||
|
const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_u16 */
|
||||||
|
u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_u32 */
|
||||||
|
u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_u64 */
|
||||||
|
u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_u8 */
|
||||||
|
u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
|
||||||
|
/* Generated stub for fromwire_u8_array */
|
||||||
|
void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED)
|
||||||
|
{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); }
|
||||||
|
/* Generated stub for json_add_member */
|
||||||
|
void json_add_member(struct json_stream *js UNNEEDED,
|
||||||
|
const char *fieldname UNNEEDED,
|
||||||
|
bool quote UNNEEDED,
|
||||||
|
const char *fmt UNNEEDED, ...)
|
||||||
|
{ fprintf(stderr, "json_add_member called!\n"); abort(); }
|
||||||
|
/* Generated stub for json_member_direct */
|
||||||
|
char *json_member_direct(struct json_stream *js UNNEEDED,
|
||||||
|
const char *fieldname UNNEEDED, size_t extra UNNEEDED)
|
||||||
|
{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
|
||||||
|
/* Generated stub for merkle_tlv */
|
||||||
|
void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED)
|
||||||
|
{ fprintf(stderr, "merkle_tlv called!\n"); abort(); }
|
||||||
|
/* Generated stub for sighash_from_merkle */
|
||||||
|
void sighash_from_merkle(const char *messagename UNNEEDED,
|
||||||
|
const char *fieldname UNNEEDED,
|
||||||
|
const struct sha256 *merkle UNNEEDED,
|
||||||
|
struct sha256 *sighash UNNEEDED)
|
||||||
|
{ fprintf(stderr, "sighash_from_merkle called!\n"); abort(); }
|
||||||
|
/* Generated stub for tlv_invoice_new */
|
||||||
|
struct tlv_invoice *tlv_invoice_new(const tal_t *ctx UNNEEDED)
|
||||||
|
{ fprintf(stderr, "tlv_invoice_new called!\n"); abort(); }
|
||||||
|
/* Generated stub for tlv_invoice_request_new */
|
||||||
|
struct tlv_invoice_request *tlv_invoice_request_new(const tal_t *ctx UNNEEDED)
|
||||||
|
{ fprintf(stderr, "tlv_invoice_request_new called!\n"); abort(); }
|
||||||
|
/* Generated stub for tlv_offer_new */
|
||||||
|
struct tlv_offer *tlv_offer_new(const tal_t *ctx UNNEEDED)
|
||||||
|
{ fprintf(stderr, "tlv_offer_new called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire */
|
||||||
|
void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_amount_sat */
|
||||||
|
void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_bool */
|
||||||
|
void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_bool called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_invoice */
|
||||||
|
void towire_invoice(u8 **pptr UNNEEDED, const struct tlv_invoice *record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_invoice called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_invoice_request */
|
||||||
|
void towire_invoice_request(u8 **pptr UNNEEDED, const struct tlv_invoice_request *record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_invoice_request called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_offer */
|
||||||
|
void towire_offer(u8 **pptr UNNEEDED, const struct tlv_offer *record UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_offer called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_secp256k1_ecdsa_signature */
|
||||||
|
void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED,
|
||||||
|
const secp256k1_ecdsa_signature *signature UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_sha256 */
|
||||||
|
void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_sha256 called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_u16 */
|
||||||
|
void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_u16 called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_u32 */
|
||||||
|
void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_u32 called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_u64 */
|
||||||
|
void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_u64 called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_u8 */
|
||||||
|
void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_u8 called!\n"); abort(); }
|
||||||
|
/* Generated stub for towire_u8_array */
|
||||||
|
void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED)
|
||||||
|
{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
|
||||||
|
/* AUTOGENERATED MOCKS END */
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *json;
|
||||||
|
size_t i;
|
||||||
|
jsmn_parser parser;
|
||||||
|
jsmntok_t toks[5000];
|
||||||
|
const jsmntok_t *t;
|
||||||
|
|
||||||
|
setup_locale();
|
||||||
|
setup_tmpctx();
|
||||||
|
|
||||||
|
if (argv[1])
|
||||||
|
json = grab_file(tmpctx, argv[1]);
|
||||||
|
else {
|
||||||
|
char *dir = getenv("BOLTDIR");
|
||||||
|
json = grab_file(tmpctx,
|
||||||
|
path_join(tmpctx,
|
||||||
|
dir ? dir : "../lightning-rfc",
|
||||||
|
"bolt12/format-string-test.json"));
|
||||||
|
if (!json) {
|
||||||
|
printf("test file not found, skipping\n");
|
||||||
|
tal_free(tmpctx);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsmn_init(&parser);
|
||||||
|
if (jsmn_parse(&parser, json, strlen(json), toks, ARRAY_SIZE(toks)) < 0)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
json_for_each_arr(i, t, toks) {
|
||||||
|
bool valid, actual;
|
||||||
|
const jsmntok_t *strtok;
|
||||||
|
char *fail;
|
||||||
|
const char *str;
|
||||||
|
size_t dlen;
|
||||||
|
struct json_escape *esc;
|
||||||
|
|
||||||
|
json_to_bool(json, json_get_member(json, t, "valid"), &valid);
|
||||||
|
strtok = json_get_member(json, t, "string");
|
||||||
|
esc = json_escape_string_(tmpctx, json + strtok->start,
|
||||||
|
strtok->end - strtok->start);
|
||||||
|
str = json_escape_unescape(tmpctx, esc);
|
||||||
|
actual = (string_to_data(tmpctx, str, strlen(str),
|
||||||
|
"lni", &dlen, &fail) != NULL);
|
||||||
|
assert(actual == valid);
|
||||||
|
}
|
||||||
|
tal_free(tmpctx);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue