From 924cc04bd2be74ab6d50398f1e61d7003718f4d7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Sep 2020 10:37:19 +0930 Subject: [PATCH] bolt11: have caller supply preferred chain. This lets us distinguish testnet from signet invoices, since they have the same prefix. Signed-off-by: Rusty Russell --- common/bolt11.c | 22 ++++++++++++++--- common/bolt11.h | 6 ++++- common/test/run-bolt11.c | 27 +++++++++++---------- devtools/bolt11-cli.c | 2 +- lightningd/invoice.c | 3 ++- lightningd/pay.c | 3 ++- lightningd/test/run-invoice-select-inchan.c | 4 ++- plugins/pay.c | 4 +-- 8 files changed, 47 insertions(+), 24 deletions(-) diff --git a/common/bolt11.c b/common/bolt11.c index 7e434c457..716a209b0 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -549,7 +549,9 @@ struct bolt11 *new_bolt11(const tal_t *ctx, /* Decodes and checks signature; returns NULL on error. */ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, const struct feature_set *our_features, - const char *description, char **fail) + const char *description, + const struct chainparams *must_be_chain, + char **fail) { char *hrp, *amountstr, *prefix; u5 *data; @@ -602,9 +604,21 @@ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, return decode_fail(b11, fail, "Prefix '%s' does not start with ln", prefix); - b11->chain = chainparams_by_bip173(prefix + 2); - if (!b11->chain) - return decode_fail(b11, fail, "Unknown chain %s", prefix + 2); + /* Signet chose to use prefix 'tb', just like testnet. So we tread + * carefully here: */ + if (must_be_chain) { + if (streq(prefix + 2, must_be_chain->bip173_name)) + b11->chain = must_be_chain; + else + return decode_fail(b11, fail, "Prefix %s is not for %s", + prefix + 2, + must_be_chain->network_name); + } else { + b11->chain = chainparams_by_bip173(prefix + 2); + if (!b11->chain) + return decode_fail(b11, fail, "Unknown chain %s", + prefix + 2); + } /* BOLT #11: * diff --git a/common/bolt11.h b/common/bolt11.h index 0f301241f..f58190eab 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -78,10 +78,14 @@ struct bolt11 { /* Decodes and checks signature; returns NULL on error; description is * (optional) out-of-band description of payment, for `h` field. * fset is NULL to accept any features (usually not desirable!). + * + * if @must_be_chain is not NULL, fails unless it's this chain. */ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, const struct feature_set *our_features, - const char *description, char **fail); + const char *description, + const struct chainparams *must_be_chain, + char **fail); /* Initialize an empty bolt11 struct with optional amount */ struct bolt11 *new_bolt11(const tal_t *ctx, diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index d8085b90a..8d2fb9673 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -58,7 +58,8 @@ static void test_b11(const char *b11str, char *reproduce; struct bolt11_field *b11_extra, *expect_extra; - b11 = bolt11_decode(tmpctx, b11str, NULL, hashed_desc, &fail); + b11 = bolt11_decode(tmpctx, b11str, NULL, hashed_desc, + expect_b11->chain, &fail); if (!b11) errx(1, "%s:%u:%s", __FILE__, __LINE__, fail); @@ -266,7 +267,7 @@ int main(void) for (size_t i = 0; i <= strlen(badstr); i++) { if (bolt11_decode(tmpctx, tal_strndup(tmpctx, badstr, i), - NULL, NULL, &fail)) + NULL, NULL, NULL, &fail)) abort(); assert(strstr(fail, "Bad bech32") || strstr(fail, "Invoices must start with ln")); @@ -462,19 +463,19 @@ int main(void) /* Empty set of allowed bits, ensures this fails! */ fset = tal(tmpctx, struct feature_set); fset->bits[BOLT11_FEATURE] = tal_arr(fset, u8, 0); - assert(!bolt11_decode(tmpctx, badstr, fset, NULL, &fail)); + assert(!bolt11_decode(tmpctx, badstr, fset, NULL, NULL, &fail)); assert(streq(fail, "9: unknown feature bit 100")); /* We'd actually allow this if we either (1) don't check, or (2) accept that feature in * either compulsory or optional forms. */ - assert(bolt11_decode(tmpctx, badstr, NULL, NULL, &fail)); + assert(bolt11_decode(tmpctx, badstr, NULL, NULL, NULL, &fail)); set_feature_bit(&fset->bits[BOLT11_FEATURE], 100); - assert(bolt11_decode(tmpctx, badstr, fset, NULL,&fail)); + assert(bolt11_decode(tmpctx, badstr, fset, NULL, NULL, &fail)); clear_feature_bit(fset->bits[BOLT11_FEATURE], 100); set_feature_bit(&fset->bits[BOLT11_FEATURE], 101); - assert(bolt11_decode(tmpctx, badstr, fset, NULL, &fail)); + assert(bolt11_decode(tmpctx, badstr, fset, NULL, NULL, &fail)); /* FIXME: quoting description in here causes a spurious mismatch! */ /* BOLT #11: @@ -540,48 +541,48 @@ int main(void) * > ### Bech32 checksum is invalid. * > lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt */ - assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt", NULL, NULL, NULL, &fail)); assert(streq(fail, "Bad bech32 string")); /* BOLT #11: * > ### Malformed bech32 string (no 1) * > pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny */ - assert(!bolt11_decode(tmpctx, "pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, NULL, NULL, &fail)); assert(streq(fail, "Bad bech32 string")); /* BOLT #11: * > ### Malformed bech32 string (mixed case) * > LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny */ - assert(!bolt11_decode(tmpctx, "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, NULL, NULL, &fail)); assert(streq(fail, "Bad bech32 string")); /* BOLT #11: * > ### Signature is not recoverable. * > lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq */ - assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq", NULL, NULL, NULL, &fail)); assert(streq(fail, "signature recovery failed")); /* BOLT #11: * > ### String is too short. * > lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh */ - assert(!bolt11_decode(tmpctx, "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh", NULL, NULL, NULL, &fail)); /* BOLT #11: * > ### Invalid multiplier * > lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg */ - assert(!bolt11_decode(tmpctx, "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg", NULL, NULL, NULL, &fail)); assert(streq(fail, "Invalid amount postfix 'x'")); /* BOLT #11: * > ### Invalid sub-millisatoshi precision. * > lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s */ - assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s", NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s", NULL, NULL, NULL, &fail)); assert(streq(fail, "Invalid sub-millisatoshi amount '2500000001p'")); /* FIXME: Test the others! */ diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index ce72a8d69..9b6d359ed 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) errx(ERROR_USAGE, "Need argument\n%s", opt_usage(argv[0], NULL)); - b11 = bolt11_decode(ctx, argv[2], NULL, description, &fail); + b11 = bolt11_decode(ctx, argv[2], NULL, description, NULL, &fail); if (!b11) errx(ERROR_BAD_DECODE, "%s", fail); diff --git a/lightningd/invoice.c b/lightningd/invoice.c index fbf5a0396..27554072b 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1325,7 +1325,8 @@ static struct command_result *json_decodepay(struct command *cmd, NULL)) return command_param_failed(); - b11 = bolt11_decode(cmd, str, cmd->ld->our_features, desc, &fail); + b11 = bolt11_decode(cmd, str, cmd->ld->our_features, desc, NULL, + &fail); if (!b11) { return command_fail(cmd, LIGHTNINGD, "Invalid bolt11: %s", fail); diff --git a/lightningd/pay.c b/lightningd/pay.c index a5ebdd431..4e7318d36 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1485,7 +1485,8 @@ static struct command_result *json_listsendpays(struct command *cmd, struct bolt11 *b11; char *fail; - b11 = bolt11_decode(cmd, b11str, cmd->ld->our_features, NULL, &fail); + b11 = bolt11_decode(cmd, b11str, cmd->ld->our_features, NULL, + chainparams, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index d50d8a487..6319f4e35 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -22,7 +22,9 @@ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, /* Generated stub for bolt11_decode */ struct bolt11 *bolt11_decode(const tal_t *ctx UNNEEDED, const char *str UNNEEDED, const struct feature_set *our_features UNNEEDED, - const char *description UNNEEDED, char **fail UNNEEDED) + const char *description UNNEEDED, + const struct chainparams *must_be_chain UNNEEDED, + char **fail UNNEEDED) { fprintf(stderr, "bolt11_decode called!\n"); abort(); } /* Generated stub for bolt11_encode_ */ char *bolt11_encode_(const tal_t *ctx UNNEEDED, diff --git a/plugins/pay.c b/plugins/pay.c index 6ad6278b8..b802dc81e 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1317,7 +1317,7 @@ static struct command_result *json_pay(struct command *cmd, return command_param_failed(); b11 = bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), - NULL, &fail); + NULL, chainparams, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail); @@ -1983,7 +1983,7 @@ static struct command_result *json_paymod(struct command *cmd, return command_param_failed(); b11 = bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), - NULL, &fail); + NULL, chainparams, &fail); if (!b11) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail);