2017-08-28 18:02:01 +02:00
|
|
|
#include <bitcoin/script.h>
|
|
|
|
#include <bitcoin/tx.h>
|
|
|
|
#include <ccan/endian/endian.h>
|
|
|
|
#include <common/initial_commit_tx.h>
|
2017-08-28 18:05:01 +02:00
|
|
|
#include <common/keyset.h>
|
2017-08-28 18:02:01 +02:00
|
|
|
#include <common/permute_tx.h>
|
2018-03-18 05:41:32 +01:00
|
|
|
#include <common/status.h>
|
2019-02-21 04:45:55 +01:00
|
|
|
#include <common/type_to_string.h>
|
2018-03-18 05:41:32 +01:00
|
|
|
#include <inttypes.h>
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2019-01-14 03:22:05 +01:00
|
|
|
* The 48-bit commitment number is obscured by `XOR` with the lower 48 bits of:
|
2017-08-28 18:02:01 +02:00
|
|
|
*
|
|
|
|
* SHA256(payment_basepoint from open_channel || payment_basepoint from accept_channel)
|
|
|
|
*/
|
|
|
|
u64 commit_number_obscurer(const struct pubkey *opener_payment_basepoint,
|
|
|
|
const struct pubkey *accepter_payment_basepoint)
|
|
|
|
{
|
pubkey: rename PUBKEY_DER_LEN to PUBKEY_CMPR_LEN.
Pubkeys are not not actually DER encoding, but Pieter Wuille corrected
me: it's SEC 1 documented encoding.
Results from 5 runs, min-max(mean +/- stddev):
store_load_msec,vsz_kb,store_rewrite_sec,listnodes_sec,listchannels_sec,routing_sec,peer_write_all_sec
38922-39297(39180.6+/-1.3e+02),2880728,41.040000-41.160000(41.106+/-0.05),2.270000-2.530000(2.338+/-0.097),44.570000-53.980000(49.696+/-3),32.840000-33.080000(32.95+/-0.095),43.060000-44.950000(43.696+/-0.72)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-04-08 08:34:05 +02:00
|
|
|
u8 ders[PUBKEY_CMPR_LEN * 2];
|
2017-08-28 18:02:01 +02:00
|
|
|
struct sha256 sha;
|
|
|
|
be64 obscurer = 0;
|
|
|
|
|
|
|
|
pubkey_to_der(ders, opener_payment_basepoint);
|
pubkey: rename PUBKEY_DER_LEN to PUBKEY_CMPR_LEN.
Pubkeys are not not actually DER encoding, but Pieter Wuille corrected
me: it's SEC 1 documented encoding.
Results from 5 runs, min-max(mean +/- stddev):
store_load_msec,vsz_kb,store_rewrite_sec,listnodes_sec,listchannels_sec,routing_sec,peer_write_all_sec
38922-39297(39180.6+/-1.3e+02),2880728,41.040000-41.160000(41.106+/-0.05),2.270000-2.530000(2.338+/-0.097),44.570000-53.980000(49.696+/-3),32.840000-33.080000(32.95+/-0.095),43.060000-44.950000(43.696+/-0.72)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-04-08 08:34:05 +02:00
|
|
|
pubkey_to_der(ders + PUBKEY_CMPR_LEN, accepter_payment_basepoint);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
sha256(&sha, ders, sizeof(ders));
|
|
|
|
/* Lower 48 bits */
|
|
|
|
memcpy((u8 *)&obscurer + 2, sha.u.u8 + sizeof(sha.u.u8) - 6, 6);
|
|
|
|
return be64_to_cpu(obscurer);
|
|
|
|
}
|
|
|
|
|
2019-09-09 18:11:24 +02:00
|
|
|
bool try_subtract_fee(enum side opener, enum side side,
|
2019-02-21 04:45:55 +01:00
|
|
|
struct amount_sat base_fee,
|
|
|
|
struct amount_msat *self,
|
|
|
|
struct amount_msat *other)
|
2017-08-28 18:02:01 +02:00
|
|
|
{
|
2019-09-09 18:11:24 +02:00
|
|
|
struct amount_msat *opener_amount;
|
2017-08-28 18:02:01 +02:00
|
|
|
|
2019-09-09 18:11:24 +02:00
|
|
|
if (opener == side)
|
|
|
|
opener_amount = self;
|
2017-08-28 18:02:01 +02:00
|
|
|
else
|
2019-09-09 18:11:24 +02:00
|
|
|
opener_amount = other;
|
2017-08-28 18:02:01 +02:00
|
|
|
|
2019-09-09 18:11:24 +02:00
|
|
|
if (amount_msat_sub_sat(opener_amount, *opener_amount, base_fee))
|
2018-03-18 05:41:32 +01:00
|
|
|
return true;
|
2019-02-21 04:45:55 +01:00
|
|
|
|
2019-09-09 18:11:24 +02:00
|
|
|
*opener_amount = AMOUNT_MSAT(0);
|
2019-02-21 04:45:55 +01:00
|
|
|
return false;
|
2017-08-28 18:02:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 *to_self_wscript(const tal_t *ctx,
|
|
|
|
u16 to_self_delay,
|
2021-06-16 19:44:51 +02:00
|
|
|
u32 csv,
|
2017-08-28 18:02:01 +02:00
|
|
|
const struct keyset *keyset)
|
|
|
|
{
|
2021-06-16 19:44:51 +02:00
|
|
|
return bitcoin_wscript_to_local(ctx, to_self_delay, csv,
|
2017-08-28 18:02:01 +02:00
|
|
|
&keyset->self_revocation_key,
|
|
|
|
&keyset->self_delayed_payment_key);
|
|
|
|
}
|
|
|
|
|
2020-08-13 19:47:02 +02:00
|
|
|
void tx_add_anchor_output(struct bitcoin_tx *tx,
|
|
|
|
const struct pubkey *funding_key)
|
|
|
|
{
|
|
|
|
u8 *wscript = bitcoin_wscript_anchor(tmpctx, funding_key);
|
|
|
|
u8 *p2wsh = scriptpubkey_p2wsh(tmpctx, wscript);
|
|
|
|
|
2020-08-20 08:49:47 +02:00
|
|
|
/* BOLT #3:
|
2020-08-13 19:47:02 +02:00
|
|
|
* The amount of the output is fixed at 330 sats, the default
|
|
|
|
* dust limit for P2WSH.
|
|
|
|
*/
|
|
|
|
bitcoin_tx_add_output(tx, p2wsh, wscript, AMOUNT_SAT(330));
|
|
|
|
}
|
|
|
|
|
2017-08-28 18:02:01 +02:00
|
|
|
struct bitcoin_tx *initial_commit_tx(const tal_t *ctx,
|
2017-12-18 07:41:52 +01:00
|
|
|
const struct bitcoin_txid *funding_txid,
|
2017-08-28 18:02:01 +02:00
|
|
|
unsigned int funding_txout,
|
2019-02-21 04:45:55 +01:00
|
|
|
struct amount_sat funding,
|
2020-08-13 19:46:02 +02:00
|
|
|
const struct pubkey funding_key[NUM_SIDES],
|
2019-09-09 18:11:24 +02:00
|
|
|
enum side opener,
|
2017-08-28 18:02:01 +02:00
|
|
|
u16 to_self_delay,
|
|
|
|
const struct keyset *keyset,
|
2017-11-21 04:33:22 +01:00
|
|
|
u32 feerate_per_kw,
|
2019-02-21 04:45:55 +01:00
|
|
|
struct amount_sat dust_limit,
|
|
|
|
struct amount_msat self_pay,
|
|
|
|
struct amount_msat other_pay,
|
|
|
|
struct amount_sat self_reserve,
|
2017-08-28 18:02:01 +02:00
|
|
|
u64 obscured_commitment_number,
|
2020-05-07 02:43:34 +02:00
|
|
|
struct wally_tx_output *direct_outputs[NUM_SIDES],
|
2019-04-03 10:20:36 +02:00
|
|
|
enum side side,
|
2021-06-16 19:44:51 +02:00
|
|
|
u32 csv_lock,
|
2020-08-13 19:44:02 +02:00
|
|
|
bool option_anchor_outputs,
|
2019-04-03 10:20:36 +02:00
|
|
|
char** err_reason)
|
2017-08-28 18:02:01 +02:00
|
|
|
{
|
2019-02-21 04:45:55 +01:00
|
|
|
struct amount_sat base_fee;
|
2017-08-28 18:02:01 +02:00
|
|
|
struct bitcoin_tx *tx;
|
|
|
|
size_t n, untrimmed;
|
2020-08-13 19:47:02 +02:00
|
|
|
bool to_local, to_remote;
|
2019-02-21 04:45:55 +01:00
|
|
|
struct amount_msat total_pay;
|
2019-03-15 16:59:56 +01:00
|
|
|
struct amount_sat amount;
|
|
|
|
u32 sequence;
|
2020-05-07 02:43:34 +02:00
|
|
|
void *dummy_local = (void *)LOCAL, *dummy_remote = (void *)REMOTE;
|
|
|
|
const void *output_order[NUM_SIDES];
|
2020-08-13 19:46:02 +02:00
|
|
|
const u8 *funding_wscript = bitcoin_redeem_2of2(tmpctx,
|
|
|
|
&funding_key[LOCAL],
|
|
|
|
&funding_key[REMOTE]);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
2019-02-21 04:45:55 +01:00
|
|
|
if (!amount_msat_add(&total_pay, self_pay, other_pay))
|
|
|
|
abort();
|
|
|
|
assert(!amount_msat_greater_sat(total_pay, funding));
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
|
|
|
* 1. Calculate which committed HTLCs need to be trimmed (see
|
|
|
|
* [Trimmed Outputs](#trimmed-outputs)).
|
|
|
|
*/
|
|
|
|
untrimmed = 0;
|
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
|
|
|
* 2. Calculate the base [commitment transaction
|
|
|
|
* fee](#fee-calculation).
|
|
|
|
*/
|
2020-08-13 19:44:02 +02:00
|
|
|
base_fee = commit_tx_base_fee(feerate_per_kw, untrimmed,
|
|
|
|
option_anchor_outputs);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
2020-08-20 08:49:47 +02:00
|
|
|
/* BOLT:
|
2020-08-13 19:47:02 +02:00
|
|
|
* If `option_anchor_outputs` applies to the commitment
|
|
|
|
* transaction, also subtract two times the fixed anchor size
|
|
|
|
* of 330 sats from the funder (either `to_local` or
|
|
|
|
* `to_remote`).
|
|
|
|
*/
|
|
|
|
if (option_anchor_outputs
|
|
|
|
&& !amount_sat_add(&base_fee, base_fee, AMOUNT_SAT(660))) {
|
|
|
|
*err_reason = "Funder cannot afford anchor outputs";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-08-28 18:02:01 +02:00
|
|
|
/* BOLT #3:
|
|
|
|
*
|
|
|
|
* 3. Subtract this base fee from the funder (either `to_local` or
|
2020-08-20 08:49:47 +02:00
|
|
|
* `to_remote`).
|
|
|
|
* If `option_anchor_outputs` applies to the commitment transaction,
|
|
|
|
* also subtract two times the fixed anchor size of 330 sats from the
|
|
|
|
* funder (either `to_local` or `to_remote`).
|
2017-08-28 18:02:01 +02:00
|
|
|
*/
|
2019-09-09 18:11:24 +02:00
|
|
|
if (!try_subtract_fee(opener, side, base_fee, &self_pay, &other_pay)) {
|
2018-03-18 05:41:32 +01:00
|
|
|
/* BOLT #2:
|
|
|
|
*
|
|
|
|
* The receiving node MUST fail the channel if:
|
|
|
|
*...
|
2018-06-17 12:13:44 +02:00
|
|
|
* - it considers `feerate_per_kw` too small for timely
|
|
|
|
* processing or unreasonably large.
|
2018-03-18 05:41:32 +01:00
|
|
|
*/
|
2019-04-03 10:20:36 +02:00
|
|
|
*err_reason = "Funder cannot afford fee on initial commitment transaction";
|
2018-03-18 05:41:32 +01:00
|
|
|
status_unusual("Funder cannot afford fee"
|
|
|
|
" on initial commitment transaction");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-06-17 12:13:44 +02:00
|
|
|
/* FIXME, should be in #2:
|
2018-03-18 05:41:32 +01:00
|
|
|
*
|
|
|
|
* The receiving node MUST fail the channel if:
|
|
|
|
*...
|
|
|
|
* - both `to_local` and `to_remote` amounts for the initial
|
|
|
|
* commitment transaction are less than or equal to
|
|
|
|
* `channel_reserve_satoshis`.
|
|
|
|
*/
|
2019-02-21 04:45:55 +01:00
|
|
|
if (!amount_msat_greater_sat(self_pay, self_reserve)
|
|
|
|
&& !amount_msat_greater_sat(other_pay, self_reserve)) {
|
2019-04-03 10:20:36 +02:00
|
|
|
*err_reason = "Neither self amount nor other amount exceed reserve on "
|
|
|
|
"initial commitment transaction";
|
2019-02-21 04:45:55 +01:00
|
|
|
status_unusual("Neither self amount %s"
|
|
|
|
" nor other amount %s"
|
|
|
|
" exceed reserve %s"
|
2018-03-18 05:41:32 +01:00
|
|
|
" on initial commitment transaction",
|
2019-02-21 04:45:55 +01:00
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
&self_pay),
|
|
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
|
|
&other_pay),
|
|
|
|
type_to_string(tmpctx, struct amount_sat,
|
|
|
|
&self_reserve));
|
2018-03-18 05:41:32 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-08-28 18:02:01 +02:00
|
|
|
|
2020-08-13 19:47:02 +02:00
|
|
|
/* Worst-case sizing: both to-local and to-remote outputs + anchors. */
|
|
|
|
tx = bitcoin_tx(ctx, chainparams, 1, untrimmed + 4, 0);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* This could be done in a single loop, but we follow the BOLT
|
|
|
|
* literally to make comments in test vectors clearer. */
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2020-08-20 08:49:47 +02:00
|
|
|
* 4. For every offered HTLC, if it is not trimmed, add an
|
2017-08-28 18:02:01 +02:00
|
|
|
* [offered HTLC output](#offered-htlc-outputs).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2020-08-20 08:49:47 +02:00
|
|
|
* 5. For every received HTLC, if it is not trimmed, add an
|
2017-08-28 18:02:01 +02:00
|
|
|
* [received HTLC output](#received-htlc-outputs).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2020-08-20 08:49:47 +02:00
|
|
|
* 6. If the `to_local` amount is greater or equal to
|
2017-08-28 18:02:01 +02:00
|
|
|
* `dust_limit_satoshis`, add a [`to_local`
|
2018-09-20 22:19:38 +02:00
|
|
|
* output](#to_local-output).
|
2017-08-28 18:02:01 +02:00
|
|
|
*/
|
2019-02-21 04:45:55 +01:00
|
|
|
if (amount_msat_greater_eq_sat(self_pay, dust_limit)) {
|
2021-06-16 19:44:51 +02:00
|
|
|
u8 *wscript = to_self_wscript(tmpctx,
|
|
|
|
to_self_delay, csv_lock,
|
|
|
|
keyset);
|
2019-03-15 16:59:56 +01:00
|
|
|
amount = amount_msat_to_sat_round_down(self_pay);
|
|
|
|
int pos = bitcoin_tx_add_output(
|
2020-05-21 03:57:00 +02:00
|
|
|
tx, scriptpubkey_p2wsh(tx, wscript), wscript, amount);
|
2019-03-15 16:59:56 +01:00
|
|
|
assert(pos == n);
|
2020-05-07 02:43:34 +02:00
|
|
|
output_order[n] = dummy_local;
|
2017-08-28 18:02:01 +02:00
|
|
|
n++;
|
2020-08-13 19:47:02 +02:00
|
|
|
to_local = true;
|
|
|
|
} else
|
|
|
|
to_local = false;
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2020-08-20 08:49:47 +02:00
|
|
|
* 7. If the `to_remote` amount is greater or equal to
|
2017-08-28 18:02:01 +02:00
|
|
|
* `dust_limit_satoshis`, add a [`to_remote`
|
2018-09-20 22:19:38 +02:00
|
|
|
* output](#to_remote-output).
|
2017-08-28 18:02:01 +02:00
|
|
|
*/
|
2019-02-21 04:45:55 +01:00
|
|
|
if (amount_msat_greater_eq_sat(other_pay, dust_limit)) {
|
2020-08-20 08:49:47 +02:00
|
|
|
/* BOLT #3:
|
2017-08-28 18:02:01 +02:00
|
|
|
*
|
2020-08-13 19:48:02 +02:00
|
|
|
* If `option_anchor_outputs` applies to the commitment
|
|
|
|
* transaction, the `to_remote` output is encumbered by a one
|
|
|
|
* block csv lock.
|
|
|
|
* <remote_pubkey> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY
|
2017-08-28 18:02:01 +02:00
|
|
|
*
|
2020-08-13 19:48:02 +02:00
|
|
|
*...
|
|
|
|
* Otherwise, this output is a simple P2WPKH to `remotepubkey`.
|
2017-08-28 18:02:01 +02:00
|
|
|
*/
|
2020-08-13 19:48:02 +02:00
|
|
|
u8 *scriptpubkey;
|
|
|
|
int pos;
|
|
|
|
|
2019-03-15 16:59:56 +01:00
|
|
|
amount = amount_msat_to_sat_round_down(other_pay);
|
2020-08-13 19:48:02 +02:00
|
|
|
if (option_anchor_outputs) {
|
|
|
|
scriptpubkey = scriptpubkey_p2wsh(tmpctx,
|
2021-06-16 19:44:51 +02:00
|
|
|
anchor_to_remote_redeem(tmpctx, &keyset->other_payment_key, csv_lock));
|
2020-08-13 19:48:02 +02:00
|
|
|
} else {
|
|
|
|
scriptpubkey = scriptpubkey_p2wpkh(tmpctx,
|
|
|
|
&keyset->other_payment_key);
|
|
|
|
}
|
|
|
|
pos = bitcoin_tx_add_output(tx, scriptpubkey, NULL, amount);
|
2019-03-15 16:59:56 +01:00
|
|
|
assert(pos == n);
|
2020-05-07 02:43:34 +02:00
|
|
|
output_order[n] = dummy_remote;
|
2017-08-28 18:02:01 +02:00
|
|
|
n++;
|
2020-08-13 19:47:02 +02:00
|
|
|
to_remote = true;
|
|
|
|
} else
|
|
|
|
to_remote = false;
|
|
|
|
|
2020-08-20 08:49:47 +02:00
|
|
|
/* BOLT #3:
|
|
|
|
* 8. If `option_anchor_outputs` applies to the commitment transaction:
|
|
|
|
* * if `to_local` exists or there are untrimmed HTLCs, add a
|
2021-02-22 02:58:26 +01:00
|
|
|
* [`to_local_anchor` output]...
|
2020-08-20 08:49:47 +02:00
|
|
|
* * if `to_remote` exists or there are untrimmed HTLCs, add a
|
2021-02-22 02:58:26 +01:00
|
|
|
* [`to_remote_anchor` output]
|
2020-08-20 08:49:47 +02:00
|
|
|
*/
|
2020-08-13 19:47:02 +02:00
|
|
|
if (option_anchor_outputs) {
|
|
|
|
if (to_local || untrimmed != 0) {
|
|
|
|
tx_add_anchor_output(tx, &funding_key[side]);
|
|
|
|
output_order[n] = NULL;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (to_remote || untrimmed != 0) {
|
|
|
|
tx_add_anchor_output(tx, &funding_key[!side]);
|
|
|
|
output_order[n] = NULL;
|
|
|
|
n++;
|
|
|
|
}
|
2017-08-28 18:02:01 +02:00
|
|
|
}
|
|
|
|
|
2019-03-25 11:35:56 +01:00
|
|
|
assert(n <= tx->wtx->num_outputs);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2020-08-20 08:49:47 +02:00
|
|
|
* 9. Sort the outputs into [BIP 69+CLTV
|
2017-08-28 18:02:01 +02:00
|
|
|
* order](#transaction-input-and-output-ordering)
|
|
|
|
*/
|
2020-05-07 02:43:34 +02:00
|
|
|
permute_outputs(tx, NULL, output_order);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
|
|
|
* ## Commitment Transaction
|
|
|
|
*
|
|
|
|
* * version: 2
|
|
|
|
*/
|
2019-03-05 14:28:29 +01:00
|
|
|
assert(tx->wtx->version == 2);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
2019-01-14 03:22:05 +01:00
|
|
|
* * locktime: upper 8 bits are 0x20, lower 24 bits are the
|
|
|
|
* lower 24 bits of the obscured commitment number
|
2017-08-28 18:02:01 +02:00
|
|
|
*/
|
2020-05-22 05:16:11 +02:00
|
|
|
bitcoin_tx_set_locktime(tx,
|
|
|
|
(0x20000000 | (obscured_commitment_number & 0xFFFFFF)));
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
/* BOLT #3:
|
|
|
|
*
|
|
|
|
* * txin count: 1
|
|
|
|
* * `txin[0]` outpoint: `txid` and `output_index` from
|
|
|
|
* `funding_created` message
|
2019-01-14 03:22:05 +01:00
|
|
|
* * `txin[0]` sequence: upper 8 bits are 0x80, lower 24 bits are upper 24 bits of the obscured commitment number
|
2019-03-15 16:59:56 +01:00
|
|
|
* * `txin[0]` script bytes: 0
|
2017-08-28 18:02:01 +02:00
|
|
|
*/
|
2019-03-15 16:59:56 +01:00
|
|
|
sequence = (0x80000000 | ((obscured_commitment_number>>24) & 0xFFFFFF));
|
2020-05-21 21:46:19 +02:00
|
|
|
bitcoin_tx_add_input(tx, funding_txid, funding_txout, sequence,
|
|
|
|
NULL, funding, NULL, funding_wscript);
|
2017-08-28 18:02:01 +02:00
|
|
|
|
2020-05-07 02:43:34 +02:00
|
|
|
if (direct_outputs != NULL) {
|
|
|
|
direct_outputs[LOCAL] = direct_outputs[REMOTE] = NULL;
|
|
|
|
for (size_t i = 0; i < tx->wtx->num_outputs; i++) {
|
|
|
|
if (output_order[i] == dummy_local)
|
|
|
|
direct_outputs[LOCAL] = &tx->wtx->outputs[i];
|
|
|
|
else if (output_order[i] == dummy_remote)
|
|
|
|
direct_outputs[REMOTE] = &tx->wtx->outputs[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This doesn't reorder outputs, so we can do this after mapping outputs. */
|
2020-02-08 21:26:55 +01:00
|
|
|
bitcoin_tx_finalize(tx);
|
2020-05-07 02:43:34 +02:00
|
|
|
|
2019-03-15 16:59:56 +01:00
|
|
|
assert(bitcoin_tx_check(tx));
|
2017-08-28 18:02:01 +02:00
|
|
|
|
|
|
|
return tx;
|
|
|
|
}
|