diff --git a/contrib/pylightning/lightning/lightning.py b/contrib/pylightning/lightning/lightning.py index f255afdbf..694077860 100644 --- a/contrib/pylightning/lightning/lightning.py +++ b/contrib/pylightning/lightning/lightning.py @@ -150,7 +150,7 @@ class LightningRpc(UnixDomainSocketRpc): } return self.call("listchannels", payload) - def invoice(self, msatoshi, label, description, expiry=None): + def invoice(self, msatoshi, label, description, expiry=None, fallback=None): """ Create an invoice for {msatoshi} with {label} and {description} with optional {expiry} seconds (default 1 hour) @@ -159,7 +159,8 @@ class LightningRpc(UnixDomainSocketRpc): "msatoshi": msatoshi, "label": label, "description": description, - "expiry": expiry + "expiry": expiry, + "fallback": fallback } return self.call("invoice", payload) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 26eb33583..02d37a70c 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -103,14 +103,16 @@ static void json_invoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { const struct invoice *invoice; - jsmntok_t *msatoshi, *label, *desc, *exp; + jsmntok_t *msatoshi, *label, *desc, *exp, *fallback; u64 *msatoshi_val; const char *label_val; const char *desc_val; + const char *bip173; struct json_result *response = new_json_result(cmd); struct wallet *wallet = cmd->ld->wallet; struct bolt11 *b11; char *b11enc; + const u8 *fallback_script; u64 expiry = 3600; if (!json_get_params(cmd, buffer, params, @@ -118,6 +120,7 @@ static void json_invoice(struct command *cmd, "label", &label, "description", &desc, "?expiry", &exp, + "?fallback", &fallback, NULL)) { return; } @@ -169,6 +172,17 @@ static void json_invoice(struct command *cmd, return; } + /* fallback address */ + if (fallback) { + bip173 = json_tok_address_scriptpubkey(cmd, buffer, fallback, &fallback_script); + if (!streq(get_chainparams(cmd->ld)->bip173_name, bip173)) { + command_fail(cmd, "Invalid fallback address for %s does not match network %s", + get_chainparams(cmd->ld)->network_name, bip173); + return; + } + } + + invoice = wallet_invoice_create(cmd->ld->wallet, take(msatoshi_val), take(label_val), @@ -188,6 +202,8 @@ static void json_invoice(struct command *cmd, b11->expiry = expiry; b11->description = tal_steal(b11, desc_val); b11->description_hash = NULL; + if (fallback) + b11->fallback = tal_steal(b11, fallback_script); /* FIXME: add private routes if necessary! */ b11enc = bolt11_encode(cmd, b11, false, hsm_sign_b11, cmd->ld); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index d27f8d8d0..3b2aa40e9 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1,11 +1,15 @@ /* Code for JSON_RPC API */ /* eg: { "method" : "dev-echo", "params" : [ "hello", "Arabella!" ], "id" : "1" } */ #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -792,3 +796,97 @@ void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename) /* Technically this is a leak, but there's only one */ notleak(io_new_listener(ld, fd, incoming_jcon_connected, ld)); } + +/** + * segwit_addr_net_decode - Try to decode a Bech32 address and detect + * testnet/mainnet/regtest + * + * This processes the address and returns a string if it is a Bech32 + * address specified by BIP173. The string is set whether it is + * testnet ("tb"), mainnet ("bc"), or regtest ("bcrt") + * It does not check, witness version and program size restrictions. + * + * Out: witness_version: Pointer to an int that will be updated to contain + * the witness program version (between 0 and 16 inclusive). + * witness_program: Pointer to a buffer of size 40 that will be updated + * to contain the witness program bytes. + * witness_program_len: Pointer to a size_t that will be updated to + * contain the length of bytes in witness_program. + * In: addrz: Pointer to the null-terminated address. + * Returns string containing the human readable segment of bech32 address + */ +static const char* segwit_addr_net_decode(int *witness_version, + uint8_t *witness_program, + size_t *witness_program_len, + const char *addrz) +{ + const char *network[] = { "bc", "tb", "bcrt" }; + for (int i = 0; i < sizeof(network) / sizeof(*network); ++i) { + if (segwit_addr_decode(witness_version, + witness_program, witness_program_len, + network[i], addrz)) + return network[i]; + } + + return NULL; +} + +const char *json_tok_address_scriptpubkey(const tal_t *cxt, const char *buffer, + const jsmntok_t *tok, const u8 **scriptpubkey) +{ + struct bitcoin_address p2pkh_destination; + struct ripemd160 p2sh_destination; + int witness_version; + /* segwit_addr_net_decode requires a buffer of size 40, and will + * not write to the buffer if the address is too long, so a buffer + * of fixed size 40 will not overflow. */ + uint8_t witness_program[40]; + size_t witness_program_len; + bool witness_ok; + + char *addrz; + const char *bip173; + bool testnet; + + bip173 = NULL; + if (bitcoin_from_base58(&testnet, &p2pkh_destination, + buffer + tok->start, tok->end - tok->start)) { + *scriptpubkey = scriptpubkey_p2pkh(cxt, &p2pkh_destination); + bip173 = chainparams_for_network(testnet ? "testnet" : "bitcoin")->bip173_name; + } else if (p2sh_from_base58(&testnet, &p2sh_destination, + buffer + tok->start, tok->end - tok->start)) { + *scriptpubkey = scriptpubkey_p2sh_hash(cxt, &p2sh_destination); + bip173 = chainparams_for_network(testnet ? "testnet" : "bitcoin")->bip173_name; + } + /* Insert other parsers that accept pointer+len here. */ + + if (bip173) return bip173; + + /* Generate null-terminated address. */ + addrz = tal_dup_arr(cxt, char, buffer + tok->start, tok->end - tok->start, 1); + addrz[tok->end - tok->start] = '\0'; + + bip173 = segwit_addr_net_decode(&witness_version, witness_program, + &witness_program_len, addrz); + + if (bip173) { + witness_ok = false; + if (witness_version == 0 && (witness_program_len == 20 || + witness_program_len == 32)) { + witness_ok = true; + } + /* Insert other witness versions here. */ + + if (witness_ok) { + *scriptpubkey = scriptpubkey_witness_raw(cxt, witness_version, + witness_program, witness_program_len); + } + else { + bip173 = NULL; + } + } + /* Insert other parsers that accept null-terminated string here. */ + + tal_free(addrz); + return bip173; +} diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 5a2d0a2a2..f9f6dd62c 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -109,5 +109,11 @@ void json_add_address(struct json_result *response, const char *fieldname, /* For initialization */ void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename); +/* Returns NULL, or bip173 network name and fills in *scriptpubkey + * allocated off ctx + */ +const char *json_tok_address_scriptpubkey(const tal_t *ctx, const char *buffer, + const jsmntok_t *tok, const u8 **scriptpubkey); + AUTODATA_TYPE(json_command, struct json_command); #endif /* LIGHTNING_LIGHTNINGD_JSONRPC_H */ diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 23a69969d..f0fab9182 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -377,9 +377,11 @@ class LightningDTests(BaseLightningDTests): def test_invoice(self): l1 = self.node_factory.get_node() + l2 = self.node_factory.get_node() + addr = l2.rpc.newaddr('bech32')['address'] before = int(time.time()) - inv = l1.rpc.invoice(123000, 'label', 'description') + inv = l1.rpc.invoice(123000, 'label', 'description', '3700', addr) after = int(time.time()) b11 = l1.rpc.decodepay(inv['bolt11']) assert b11['currency'] == 'bcrt' @@ -387,8 +389,10 @@ class LightningDTests(BaseLightningDTests): assert b11['created_at'] <= after assert b11['payment_hash'] == inv['payment_hash'] assert b11['description'] == 'description' - assert b11['expiry'] == 3600 + assert b11['expiry'] == 3700 assert b11['payee'] == l1.info['id'] + assert b11['fallback']['addr'] == addr + assert b11['fallback']['type'] == 'P2WPKH' # Check pay_index is null outputs = l1.db_query('SELECT pay_index IS NULL AS q FROM invoices WHERE label="label";') @@ -2778,7 +2782,7 @@ class LightningDTests(BaseLightningDTests): # Now send some money to l2. # lightningd uses P2SH-P2WPKH - waddr = l2.rpc.newaddr()['address'] + waddr = l2.rpc.newaddr('bech32')['address'] out = l1.rpc.withdraw(waddr, 2 * amount) l1.bitcoin.rpc.generate(1) @@ -2794,7 +2798,7 @@ class LightningDTests(BaseLightningDTests): # Simple test for withdrawal to P2WPKH # Address from: https://bc-2.jp/tools/bech32demo/index.html - waddr = 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx' + waddr = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080' self.assertRaises(ValueError, l1.rpc.withdraw, 'xx1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', 2 * amount) self.assertRaises(ValueError, l1.rpc.withdraw, 'tb1pw508d6qejxtdg4y5r3zarvary0c5xw7kdl9fad', 2 * amount) self.assertRaises(ValueError, l1.rpc.withdraw, 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxxxxxx', 2 * amount) @@ -2807,7 +2811,7 @@ class LightningDTests(BaseLightningDTests): # Simple test for withdrawal to P2WSH # Address from: https://bc-2.jp/tools/bech32demo/index.html - waddr = 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7' + waddr = 'bcrt1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qzf4jry' self.assertRaises(ValueError, l1.rpc.withdraw, 'xx1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', 2 * amount) self.assertRaises(ValueError, l1.rpc.withdraw, 'tb1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qsm03tq', 2 * amount) self.assertRaises(ValueError, l1.rpc.withdraw, 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount) @@ -2842,7 +2846,7 @@ class LightningDTests(BaseLightningDTests): assert(c.fetchone()[0] == 6) # Test withdrawal to self. - out = l1.rpc.withdraw(l1.rpc.newaddr()['address'], 'all') + out = l1.rpc.withdraw(l1.rpc.newaddr('bech32')['address'], 'all') bitcoind.rpc.generate(1) c = db.cursor() c.execute('SELECT COUNT(*) FROM outputs WHERE status=0') diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 9aa204e73..966739379 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -75,113 +75,6 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED, } } -/** - * segwit_addr_net_decode - Try to decode a Bech32 address and detect - * testnet/mainnet - * - * This processes the address and returns true if it is a Bech32 - * address specified by BIP173. If it returns true, then *testnet is - * set whether it is testnet "tb" address or false if mainnet "bc" - * address. It does not check, witness version and program size - * restrictions. - * - * Out: testnet: Pointer to a bool that will be updated to true if the - * address is testnet, or false if mainnet. - * witness_version: Pointer to an int that will be updated to contain - * the witness program version (between 0 and 16 inclusive). - * witness_program: Pointer to a buffer of size 40 that will be updated - * to contain the witness program bytes. - * witness_program_len: Pointer to a size_t that will be updated to - * contain the length of bytes in witness_program. - * In: addrz: Pointer to the null-terminated address. - * Returns true if successful, false if fail (on fail, none of the out - * parameters are modified). - */ -static bool segwit_addr_net_decode(bool *testnet, int *witness_version, - uint8_t *witness_program, - size_t *witness_program_len, - const char *addrz) -{ - /* segwit_addr_decode itself expects a prog buffer (which we pass - * witness_program as) of size 40, so segwit_addr_net_decode - * inherits that requirement. It will not write to that buffer - * if the input address is too long, so no buffer overflow risk. */ - if (segwit_addr_decode(witness_version, - witness_program, witness_program_len, - "bc", addrz)) { - *testnet = false; - return true; - } else if (segwit_addr_decode(witness_version, - witness_program, witness_program_len, - "tb", addrz)) { - *testnet = true; - return true; - } - return false; -} - -/** - * scriptpubkey_from_address - Determine scriptpubkey from a given address - * - * This processes the address and returns the equivalent scriptpubkey - * for the address. If fail to parse the address, return NULL. If can - * parse address, also sets the testnet flag if address is a testnet - * address or clears it if mainnet. - */ -static u8 *scriptpubkey_from_address(const tal_t *cxt, bool *testnet, - const char *addr, size_t addrlen) -{ - struct bitcoin_address p2pkh_destination; - struct ripemd160 p2sh_destination; - int witness_version; - /* segwit_addr_net_decode requires a buffer of size 40, and will - * not write to the buffer if the address is too long, so a buffer - * of fixed size 40 will not overflow. */ - uint8_t witness_program[40]; - size_t witness_program_len; - bool witness_ok; - u8 *script = NULL; - - char *addrz; - bool my_testnet; - - if (bitcoin_from_base58(testnet, &p2pkh_destination, - addr, addrlen)) { - script = scriptpubkey_p2pkh(cxt, &p2pkh_destination); - } else if (p2sh_from_base58(testnet, &p2sh_destination, - addr, addrlen)) { - script = scriptpubkey_p2sh_hash(cxt, &p2sh_destination); - } - /* Insert other parsers that accept pointer+len here. */ - - if (script) return script; - - /* Generate null-terminated address. */ - addrz = tal_dup_arr(cxt, char, addr, addrlen, 1); - addrz[addrlen] = '\0'; - - if (segwit_addr_net_decode(&my_testnet, &witness_version, - witness_program, &witness_program_len, - addrz)) { - witness_ok = false; - if (witness_version == 0 && (witness_program_len == 20 || - witness_program_len == 32)) { - witness_ok = true; - } - /* Insert other witness versions here. */ - if (witness_ok) { - *testnet = my_testnet; - script = scriptpubkey_witness_raw(cxt, witness_version, - witness_program, - witness_program_len); - } - } - /* Insert other parsers that accept null-terminated string here. */ - - tal_free(addrz); - return script; -} - /** * json_withdraw - Entrypoint for the withdrawal flow * @@ -194,11 +87,11 @@ static void json_withdraw(struct command *cmd, { jsmntok_t *desttok, *sattok; struct withdrawal *withdraw; - bool testnet; u32 feerate_per_kw = get_feerate(cmd->ld->topology, FEERATE_NORMAL); u64 fee_estimate; struct bitcoin_tx *tx; bool withdraw_all = false; + const char *bip173 = NULL; if (!json_get_params(cmd, buffer, params, "destination", &desttok, @@ -218,26 +111,21 @@ static void json_withdraw(struct command *cmd, } /* Parse address. */ - withdraw->destination - = scriptpubkey_from_address(withdraw, &testnet, - buffer + desttok->start, - desttok->end - desttok->start); + bip173 = json_tok_address_scriptpubkey(cmd, buffer, + desttok, (const u8**)(&withdraw->destination)); /* Check that destination address could be understood. */ - if (!withdraw->destination) { + if (!bip173) { command_fail(cmd, "Could not parse destination address"); return; } /* Check address given is compatible with the chain we are on. */ - if (testnet != get_chainparams(cmd->ld)->testnet) { - if (testnet) { - command_fail(cmd, - "Use of testnet address on mainnet"); - } else { - command_fail(cmd, - "Use of mainnet address on testnet"); - } + if (!streq(bip173, get_chainparams(cmd->ld)->bip173_name)) { + command_fail(cmd, + "Use of %s address on %s", + chainparams_by_bip173(bip173)->network_name, + get_chainparams(cmd->ld)->network_name); return; }