diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 504f3b9fbf6f..c28d71f62ced 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -40,6 +40,7 @@ static const char *mvt_tags[] = { "leased", "stealable", "channel_proposed", + "splice", }; const char *mvt_tag_str(enum mvt_tag tag) @@ -177,19 +178,31 @@ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, } struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, + const struct channel_id *chan_id, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val, - u32 output_count) + u32 output_count, + bool is_splice) { - return new_chain_coin_mvt(ctx, NULL, txid, + struct chain_coin_mvt *mvt; + enum mvt_tag *tags = new_tag_arr(NULL, CHANNEL_CLOSE); + + if (is_splice) + tal_arr_expand(&tags, SPLICE); + + mvt = new_chain_coin_mvt(ctx, NULL, txid, out, NULL, blockheight, - take(new_tag_arr(NULL, CHANNEL_CLOSE)), + take(tags), amount, false, output_val, output_count); + if (chan_id) + mvt->account_name = fmt_channel_id(mvt, chan_id); + + return mvt; } struct chain_coin_mvt *new_coin_channel_open_proposed(const tal_t *ctx, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index d35340073cff..9b34c69a9a38 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -14,7 +14,7 @@ enum mvt_type { CHANNEL_MVT = 1, }; -#define NUM_MVT_TAGS (CHANNEL_PROPOSED + 1) +#define NUM_MVT_TAGS (SPLICE + 1) enum mvt_tag { DEPOSIT = 0, WITHDRAWAL = 1, @@ -40,6 +40,7 @@ enum mvt_tag { LEASED = 21, STEALABLE = 22, CHANNEL_PROPOSED = 23, + SPLICE = 24, }; struct channel_coin_mvt { @@ -181,13 +182,15 @@ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, NON_NULL_ARGS(2); struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, + const struct channel_id *chan_id, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val, - u32 output_count) - NON_NULL_ARGS(2, 3); + u32 output_count, + bool is_splice) + NON_NULL_ARGS(3, 4); struct chain_coin_mvt *new_coin_channel_open_proposed(const tal_t *ctx, const struct channel_id *chan_id, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index ad354de41f0b..67f29ef3aedd 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -912,6 +912,27 @@ static void handle_update_inflight(struct lightningd *ld, wallet_inflight_save(ld->wallet, inflight); } +static void channel_record_splice(struct channel *channel, + struct amount_msat orig_our_msats, + struct amount_sat orig_funding_sats, + struct bitcoin_outpoint *funding, + u32 blockheight, struct bitcoin_txid *txid, const struct channel_inflight *inflight) +{ + struct chain_coin_mvt *mvt; + u32 output_count; + + output_count = inflight->funding_psbt->num_outputs; + mvt = new_coin_channel_close(tmpctx, &channel->cid, + txid, + funding, + blockheight, + orig_our_msats, + orig_funding_sats, + output_count, + /* is_splice = */true); + notify_chain_mvt(channel->peer->ld, mvt); +} + void channel_record_open(struct channel *channel, u32 blockheight, bool record_push) { struct chain_coin_mvt *mvt; @@ -1039,7 +1060,9 @@ bool channel_on_channel_ready(struct channel *channel, static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) { - struct amount_sat funding_sats; + struct amount_sat funding_sats, prev_funding_sats; + struct amount_msat prev_our_msats; + struct bitcoin_outpoint prev_funding_out; s64 splice_amnt; struct channel_inflight *inflight; struct bitcoin_txid locked_txid; @@ -1054,16 +1077,22 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) return; } - channel->our_msat.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ - channel->msat_to_us_min.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ - channel->msat_to_us_max.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ - inflight = channel_inflight_find(channel, &locked_txid); if(!inflight) channel_internal_error(channel, "Unable to load inflight for" " locked_txid %s", fmt_bitcoin_txid(tmpctx, &locked_txid)); + /* Stash prev funding data so we can log it after scid is updated + * (to get the blockheight) */ + prev_our_msats = channel->our_msat; + prev_funding_sats = channel->funding_sats; + prev_funding_out = channel->funding; + + channel->our_msat.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + channel->msat_to_us_min.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + channel->msat_to_us_max.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + wallet_htlcsigs_confirm_inflight(channel->peer->ld->wallet, channel, &inflight->funding->outpoint); @@ -1085,6 +1114,16 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) /* That freed watchers in inflights: now watch funding tx */ channel_watch_funding(channel->peer->ld, channel); + /* Log that funding output has been spent */ + channel_record_splice(channel, + prev_our_msats, + prev_funding_sats, + &prev_funding_out, + channel->scid ? + short_channel_id_blocknum(*channel->scid) : 0, + &locked_txid, + inflight); + /* Put the successful inflight back in as a memory-only object. * peer_control's funding_spent function will pick this up and clean up * our inflight. diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index e9d61f37e094..d721e61f0a8e 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -3463,14 +3463,15 @@ int main(int argc, char *argv[]) FUNDING_OUTPUT, NULL, NULL, NULL); /* Record funding output spent */ - send_coin_mvt(take(new_coin_channel_close(NULL, &tx->txid, + send_coin_mvt(take(new_coin_channel_close(NULL, NULL, &tx->txid, &funding, tx_blockheight, our_msat, funding_sats, is_elements(chainparams) ? /* Minus 1, fee output */ tal_count(tx->outputs) - 1 : - tal_count(tx->outputs)))); + tal_count(tx->outputs), + /* is_splice? */ false))); status_debug("Remote per-commit point: %s", fmt_pubkey(tmpctx, &remote_per_commit_point)); diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 8be39f6c9635..30a31b0b89f4 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -103,12 +103,14 @@ void memleak_status_broken(void *unused UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "memleak_status_broken called!\n"); abort(); } /* Generated stub for new_coin_channel_close */ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, + const struct channel_id *chan_id UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, const struct amount_msat amount UNNEEDED, const struct amount_sat output_val UNNEEDED, - u32 output_count) + u32 output_count UNNEEDED, + bool is_splice) { fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } /* Generated stub for new_coin_external_deposit */ diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index f390399264f5..bc10c960ad44 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -153,12 +153,14 @@ void memleak_status_broken(void *unused UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "memleak_status_broken called!\n"); abort(); } /* Generated stub for new_coin_channel_close */ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, + const struct channel_id *chan_id UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, const struct amount_msat amount UNNEEDED, const struct amount_sat output_val UNNEEDED, - u32 output_count) + u32 output_count UNNEEDED, + bool is_splice) { fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } /* Generated stub for new_coin_external_deposit */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 4735e335954f..33ffb87d1237 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -671,6 +671,7 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev->payment_id = NULL; chain_ev->ignored = false; chain_ev->stealable = false; + chain_ev->splice_close = false; chain_ev->desc = NULL; /* Update the account info too */ @@ -1458,9 +1459,11 @@ parse_and_log_chain_move(struct command *cmd, e->ignored = false; e->stealable = false; + e->splice_close = false; for (size_t i = 0; i < tal_count(tags); i++) { e->ignored |= tags[i] == IGNORED; e->stealable |= tags[i] == STEALABLE; + e->splice_close |= tags[i] == SPLICE; } db_begin_transaction(db); diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index a9c5f3a6a71c..4d9785d02a26 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -34,6 +34,10 @@ struct chain_event { * we'll need to watch it for longer */ bool stealable; + /* Is this chain event because of a splice + * confirmation? */ + bool splice_close; + /* Is this a rebalance event? */ bool rebalance; diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index b8f3fc32ddb7..06943af912ec 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -99,6 +99,7 @@ static struct migration db_migrations[] = { {SQL("ALTER TABLE chain_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channel_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channel_events ADD rebalance_id BIGINT DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE chain_events ADD spliced INTEGER DEFAULT 0;"), NULL}, {NULL, migration_remove_dupe_lease_fees} }; diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 00f388fd81cc..c165c435db6d 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -62,6 +62,8 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st else e->desc = NULL; + e->splice_close = db_col_int(stmt, "e.spliced") == 1; + return e; } @@ -162,6 +164,7 @@ struct chain_event **list_chain_events_timebox(const tal_t *ctx, ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -204,6 +207,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -239,6 +243,7 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -548,7 +553,9 @@ struct account *find_close_account(const tal_t *ctx, " ON e.account_id = a.id" " WHERE " " e.tag = ?" - " AND e.spending_txid = ?")); + " AND e.spending_txid = ?" + /* ignore splicing 'close' events */ + " AND e.spliced = 0 ")); db_bind_text(stmt, mvt_tag_str(CHANNEL_CLOSE)); db_bind_txid(stmt, txid); @@ -678,6 +685,7 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -726,6 +734,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -755,6 +764,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -1263,6 +1273,9 @@ void maybe_update_account(struct db *db, *acct->open_event_db_id = e->db_id; break; case CHANNEL_CLOSE: + /* Splices dont count as closes */ + if (e->splice_close) + break; updated = true; acct->closed_event_db_id = tal(acct, u64); *acct->closed_event_db_id = e->db_id; @@ -1294,6 +1307,7 @@ void maybe_update_account(struct db *db, case TO_MINER: case LEASE_FEE: case STEALABLE: + case SPLICE: /* Ignored */ break; } @@ -1304,7 +1318,7 @@ void maybe_update_account(struct db *db, acct->peer_id = tal_dup(acct, struct node_id, peer_id); } - if (closed_count > 0) { + if (!e->splice_close && closed_count > 0) { updated = true; acct->closed_count = closed_count; } @@ -1423,6 +1437,7 @@ static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db ", e.ignored" ", e.stealable" ", e.ev_desc" + ", e.spliced" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -2002,9 +2017,10 @@ bool log_chain_event(struct db *db, ", ignored" ", stealable" ", ev_desc" + ", spliced" ")" " VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, acct->db_id); if (e->origin_acct) @@ -2037,6 +2053,7 @@ bool log_chain_event(struct db *db, db_bind_text(stmt, e->desc); else db_bind_null(stmt); + db_bind_int(stmt, e->splice_close ? 1 : 0); db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 5ebac38ab05e..8f805d413873 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -355,6 +355,8 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) if (e1->desc) CHECK(streq(e1->desc, e2->desc)); + CHECK(e1->splice_close == e2->splice_close); + return true; } @@ -406,6 +408,7 @@ static struct chain_event *make_chain_event(const tal_t *ctx, ev->blockheight = blockheight; ev->ignored = false; ev->stealable = false; + ev->splice_close = false; ev->desc = tal_fmt(ev, "hello hello"); memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); ev->outpoint.n = outnum; @@ -1084,6 +1087,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev1->blockheight = 1919191; ev1->ignored = false; ev1->stealable = false; + ev1->splice_close = false; memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev1->outpoint.n = 1; ev1->spending_txid = tal(ctx, struct bitcoin_txid); @@ -1105,6 +1109,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev2->blockheight = 1919191; ev2->ignored = false; ev2->stealable = false; + ev2->splice_close = false; memset(&ev2->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev2->outpoint.n = 1; ev2->spending_txid = NULL; @@ -1124,6 +1129,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev3->blockheight = 3939393; ev3->ignored = false; ev3->stealable = false; + ev3->splice_close = false; memset(&ev3->outpoint.txid, 'E', sizeof(struct bitcoin_txid)); ev3->outpoint.n = 1; ev3->spending_txid = tal(ctx, struct bitcoin_txid); @@ -1351,6 +1357,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) ev1->blockheight = 1919191; ev1->ignored = false; ev1->stealable = false; + ev1->splice_close = false; memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev1->outpoint.n = 1; ev1->spending_txid = tal(ctx, struct bitcoin_txid); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index b6f0791a9231..8d45c180e53f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1002,7 +1002,7 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, u8 *towire_announcement_signatures(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED, struct short_channel_id short_channel_id UNNEEDED, const secp256k1_ecdsa_signature *node_signature UNNEEDED, const secp256k1_ecdsa_signature *bitcoin_signature UNNEEDED) { fprintf(stderr, "towire_announcement_signatures called!\n"); abort(); } /* Generated stub for towire_channel_reestablish */ -u8 *towire_channel_reestablish(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED, u64 next_commitment_number UNNEEDED, u64 next_revocation_number UNNEEDED, const struct secret *your_last_per_commitment_secret UNNEEDED, const struct pubkey *my_current_per_commitment_point UNNEEDED, const struct tlv_channel_reestablish_tlvs *channel_reestablish UNNEEDED) +u8 *towire_channel_reestablish(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED, u64 next_commitment_number UNNEEDED, u64 next_revocation_number UNNEEDED, const struct secret *your_last_per_commitment_secret UNNEEDED, const struct pubkey *my_current_per_commitment_point UNNEEDED, const struct tlv_channel_reestablish_tlvs *tlvs UNNEEDED) { fprintf(stderr, "towire_channel_reestablish called!\n"); abort(); } /* Generated stub for towire_channeld_dev_memleak */ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED)