From 1648eb548aaaafe26e4bde6ef3a2fdbcaa8b594f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 22 Nov 2017 10:56:08 +1030 Subject: [PATCH] devtools/bolt11-cli: simple helper to decode bolt11. Can be extended to encode later, for example. Signed-off-by: Rusty Russell --- Makefile | 1 + devtools/.gitignore | 1 + devtools/Makefile | 28 +++++++ devtools/bolt11-cli.c | 167 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 devtools/.gitignore create mode 100644 devtools/Makefile create mode 100644 devtools/bolt11-cli.c diff --git a/Makefile b/Makefile index 7bdc1414f..aa4ce0ed2 100644 --- a/Makefile +++ b/Makefile @@ -164,6 +164,7 @@ include lightningd/Makefile include cli/Makefile include test/Makefile include doc/Makefile +include devtools/Makefile # Git doesn't maintain timestamps, so we only regen if git says we should. CHANGED_FROM_GIT = [ x"`git log $@ | head -n1`" != x"`git log $< | head -n1`" -o x"`git diff $<`" != x"" ] diff --git a/devtools/.gitignore b/devtools/.gitignore new file mode 100644 index 000000000..a78a1afac --- /dev/null +++ b/devtools/.gitignore @@ -0,0 +1 @@ +bolt11-cli diff --git a/devtools/Makefile b/devtools/Makefile new file mode 100644 index 000000000..b8973ebd5 --- /dev/null +++ b/devtools/Makefile @@ -0,0 +1,28 @@ +DEVTOOLS_CLI_SRC := devtools/bolt11-cli.c +DEVTOOLS_CLI_OBJS := $(DEVTOOLS_CLI_SRC:.c=.o) + +DEVTOOLS_CLI_COMMON_OBJS := \ + common/bech32.o \ + common/bolt11.o \ + common/hash_u5.o \ + common/type_to_string.o \ + common/utils.o \ + common/version.o + +devtools-all: devtools/bolt11-cli + +devtools/bolt11-cli: $(DEVTOOLS_CLI_OBJS) $(DEVTOOLS_CLI_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o + +$(DEVTOOLS_CLI_OBJS): wire/wire.h + +# Make sure these depend on everything. +ALL_PROGRAMS += devtools/bolt11-cli +ALL_OBJS += $(DEVTOOLS_CLI_OBJS) + +check-source: $(DEVTOOLS_CLI_SRC:%=check-src-include-order/%) + +clean: devtools-clean + +devtools-clean: + $(RM) $(DEVTOOLS_CLI_OBJS) + diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c new file mode 100644 index 000000000..6320dcb87 --- /dev/null +++ b/devtools/bolt11-cli.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_ERROR 0 +#define ERROR_BAD_DECODE 1 +#define ERROR_USAGE 3 + +/* Tal wrappers for opt. */ +static void *opt_allocfn(size_t size) +{ + return tal_alloc_(NULL, size, false, false, + TAL_LABEL("opt_allocfn", "")); +} + +static void *tal_reallocfn(void *ptr, size_t size) +{ + if (!ptr) + return opt_allocfn(size); + tal_resize_(&ptr, 1, size, false); + return ptr; +} + +static void tal_freefn(void *ptr) +{ + tal_free(ptr); +} + +static char *fmt_time(const tal_t *ctx, u64 time) +{ + /* ctime is not sane. Take pointer, returns \n in string. */ + time_t t = time; + const char *p = ctime(&t); + + return tal_fmt(ctx, "%.*s", (int)strcspn(p, "\n"), p); +} + +int main(int argc, char *argv[]) +{ + const tal_t *ctx = tal(NULL, char); + const char *method; + struct bolt11 *b11; + struct bolt11_field *extra; + size_t i; + char *fail; + + err_set_progname(argv[0]); + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY + | SECP256K1_CONTEXT_SIGN); + + opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); + opt_register_noarg("--help|-h", opt_usage_and_exit, + " ", "Show this message"); + opt_register_version(); + + opt_early_parse(argc, argv, opt_log_stderr_exit); + opt_parse(&argc, argv, opt_log_stderr_exit); + + method = argv[1]; + if (!method) + errx(ERROR_USAGE, "Need at least one argument\n%s", + opt_usage(argv[0], NULL)); + + if (!streq(method, "decode")) + errx(ERROR_USAGE, "Need decode argument\n%s", + opt_usage(argv[0], NULL)); + + if (!argv[2]) + errx(ERROR_USAGE, "Need argument\n%s", + opt_usage(argv[0], NULL)); + + b11 = bolt11_decode(ctx, argv[2], NULL, &fail); + if (!b11) + errx(ERROR_BAD_DECODE, "%s", fail); + + printf("currency: %s\n", b11->chain->bip173_name); + printf("timestamp: %"PRIu64" (%s)\n", + b11->timestamp, fmt_time(ctx, b11->timestamp)); + printf("expiry: %"PRIu64" (%s)\n", + b11->expiry, fmt_time(ctx, b11->timestamp + b11->expiry)); + printf("payee: %s\n", + type_to_string(ctx, struct pubkey, &b11->receiver_id)); + printf("payment_hash: %s\n", + tal_hexstr(ctx, &b11->payment_hash, sizeof(b11->payment_hash))); + if (b11->msatoshi) + printf("msatoshi: %"PRIu64"\n", *b11->msatoshi); + if (b11->description) + printf("description: %s\n", b11->description); + if (b11->description_hash) + printf("description_hash: %s\n", + tal_hexstr(ctx, b11->description, + sizeof(*b11->description_hash))); + + if (tal_len(b11->fallback)) { + struct bitcoin_address pkh; + struct ripemd160 sh; + struct sha256 wsh; + + printf("fallback: %s\n", tal_hex(ctx, b11->fallback)); + if (is_p2pkh(b11->fallback, &pkh)) { + printf("fallback-P2PKH: %s\n", + bitcoin_to_base58(ctx, b11->chain->testnet, + &pkh)); + } else if (is_p2sh(b11->fallback, &sh)) { + printf("fallback-P2SH: %s\n", + p2sh_to_base58(ctx, + b11->chain->testnet, + &sh)); + } else if (is_p2wpkh(b11->fallback, &pkh)) { + char out[73 + strlen(b11->chain->bip173_name)]; + if (segwit_addr_encode(out, b11->chain->bip173_name, 0, + (const u8 *)&pkh, sizeof(pkh))) + printf("fallback-P2WPKH: %s\n", out); + } else if (is_p2wsh(b11->fallback, &wsh)) { + char out[73 + strlen(b11->chain->bip173_name)]; + if (segwit_addr_encode(out, b11->chain->bip173_name, 0, + (const u8 *)&wsh, sizeof(wsh))) + printf("fallback-P2WSH: %s\n", out); + } + } + + for (i = 0; i < tal_count(b11->routes); i++) { + printf("route: (node/chanid/fee/expirydelta) "); + for (size_t n = 0; n < tal_count(b11->routes[i]); n++) { + printf(" %s/%s/%"PRIu64"/%u", + type_to_string(ctx, struct pubkey, + &b11->routes[i][n].pubkey), + type_to_string(ctx, struct short_channel_id, + &b11->routes[i][n].short_channel_id), + b11->routes[i][n].fee, + b11->routes[i][n].cltv_expiry_delta); + } + printf("\n"); + } + + list_for_each(&b11->extra_fields, extra, list) { + char *data = tal_arr(ctx, char, tal_len(extra->data)+1); + + for (i = 0; i < tal_len(extra->data); i++) + data[i] = bech32_charset[extra->data[i]]; + + data[i] = '\0'; + printf("unknown: %c %s\n", extra->tag, data); + } + + printf("signature: %s\n", + type_to_string(ctx, secp256k1_ecdsa_signature, &b11->sig)); + tal_free(ctx); + return NO_ERROR; +}