From cdcafdaf749ebeed9f7d60e8b898dbaf8c9b1272 Mon Sep 17 00:00:00 2001 From: trueptolemy Date: Fri, 16 Aug 2019 01:41:23 +0800 Subject: [PATCH] API: `txprepare` now support mutiple outputs --- common/json_helpers.c | 10 ++ common/json_helpers.h | 5 + common/json_tok.c | 16 +- common/json_tok.h | 6 + contrib/pylightning/lightning/lightning.py | 12 +- hsmd/hsm_wire.csv | 4 +- hsmd/hsmd.c | 9 +- tests/test_wallet.py | 24 +-- tools/generate-wire.py | 1 + wallet/wallet.h | 4 +- wallet/walletrpc.c | 163 +++++++++++++++++---- wire/fromwire.c | 11 ++ wire/towire.c | 7 + wire/wire.h | 3 + 14 files changed, 213 insertions(+), 62 deletions(-) diff --git a/common/json_helpers.c b/common/json_helpers.c index c99e3f1f4..7664fa67f 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -60,6 +60,16 @@ bool json_to_sat(const char *buffer, const jsmntok_t *tok, return parse_amount_sat(sat, buffer + tok->start, tok->end - tok->start); } +bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat) +{ + if (json_tok_streq(buffer, tok, "all")) { + *sat = AMOUNT_SAT(-1ULL); + return true; + } + return json_to_sat(buffer, tok, sat); +} + bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, struct short_channel_id *scid, bool may_be_deprecated_form) diff --git a/common/json_helpers.h b/common/json_helpers.h index 9872af9a3..b3b21f713 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -32,6 +32,11 @@ bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, bool json_to_sat(const char *buffer, const jsmntok_t *tok, struct amount_sat *sat); +/* Extract a satoshis amount from this */ +/* If the string is "all", set amonut as AMOUNT_SAT(-1ULL). */ +bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat); + /* Extract a millisatoshis amount from this */ bool json_to_msat(const char *buffer, const jsmntok_t *tok, struct amount_msat *msat); diff --git a/common/json_tok.c b/common/json_tok.c index 8668df60b..6868b2752 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -183,7 +183,19 @@ struct command_result *param_sat(struct command *cmd, const char *name, return NULL; return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a satoshi amount, not '%.*s'", - name, tok->end - tok->start, buffer + tok->start); + "%s should be a satoshi amount, not '%.*s'", + name ? name : "amount field", + tok->end - tok->start, buffer + tok->start); } +struct command_result *param_sat_or_all(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct amount_sat **sat) +{ + if (json_tok_streq(buffer, tok, "all")) { + *sat = tal(cmd, struct amount_sat); + **sat = AMOUNT_SAT(-1ULL); + return NULL; + } + return param_sat(cmd, name, buffer, tok, sat); +} diff --git a/common/json_tok.h b/common/json_tok.h index feb158536..6a15c30ef 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -74,6 +74,12 @@ struct command_result *param_sat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_sat **sat); +/* Extract satoshi amount from this string. */ +/* If the string is "all", set amonut as AMOUNT_SAT(-1ULL). */ +struct command_result *param_sat_or_all(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct amount_sat **sat); + /* * Set the address of @out to @tok. Used as a callback by handlers that * want to unmarshal @tok themselves. diff --git a/contrib/pylightning/lightning/lightning.py b/contrib/pylightning/lightning/lightning.py index 9e505d368..852e29194 100644 --- a/contrib/pylightning/lightning/lightning.py +++ b/contrib/pylightning/lightning/lightning.py @@ -878,18 +878,18 @@ class LightningRpc(UnixDomainSocketRpc): } return self.call("withdraw", payload) - def txprepare(self, destination, satoshi, feerate=None, minconf=None): + def txprepare(self, outputs, feerate=None, minconf=None): """ - Prepare a bitcoin transaction which sends to {destination} address - {satoshi} (or "all") amount via Bitcoin transaction. Only select outputs - with {minconf} confirmations. + Prepare a bitcoin transaction which sends to [outputs]. + The format of output is like [{address1: amount1}, + {address2: amount2}], or [{address: "all"}]). + Only select outputs with {minconf} confirmations. Outputs will be reserved until you call txdiscard or txsend, or lightningd restarts. """ payload = { - "destination": destination, - "satoshi": satoshi, + "outputs": outputs, "feerate": feerate, "minconf": minconf, } diff --git a/hsmd/hsm_wire.csv b/hsmd/hsm_wire.csv index 3914ecb4d..bdb542216 100644 --- a/hsmd/hsm_wire.csv +++ b/hsmd/hsm_wire.csv @@ -69,8 +69,8 @@ msgtype,hsm_sign_withdrawal,7 msgdata,hsm_sign_withdrawal,satoshi_out,amount_sat, msgdata,hsm_sign_withdrawal,change_out,amount_sat, msgdata,hsm_sign_withdrawal,change_keyindex,u32, -msgdata,hsm_sign_withdrawal,scriptpubkey_len,u16, -msgdata,hsm_sign_withdrawal,scriptpubkey,u8,scriptpubkey_len +msgdata,hsm_sign_withdrawal,num_outputs,u16, +msgdata,hsm_sign_withdrawal,outputs,bitcoin_tx_output,num_outputs msgdata,hsm_sign_withdrawal,num_inputs,u16, msgdata,hsm_sign_withdrawal,inputs,utxo,num_inputs diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 402f4e814..c1b8eb9b0 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -1514,24 +1514,17 @@ static struct io_plan *handle_sign_withdrawal_tx(struct io_conn *conn, struct utxo **utxos; struct bitcoin_tx *tx; struct pubkey changekey; - u8 *scriptpubkey; struct bitcoin_tx_output **outputs; - outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 0); if (!fromwire_hsm_sign_withdrawal(tmpctx, msg_in, &satoshi_out, &change_out, &change_keyindex, - &scriptpubkey, &utxos)) + &outputs, &utxos)) return bad_req(conn, c, msg_in); if (!bip32_pubkey(&secretstuff.bip32, &changekey, change_keyindex)) return bad_req_fmt(conn, c, msg_in, "Failed to get key %u", change_keyindex); - struct bitcoin_tx_output *output = tal(outputs, - struct bitcoin_tx_output); - output->script = tal_steal(output, scriptpubkey); - output->amount = satoshi_out; - tal_arr_expand(&outputs, output); tx = withdraw_tx(tmpctx, c->chainparams, cast_const2(const struct utxo **, utxos), outputs, &changekey, change_out, NULL, NULL); diff --git a/tests/test_wallet.py b/tests/test_wallet.py index d50de4060..d4bcc1d12 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -207,8 +207,7 @@ def test_txprepare(node_factory, bitcoind): bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10) - prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - Millisatoshi(amount * 3 * 1000)) + prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 1000)}]) decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx']) assert decode['txid'] == prep['txid'] # 4 inputs, 2 outputs. @@ -231,8 +230,7 @@ def test_txprepare(node_factory, bitcoind): assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash' # Now prepare one with no change. - prep2 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - 'all') + prep2 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}]) decode = bitcoind.rpc.decoderawtransaction(prep2['unsigned_tx']) assert decode['txid'] == prep2['txid'] # 6 inputs, 1 outputs. @@ -250,8 +248,7 @@ def test_txprepare(node_factory, bitcoind): assert discard['txid'] == prep['txid'] assert discard['unsigned_tx'] == prep['unsigned_tx'] - prep3 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - 'all') + prep3 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}]) decode = bitcoind.rpc.decoderawtransaction(prep3['unsigned_tx']) assert decode['txid'] == prep3['txid'] # 4 inputs, 1 outputs. @@ -271,8 +268,7 @@ def test_txprepare(node_factory, bitcoind): # Discard everything, we should now spend all inputs. l1.rpc.txdiscard(prep2['txid']) l1.rpc.txdiscard(prep3['txid']) - prep4 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - 'all') + prep4 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}]) decode = bitcoind.rpc.decoderawtransaction(prep4['unsigned_tx']) assert decode['txid'] == prep4['txid'] # 10 inputs, 1 outputs. @@ -299,8 +295,7 @@ def test_txsend(node_factory, bitcoind): bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10) - prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - Millisatoshi(amount * 3 * 1000)) + prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 1000)}]) out = l1.rpc.txsend(prep['txid']) # Cannot discard after send! @@ -343,8 +338,7 @@ def test_txprepare_restart(node_factory, bitcoind): bitcoind.generate_block(1) wait_for(lambda: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 10) - prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - 'all') + prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}]) decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx']) assert decode['txid'] == prep['txid'] # All 10 inputs @@ -359,8 +353,7 @@ def test_txprepare_restart(node_factory, bitcoind): with pytest.raises(RpcError, match=r'not an unreleased txid'): l1.rpc.txdiscard(prep['txid']) - prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - 'all') + prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}]) decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx']) assert decode['txid'] == prep['txid'] @@ -377,8 +370,7 @@ def test_txprepare_restart(node_factory, bitcoind): for i in decode['vin']: assert l1.daemon.is_in_log('wallet: reserved output {}/{} reset to available'.format(i['txid'], i['vout'])) - prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg', - 'all') + prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}]) decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx']) assert decode['txid'] == prep['txid'] # All 10 inputs diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 71ec01744..8382940f0 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -216,6 +216,7 @@ class Type(FieldSet): 'bitcoin_tx', 'wirestring', 'per_peer_state', + 'bitcoin_tx_output', ] # Some BOLT types are re-typed based on their field name diff --git a/wallet/wallet.h b/wallet/wallet.h index f5d16580b..4c67cfb69 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -58,8 +58,8 @@ struct unreleased_tx { struct list_node list; /* All the utxos. */ struct wallet_tx *wtx; - /* Scriptpubkey this pays to. */ - const u8 *destination; + /* Outputs(scriptpubkey and satoshi) this pays to. */ + struct bitcoin_tx_output **outputs; /* The tx itself (unsigned initially) */ struct bitcoin_tx *tx; struct bitcoin_txid txid; diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index e2c4e3598..8903a0cd1 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -93,7 +93,9 @@ static struct command_result *param_bitcoin_address(struct command *cmd, scriptpubkey)) { case ADDRESS_PARSE_UNRECOGNIZED: return command_fail(cmd, LIGHTNINGD, - "Could not parse destination address"); + "Could not parse destination address, " + "%s should be a valid address", + name ? name : "address field"); case ADDRESS_PARSE_WRONG_NETWORK: return command_fail(cmd, LIGHTNINGD, "Destination address is not on network %s", @@ -117,7 +119,8 @@ static struct command_result *broadcast_and_wait(struct command *cmd, utx->wtx->amount, utx->wtx->change, utx->wtx->change_key_index, - utx->destination, + cast_const2(const struct bitcoin_tx_output **, + utx->outputs), utx->wtx->utxos); if (!wire_sync_write(cmd->ld->hsm_fd, take(msg))) @@ -152,36 +155,65 @@ static struct command_result *broadcast_and_wait(struct command *cmd, /* Common code for withdraw and txprepare. * - * Returns NULL on success, and fills in wtx, destination and + * Returns NULL on success, and fills in wtx, output and * maybe changekey (owned by cmd). Otherwise, cmd has failed, so don't * access it! (It's been freed). */ static struct command_result *json_prepare_tx(struct command *cmd, const char *buffer, const jsmntok_t *params, - struct unreleased_tx **utx) + struct unreleased_tx **utx, + bool for_withdraw) { u32 *feerate_per_kw; struct command_result *res; u32 *minconf, maxheight; struct pubkey *changekey; struct bitcoin_tx_output **outputs; + const jsmntok_t *outputstok, *t; + const u8 *destination = NULL; + size_t out_len, i; *utx = tal(cmd, struct unreleased_tx); (*utx)->wtx = tal(*utx, struct wallet_tx); wtx_init(cmd, (*utx)->wtx, AMOUNT_SAT(-1ULL)); - outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 0); - if (!param(cmd, buffer, params, - p_req("destination", param_bitcoin_address, - &(*utx)->destination), - p_req("satoshi", param_wtx, (*utx)->wtx), - p_opt("feerate", param_feerate, &feerate_per_kw), - p_opt_def("minconf", param_number, &minconf, 1), - NULL)) + if (!for_withdraw) { + /* From v0.7.3, the new style for *txprepare* use array of outputs + * to replace original 'destination' and 'satoshi' parameters.*/ + if (!param(cmd, buffer, params, + p_req("outputs", param_array, &outputstok), + p_opt("feerate", param_feerate, &feerate_per_kw), + p_opt_def("minconf", param_number, &minconf, 1), + NULL)) { + + /* For generating help, give new-style. */ + if (!params || !deprecated_apis) + return command_param_failed(); + + /* For the old style: + * *txprepare* 'destination' 'satoshi' ['feerate'] ['minconf'] */ + if (!param(cmd, buffer, params, + p_req("destination", param_bitcoin_address, + &destination), + p_req("satoshi", param_wtx, (*utx)->wtx), + p_opt("feerate", param_feerate, &feerate_per_kw), + p_opt_def("minconf", param_number, &minconf, 1), + NULL)) + /* If the parameters mixed the new style and the old style, + * fail it. */ + return command_param_failed(); + } + } else { + /* *withdraw* command still use 'destination' and 'satoshi' as parameters. */ + if (!param(cmd, buffer, params, + p_req("destination", param_bitcoin_address, + &destination), + p_req("satoshi", param_wtx, (*utx)->wtx), + p_opt("feerate", param_feerate, &feerate_per_kw), + p_opt_def("minconf", param_number, &minconf, 1), + NULL)) return command_param_failed(); - - /* Destination is owned by cmd: change that to be owned by utx. */ - tal_steal(*utx, (*utx)->destination); + } if (!feerate_per_kw) { res = param_feerate_estimate(cmd, &feerate_per_kw, @@ -191,11 +223,97 @@ static struct command_result *json_prepare_tx(struct command *cmd, } maxheight = minconf_to_maxheight(*minconf, cmd->ld); + + /* *withdraw* command or old *txprepare* command. + * Support only one output. */ + if (destination) { + outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 1); + outputs[0] = tal(outputs, struct bitcoin_tx_output); + outputs[0]->script = tal_steal(outputs[0], + cast_const(u8 *, destination)); + outputs[0]->amount = (*utx)->wtx->amount; + out_len = tal_count(outputs[0]->script); + + goto create_tx; + } + + if (outputstok->size == 0) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty outputs"); + + outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, outputstok->size); + out_len = 0; + (*utx)->wtx->all_funds = false; + (*utx)->wtx->amount = AMOUNT_SAT(0); + json_for_each_arr(i, t, outputstok) { + struct amount_sat *amount; + const u8 *destination; + enum address_parse_result res; + + /* output format: {destination: amount} */ + if (t->type != JSMN_OBJECT) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "The output format must be " + "{destination: amount}");; + + res = json_tok_address_scriptpubkey(cmd, + get_chainparams(cmd->ld), + buffer, &t[1], + &destination); + if (res == ADDRESS_PARSE_UNRECOGNIZED) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Could not parse destination address"); + else if (res == ADDRESS_PARSE_WRONG_NETWORK) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Destination address is not on network %s", + get_chainparams(cmd->ld)->network_name); + + amount = tal(tmpctx, struct amount_sat); + if (!json_to_sat_or_all(buffer, &t[2], amount)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%.*s' is a invalid satoshi amount", + t[2].end - t[2].start, buffer + t[2].start); + + out_len += tal_count(destination); + outputs[i] = tal(outputs, struct bitcoin_tx_output); + outputs[i]->amount = *amount; + outputs[i]->script = tal_steal(outputs[i], + cast_const(u8 *, destination)); + + /* In fact, the maximum amount of bitcoin satoshi is 2.1e15. + * It can't be equal to/bigger than 2^64. + * On the hand, the maximum amount of litoshi is 8.4e15, + * which also can't overflow. */ + /* This means this destination need "all" satoshi we have. */ + if (amount_sat_eq(*amount, AMOUNT_SAT(-1ULL))) { + if (outputstok->size > 1) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "outputs[%zi]: this destination wants" + " all satoshi. The count of outputs" + " can't be more than 1. ", i); + (*utx)->wtx->all_funds = true; + /* `AMOUNT_SAT(-1ULL)` is the max permissible for `wallet_select_all`. */ + (*utx)->wtx->amount = *amount; + break; + } + + if (!amount_sat_add(&(*utx)->wtx->amount, (*utx)->wtx->amount, *amount)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "outputs: The sum of first %zi outputs" + " overflow. ", i); + } + +create_tx: + (*utx)->outputs = tal_steal(*utx, outputs); res = wtx_select_utxos((*utx)->wtx, *feerate_per_kw, - tal_count((*utx)->destination), maxheight); + out_len, maxheight); if (res) return res; + /* Because of the max limit of AMOUNT_SAT(-1ULL), + * `(*utx)->wtx->all_funds` won't change in `wtx_select_utxos()` */ + if ((*utx)->wtx->all_funds) + outputs[0]->amount = (*utx)->wtx->amount; + if (!amount_sat_eq((*utx)->wtx->change, AMOUNT_SAT(0))) { changekey = tal(tmpctx, struct pubkey); if (!bip32_pubkey(cmd->ld->wallet->bip32_base, changekey, @@ -203,15 +321,8 @@ static struct command_result *json_prepare_tx(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "Keys generation failure"); } else changekey = NULL; - - struct bitcoin_tx_output *output = tal(outputs, - struct bitcoin_tx_output); - output->script = tal_dup_arr(output, u8, (*utx)->destination, - tal_count((*utx)->destination), 0); - output->amount = (*utx)->wtx->amount; - tal_arr_expand(&outputs, output); (*utx)->tx = withdraw_tx(*utx, get_chainparams(cmd->ld), - (*utx)->wtx->utxos, outputs, + (*utx)->wtx->utxos, (*utx)->outputs, changekey, (*utx)->wtx->change, cmd->ld->wallet->bip32_base, &(*utx)->change_outnum); @@ -229,7 +340,7 @@ static struct command_result *json_txprepare(struct command *cmd, struct command_result *res; struct json_stream *response; - res = json_prepare_tx(cmd, buffer, params, &utx); + res = json_prepare_tx(cmd, buffer, params, &utx, false); if (res) return res; @@ -352,7 +463,7 @@ static struct command_result *json_withdraw(struct command *cmd, struct command_result *res; struct bitcoin_txid txid; - res = json_prepare_tx(cmd, buffer, params, &utx); + res = json_prepare_tx(cmd, buffer, params, &utx, true); if (res) return res; diff --git a/wire/fromwire.c b/wire/fromwire.c index 2d7ca0759..99f7ac521 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -378,3 +378,14 @@ void fromwire_bip32_key_version(const u8** cursor, size_t *max, version->bip32_pubkey_version = fromwire_u32(cursor, max); version->bip32_privkey_version = fromwire_u32(cursor, max); } + +struct bitcoin_tx_output *fromwire_bitcoin_tx_output(const tal_t *ctx, + const u8 **cursor, size_t *max) +{ + struct bitcoin_tx_output *output = tal(ctx, struct bitcoin_tx_output); + output->amount = fromwire_amount_sat(cursor, max); + u16 script_len = fromwire_u16(cursor, max); + output->script = tal_arr(output, u8, script_len); + fromwire_u8_array(cursor, max, output->script, script_len); + return output; +} diff --git a/wire/towire.c b/wire/towire.c index eb17a7550..40ca26cfe 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -249,3 +249,10 @@ void towire_bip32_key_version(u8 **pptr, const struct bip32_key_version *version towire_u32(pptr, version->bip32_pubkey_version); towire_u32(pptr, version->bip32_privkey_version); } + +void towire_bitcoin_tx_output(u8 **pptr, const struct bitcoin_tx_output *output) +{ + towire_amount_sat(pptr, output->amount); + towire_u16(pptr, tal_count(output->script)); + towire_u8_array(pptr, output->script, tal_count(output->script)); +} diff --git a/wire/wire.h b/wire/wire.h index d45c0c951..ee8051fd0 100644 --- a/wire/wire.h +++ b/wire/wire.h @@ -88,6 +88,7 @@ void towire_wirestring(u8 **pptr, const char *str); void towire_siphash_seed(u8 **cursor, const struct siphash_seed *seed); void towire_bip32_key_version(u8 **cursor, const struct bip32_key_version *version); +void towire_bitcoin_tx_output(u8 **pptr, const struct bitcoin_tx_output *output); const u8 *fromwire(const u8 **cursor, size_t *max, void *copy, size_t n); u8 fromwire_u8(const u8 **cursor, size_t *max); @@ -138,4 +139,6 @@ void fromwire_siphash_seed(const u8 **cursor, size_t *max, struct siphash_seed *seed); void fromwire_bip32_key_version(const u8 **cursor, size_t *max, struct bip32_key_version *version); +struct bitcoin_tx_output *fromwire_bitcoin_tx_output(const tal_t *ctx, + const u8 **cursor, size_t *max); #endif /* LIGHTNING_WIRE_WIRE_H */