diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b3e06c3..356bb998e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- JSON API: `newaddr` outputs `bech32` or `p2sh-segwit`, or both with new `all` parameter (#2390)# + ### Changed ### Deprecated diff --git a/doc/lightning-newaddr.7 b/doc/lightning-newaddr.7 index a3895987e..7139943ce 100644 --- a/doc/lightning-newaddr.7 +++ b/doc/lightning-newaddr.7 @@ -2,12 +2,12 @@ .\" Title: lightning-newaddr .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 07/23/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-NEWADDR" "7" "07/23/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-NEWADDR" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -38,12 +38,12 @@ The \fBnewaddr\fR RPC command generates a new address which can subsequently be .sp The funding transaction needs to be confirmed before funds can be used\&. .sp -\fIaddresstype\fR specifies the type of address wanted; i\&.e\&. p2sh\-segwit (e\&.g\&. 2MxaozoqWwiUcuD9KKgUSrLFDafLqimT9Ta on bitcoin testnet or 3MZxzq3jBSKNQ2e7dzneo9hy4FvNzmMmt3 on bitcoin mainnet) or bech32 (e\&.g\&. tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg on bitcoin testnet or bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej on bitcoin mainnet)\&. +\fIaddresstype\fR specifies the type of address wanted; i\&.e\&. \fIp2sh\-segwit\fR (e\&.g\&. 2MxaozoqWwiUcuD9KKgUSrLFDafLqimT9Ta on bitcoin testnet or 3MZxzq3jBSKNQ2e7dzneo9hy4FvNzmMmt3 on bitcoin mainnet) or \fIbech32 \*(Aq (e\&.g\&. tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg on bitcoin testnet or bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej on bitcoin mainnet)\&. The special value \*(Aqall\fR generates both address types for the same underlying key\&. .sp If not specified the address generated is bech32\&. .SH "RETURN VALUE" .sp -On success, a new address will be returned\&. +On success, a \fIbech32\fR address and/or a \fIp2sh\-segwit\fR address will be returned\&. .SH "ERRORS" .sp If an unrecognized address type is requested an error message will be returned\&. diff --git a/doc/lightning-newaddr.7.txt b/doc/lightning-newaddr.7.txt index 2f3c063d6..0cdd21a31 100644 --- a/doc/lightning-newaddr.7.txt +++ b/doc/lightning-newaddr.7.txt @@ -19,16 +19,17 @@ subsequently be used to fund channels managed by the c-lightning node. The funding transaction needs to be confirmed before funds can be used. 'addresstype' specifies the type of address wanted; i.e. -p2sh-segwit (e.g. 2MxaozoqWwiUcuD9KKgUSrLFDafLqimT9Ta on bitcoin testnet -or 3MZxzq3jBSKNQ2e7dzneo9hy4FvNzmMmt3 on bitcoin mainnet) or bech32 +'p2sh-segwit' (e.g. 2MxaozoqWwiUcuD9KKgUSrLFDafLqimT9Ta on bitcoin testnet +or 3MZxzq3jBSKNQ2e7dzneo9hy4FvNzmMmt3 on bitcoin mainnet) or 'bech32 ' (e.g. tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg on bitcoin testnet or bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej on bitcoin mainnet). +The special value 'all' generates both address types for the same underlying key. If not specified the address generated is bech32. RETURN VALUE ------------ -On success, a new address will be returned. +On success, a 'bech32' address and/or a 'p2sh-segwit' address will be returned. ERRORS ------ diff --git a/tests/test_misc.py b/tests/test_misc.py index 39993b169..2a17c3a5f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1232,3 +1232,16 @@ def test_bad_onion(node_factory, bitcoind): assert err.value.error['data']['failcode'] == WIRE_INVALID_ONION_HMAC assert err.value.error['data']['erring_node'] == mangled_nodeid assert err.value.error['data']['erring_channel'] == route[1]['channel'] + + +def test_newaddr(node_factory): + l1 = node_factory.get_node() + p2sh = l1.rpc.newaddr('p2sh-segwit') + assert 'bech32' not in p2sh + assert p2sh['p2sh-segwit'].startswith('2') + bech32 = l1.rpc.newaddr('bech32') + assert 'p2sh-segwit' not in bech32 + assert bech32['bech32'].startswith('bcrt1') + both = l1.rpc.newaddr('all') + assert both['p2sh-segwit'].startswith('2') + assert both['bech32'].startswith('bcrt1') diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 075d2a6cc..6c4c34a7c 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -266,25 +266,31 @@ encode_scriptpubkey_to_addr(const tal_t *ctx, return out; } -/* Extract a bool indicating "p2sh-segwit" or "bech32" */ +enum addrtype { + ADDR_P2SH_SEGWIT = 1, + ADDR_BECH32 = 2, + ADDR_ALL = (ADDR_P2SH_SEGWIT + ADDR_BECH32) +}; + +/* Extract bool indicating "p2sh-segwit" or "bech32" */ static struct command_result *param_newaddr(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, - bool **is_p2wpkh) + enum addrtype **addrtype) { - *is_p2wpkh = tal(cmd, bool); - if (json_tok_streq(buffer, tok, "p2sh-segwit")) { - **is_p2wpkh = false; - return NULL; - } - if (json_tok_streq(buffer, tok, "bech32")) { - **is_p2wpkh = true; - return NULL; - } - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be 'bech32' or 'p2sh-segwit', not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); + *addrtype = tal(cmd, enum addrtype); + if (json_tok_streq(buffer, tok, "p2sh-segwit")) + **addrtype = ADDR_P2SH_SEGWIT; + else if (json_tok_streq(buffer, tok, "bech32")) + **addrtype = ADDR_BECH32; + else if (json_tok_streq(buffer, tok, "all")) + **addrtype = ADDR_ALL; + else + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be 'bech32', 'p2sh-segwit' or 'all', not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); + return NULL; } static struct command_result *json_newaddr(struct command *cmd, @@ -295,12 +301,12 @@ static struct command_result *json_newaddr(struct command *cmd, struct json_stream *response; struct ext_key ext; struct pubkey pubkey; - bool *is_p2wpkh; + enum addrtype *addrtype; s64 keyidx; - char *out; + char *p2sh, *bech32; if (!param(cmd, buffer, params, - p_opt_def("addresstype", param_newaddr, &is_p2wpkh, true), + p_opt_def("addresstype", param_newaddr, &addrtype, ADDR_BECH32), NULL)) return command_param_failed(); @@ -321,17 +327,20 @@ static struct command_result *json_newaddr(struct command *cmd, txfilter_add_derkey(cmd->ld->owned_txfilter, ext.pub_key); - out = encode_pubkey_to_addr(cmd, cmd->ld, - &pubkey, !*is_p2wpkh, - NULL); - if (!out) { + p2sh = encode_pubkey_to_addr(cmd, cmd->ld, &pubkey, true, NULL); + bech32 = encode_pubkey_to_addr(cmd, cmd->ld, &pubkey, false, NULL); + if (!p2sh || !bech32) { return command_fail(cmd, LIGHTNINGD, "p2wpkh address encoding failure."); } response = json_stream_success(cmd); json_object_start(response, NULL); - json_add_string(response, "address", out); + json_add_string(response, "address", bech32 ? bech32 : p2sh); + if (*addrtype & ADDR_BECH32) + json_add_string(response, "bech32", bech32); + if (*addrtype & ADDR_P2SH_SEGWIT) + json_add_string(response, "p2sh-segwit", p2sh); json_object_end(response); return command_success(cmd, response); } @@ -339,8 +348,8 @@ static struct command_result *json_newaddr(struct command *cmd, static const struct json_command newaddr_command = { "newaddr", json_newaddr, - "Get a new {bech32, p2sh-segwit} address to fund a channel (default is bech32)", false, - "Generates a new address that belongs to the internal wallet. Funds sent to these addresses will be managed by lightningd. Use `withdraw` to withdraw funds to an external wallet." + "Get a new {bech32, p2sh-segwit} (or all) address to fund a channel (default is bech32)", false, + "Generates a new address (or both) that belongs to the internal wallet. Funds sent to these addresses will be managed by lightningd. Use `withdraw` to withdraw funds to an external wallet." }; AUTODATA(json_command, &newaddr_command);