From 2e9c387f459f16fdf22c0f9e8cb66fc520a3f798 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 16 Jun 2020 17:09:21 -0500 Subject: [PATCH] coin_moves: update withdrawal logic to account for 'variable owner' txs Our existing coin_moves tracking logic assumed that any tx we had an input in belonged to *all* of our wallet (not a bad assumption as long as there was no way to update a tx that spends our wallets) Now that we've got `signpsbt` implemented, however, we need to be careful about how we account for withdrawals. For now we do a best guess at what the feerate is, and lump all of our spent outputs as a 'withdrawal' when it's impossible to disambiguate --- bitcoin/test/run-bitcoin_block_from_hex.c | 3 + bitcoin/test/run-tx-encode.c | 3 + cli/test/run-large-input.c | 3 + cli/test/run-remove-hint.c | 3 + common/test/run-bigsize.c | 3 + common/test/run-cryptomsg.c | 3 + common/test/run-derive_basepoints.c | 3 + common/test/run-features.c | 3 + common/test/run-gossip_rcvd_filter.c | 3 + common/test/run-ip_port_parsing.c | 3 + common/test/run-json_remove.c | 3 + common/test/run-key_derive.c | 3 + common/test/run-lock.c | 3 + common/test/run-softref.c | 3 + common/test/run-sphinx.c | 3 + connectd/test/run-initiator-success.c | 3 + connectd/test/run-responder-success.c | 3 + lightningd/chaintopology.c | 106 +++++++++++++--------- tests/test_wallet.py | 41 ++++++++- 19 files changed, 152 insertions(+), 46 deletions(-) diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index 6cf84bc2f..52d13fa32 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -22,6 +22,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index 6bc269a1d..30238ef7e 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -23,6 +23,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index cee090de4..2d4d01cf8 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -47,6 +47,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index f621fea31..0207dc5f2 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -50,6 +50,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-bigsize.c b/common/test/run-bigsize.c index f9e44f8b3..33b3640d2 100644 --- a/common/test/run-bigsize.c +++ b/common/test/run-bigsize.c @@ -27,6 +27,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-cryptomsg.c b/common/test/run-cryptomsg.c index dced33197..9278076e5 100644 --- a/common/test/run-cryptomsg.c +++ b/common/test/run-cryptomsg.c @@ -22,6 +22,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-derive_basepoints.c b/common/test/run-derive_basepoints.c index 572bdc5c7..733c28414 100644 --- a/common/test/run-derive_basepoints.c +++ b/common/test/run-derive_basepoints.c @@ -23,6 +23,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-features.c b/common/test/run-features.c index 5e7ea09cb..ef0f3f28c 100644 --- a/common/test/run-features.c +++ b/common/test/run-features.c @@ -22,6 +22,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-gossip_rcvd_filter.c b/common/test/run-gossip_rcvd_filter.c index 7afc47b1d..531ea29a0 100644 --- a/common/test/run-gossip_rcvd_filter.c +++ b/common/test/run-gossip_rcvd_filter.c @@ -19,6 +19,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 0a7df6bc6..5439ddca1 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -21,6 +21,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 572274783..4c318216a 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -19,6 +19,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-key_derive.c b/common/test/run-key_derive.c index f48e58cbc..76a7e3068 100644 --- a/common/test/run-key_derive.c +++ b/common/test/run-key_derive.c @@ -24,6 +24,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-lock.c b/common/test/run-lock.c index 6b3255296..d29f323e3 100644 --- a/common/test/run-lock.c +++ b/common/test/run-lock.c @@ -23,6 +23,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-softref.c b/common/test/run-softref.c index 731485a67..d9f7c381e 100644 --- a/common/test/run-softref.c +++ b/common/test/run-softref.c @@ -20,6 +20,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index bf4ea5001..a31e55a31 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -36,6 +36,9 @@ void amount_msat_from_u64(struct amount_msat *msat UNNEEDED, u64 millisatoshis U /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index 4f2d27c77..953a65b19 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -26,6 +26,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index f93d6ecc8..29fdab58a 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -26,6 +26,9 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_sat_eq */ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less called!\n"); abort(); } /* Generated stub for amount_sat_sub */ bool amount_sat_sub(struct amount_sat *val UNNEEDED, struct amount_sat a UNNEEDED, diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index a2e60a8e6..fa32bc410 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -662,22 +662,24 @@ static void updates_complete(struct chain_topology *topo) next_topology_timer(topo); } -static void record_output_spend(struct lightningd *ld, - const struct bitcoin_txid *txid, - const struct bitcoin_txid *utxo_txid, - u32 vout, u32 blockheight, - struct amount_sat *input_amt) +static void record_utxo_spent(struct lightningd *ld, + const struct bitcoin_txid *txid, + const struct bitcoin_txid *utxo_txid, + u32 vout, u32 blockheight, + struct amount_sat *input_amt) { struct utxo *utxo; struct chain_coin_mvt *mvt; u8 *ctx = tal(NULL, u8); utxo = wallet_utxo_get(ctx, ld->wallet, utxo_txid, vout); - if (!utxo) + if (!utxo) { log_broken(ld->log, "No record of utxo %s:%d", type_to_string(tmpctx, struct bitcoin_txid, utxo_txid), vout); + return; + } *input_amt = utxo->amount; mvt = new_coin_spend_track(ctx, txid, utxo_txid, vout, blockheight); @@ -685,23 +687,14 @@ static void record_output_spend(struct lightningd *ld, tal_free(ctx); } -static void record_tx_outs_and_fees(struct lightningd *ld, const struct bitcoin_tx *tx, - struct bitcoin_txid *txid, u32 blockheight, - struct amount_sat inputs_total) +static void record_outputs_as_withdraws(const tal_t *ctx, + struct lightningd *ld, + const struct bitcoin_tx *tx, + struct bitcoin_txid *txid, + u32 blockheight) { - struct amount_sat fee; struct chain_coin_mvt *mvt; - size_t i; - u8 *ctx = tal(NULL, u8); - - if (!tx) - log_broken(ld->log, "We have no record of transaction %s", - type_to_string(ctx, struct bitcoin_txid, txid)); - - /* We record every output on this transaction as a withdraw */ - /* FIXME: collaborative tx will need to keep track of which - * outputs are ours */ - for (i = 0; i < tx->wtx->num_outputs; i++) { + for (size_t i = 0; i < tx->wtx->num_outputs; i++) { struct amount_asset asset; struct amount_sat outval; if (elements_tx_output_is_fee(tx, i)) @@ -709,18 +702,55 @@ static void record_tx_outs_and_fees(struct lightningd *ld, const struct bitcoin_ asset = bitcoin_tx_output_get_amount(tx, i); assert(amount_asset_is_main(&asset)); outval = amount_asset_to_sat(&asset); - mvt = new_coin_withdrawal_sat(ctx, "wallet", txid, txid, - i, blockheight, outval); + mvt = new_coin_withdrawal_sat(ctx, "wallet", txid, + txid, i, blockheight, + outval); notify_chain_mvt(ld, mvt); } +} - fee = bitcoin_tx_compute_fee_w_inputs(tx, inputs_total); +static void record_tx_outs_and_fees(struct lightningd *ld, + const struct bitcoin_tx *tx, + struct bitcoin_txid *txid, + u32 blockheight, + struct amount_sat inputs_total, + bool our_tx) +{ + struct amount_sat fee, out_val; + struct chain_coin_mvt *mvt; + bool ok; + struct wally_psbt *psbt = NULL; + u8 *ctx = tal(NULL, u8); + + /* We own every input on this tx, so track withdrawals precisely */ + if (our_tx) { + record_outputs_as_withdraws(ctx, ld, tx, txid, blockheight); + fee = bitcoin_tx_compute_fee_w_inputs(tx, inputs_total); + goto log_fee; + } + + /* FIXME: look up stashed psbt! */ + if (!psbt) { + fee = bitcoin_tx_compute_fee_w_inputs(tx, inputs_total); + ok = amount_sat_sub(&out_val, inputs_total, fee); + assert(ok); + + /* We don't have detailed withdrawal info for this tx, + * so we log the wallet withdrawal as a single entry */ + mvt = new_coin_withdrawal_sat(ctx, "wallet", txid, NULL, + 0, blockheight, out_val); + notify_chain_mvt(ld, mvt); + goto log_fee; + } + + fee = AMOUNT_SAT(0); /* Note that to figure out the *total* 'onchain' * cost of a channel, you'll want to also include * fees logged here, to the 'wallet' account (for funding tx). * You can do this in post by accounting for any 'chain_fees' logged for * the funding txid when looking at a channel. */ +log_fee: notify_chain_mvt(ld, new_coin_chain_fees_sat(ctx, "wallet", txid, blockheight, fee)); @@ -736,7 +766,7 @@ static void topo_update_spends(struct chain_topology *topo, struct block *b) const struct short_channel_id *scid; for (size_t i = 0; i < tal_count(b->full_txs); i++) { const struct bitcoin_tx *tx = b->full_txs[i]; - bool our_tx = false; + bool our_tx = true, includes_our_spend = false; struct bitcoin_txid txid; struct amount_sat inputs_total = AMOUNT_SAT(0); @@ -758,34 +788,22 @@ static void topo_update_spends(struct chain_topology *topo, struct block *b) tal_free(scid); } - our_tx |= our_spend; + our_tx &= our_spend; + includes_our_spend |= our_spend; if (our_spend) { struct amount_sat input_amt; bool ok; - record_output_spend(topo->ld, &txid, &outpoint_txid, - input->index, b->height, &input_amt); + record_utxo_spent(topo->ld, &txid, &outpoint_txid, + input->index, b->height, &input_amt); ok = amount_sat_add(&inputs_total, inputs_total, input_amt); assert(ok); - } else if (our_tx) - log_broken(topo->ld->log, "Recording fee spend for tx %s but " - "our wallet did not contribute input %s:%d", - type_to_string(tmpctx, struct bitcoin_txid, - &txid), - type_to_string(tmpctx, struct bitcoin_txid, - &outpoint_txid), - input->index); - + } } - /* For now we assume that if one of the spent utxos - * in this tx is 'ours', that we own all of the - * utxos and therefore paid all of the fees - * FIXME: update once interactive tx construction - * is a reality */ - if (our_tx) + if (includes_our_spend) record_tx_outs_and_fees(topo->ld, tx, &txid, - b->height, inputs_total); + b->height, inputs_total, our_tx); } } diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 56e9bc1ac..ab1640040 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -6,7 +6,7 @@ from flaky import flaky # noqa: F401 from pyln.client import RpcError, Millisatoshi from utils import ( only_one, wait_for, sync_blockheight, EXPERIMENTAL_FEATURES, COMPAT, - VALGRIND + VALGRIND, check_coin_moves ) import os @@ -568,7 +568,9 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): """ amount = 1000000 total_outs = 12 - l1 = node_factory.get_node(feerates=(7500, 7500, 7500, 7500)) + coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') + l1 = node_factory.get_node(options={'plugin': coin_mvt_plugin}, + feerates=(7500, 7500, 7500, 7500)) l2 = node_factory.get_node() addr = chainparams['example_addr'] @@ -677,6 +679,41 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): with pytest.raises(RpcError, match=r"should be a PSBT, not"): l1.rpc.signpsbt(modded_psbt) + wallet_coin_mvts = [ + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + # Nicely splits out withdrawals and chain fees, because it's all our tx + {'type': 'chain_mvt', 'credit': 0, 'debit': 988255000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 11745000, 'tag': 'chain_fees'}, + {'type': 'chain_mvt', 'credit': 988255000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, + # Note that this is technically wrong since we paid 11745sat in fees + # but since it includes inputs / outputs from a second node, we can't + # do proper acccounting for it. + {'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'}, + {'type': 'chain_mvt', 'credit': 988255000, 'debit': 0, 'tag': 'deposit'}, + ] + check_coin_moves(l1, 'wallet', wallet_coin_mvts) + def test_txsend(node_factory, bitcoind, chainparams): amount = 1000000