lightningd: use channel_coin_mvt / chain_coin_mvt directly for notifications.
What changed, and why it matters
This is a routine internal code cleanup in Core Lightning. It changes how the node reports coin movements (on-chain and channel transactions) to plugins, switching from a single generic data structure to two more specific ones. There is no indication this fixes a security bug or introduces a vulnerability.
No security action required. Treat as a normal refactoring commit. Reviewers may optionally verify that the emitted coin_movement JSON notification fields remain backward-compatible for plugins.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors coin-movement notifications. Previously, channel_coin_mvt and chain_coin_mvt were converted into a generic coin_mvt struct via finalize_channel_mvt/finalize_chain_mvt, then emitted through notify_coin_mvt. The patch removes those wrappers and the generic notify_coin_mvt, and instead emits channel_coin_mvt and chain_coin_mvt directly through notify_channel_mvt and notify_chain_mvt. The JSON serialization is split into chain_movement_notification_serialize and channel_movement_notification_serialize. Callers in onchain_control.c, peer_htlcs.c, and walletrpc.c are updated to include notification.h and call the new functions. The observable JSON output is intended to remain equivalent.
Changed components
lightningd coin-movement notification subsystemlightningd/coin_mvts.clightningd/notification.clightningd/onchain_control.clightningd/peer_htlcs.cwallet/walletrpc.cInspect captured patch +86 / −97
diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c
index df91cabb..da6137b8 100644
--- a/lightningd/coin_mvts.c
+++ b/lightningd/coin_mvts.c
@@ -6,29 +6,6 @@
#include <lightningd/peer_control.h>
-void notify_channel_mvt(struct lightningd *ld, const struct channel_coin_mvt *mvt)
-{
- const struct coin_mvt *cm;
- u32 timestamp;
-
- timestamp = time_now().ts.tv_sec;
- cm = finalize_channel_mvt(mvt, mvt, chainparams->lightning_hrp,
- timestamp, &ld->our_nodeid);
-
- notify_coin_mvt(ld, cm);
-}
-
-void notify_chain_mvt(struct lightningd *ld, const struct chain_coin_mvt *mvt)
-{
- const struct coin_mvt *cm;
- u32 timestamp;
-
- timestamp = time_now().ts.tv_sec;
- cm = finalize_chain_mvt(mvt, mvt, chainparams->lightning_hrp,
- timestamp, &ld->our_nodeid);
- notify_coin_mvt(ld, cm);
-}
-
struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx,
struct htlc_in *hin,
struct channel *channel)
diff --git a/lightningd/coin_mvts.h b/lightningd/coin_mvts.h
index 90db5317..abdc8315 100644
--- a/lightningd/coin_mvts.h
+++ b/lightningd/coin_mvts.h
@@ -20,9 +20,6 @@ struct balance_snapshot {
struct account_balance **accts;
};
-void notify_channel_mvt(struct lightningd *ld, const struct channel_coin_mvt *mvt);
-void notify_chain_mvt(struct lightningd *ld, const struct chain_coin_mvt *mvt);
-
struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx,
struct htlc_in *hin,
struct channel *channel);
diff --git a/lightningd/notification.c b/lightningd/notification.c
index 1e4a58aa..cb60dabd 100644
--- a/lightningd/notification.c
+++ b/lightningd/notification.c
@@ -445,90 +445,100 @@ void notify_sendpay_failure(struct lightningd *ld,
notify_send(ld, n);
}
-static void json_mvt_id(struct json_stream *stream, enum mvt_type mvt_type,
- const struct mvt_id *id)
-{
- switch (mvt_type) {
- case CHAIN_MVT:
- /* some 'journal entries' don't have a txid */
- if (id->tx_txid)
- json_add_string(stream, "txid",
- fmt_bitcoin_txid(tmpctx,
- id->tx_txid));
- /* some chain ledger entries aren't associated with a utxo
- * e.g. journal updates (due to penalty/state loss) and
- * chain_fee entries */
- if (id->outpoint) {
- json_add_string(stream, "utxo_txid",
- fmt_bitcoin_txid(tmpctx,
- &id->outpoint->txid));
- json_add_u32(stream, "vout", id->outpoint->n);
- }
-
- /* on-chain htlcs include a payment hash */
- if (id->payment_hash)
- json_add_sha256(stream, "payment_hash", id->payment_hash);
- return;
- case CHANNEL_MVT:
- /* push funding / leases don't have a payment_hash */
- if (id->payment_hash)
- json_add_sha256(stream, "payment_hash", id->payment_hash);
- if (id->part_id)
- json_add_u64(stream, "part_id", *id->part_id);
- return;
+static void chain_movement_notification_serialize(struct json_stream *stream,
+ struct lightningd *ld,
+ const struct chain_coin_mvt *chain_mvt)
+{
+ json_add_num(stream, "version", COIN_MVT_VERSION);
+ json_add_string(stream, "type", "chain_mvt");
+ json_add_node_id(stream, "node_id", &ld->our_nodeid);
+ if (chain_mvt->peer_id)
+ json_add_node_id(stream, "peer_id", chain_mvt->peer_id);
+ json_add_string(stream, "account_id", chain_mvt->account_name);
+ if (chain_mvt->originating_acct)
+ json_add_string(stream, "originating_account",
+ chain_mvt->originating_acct);
+ /* some 'journal entries' don't have a txid */
+ if (chain_mvt->tx_txid)
+ json_add_string(stream, "txid",
+ fmt_bitcoin_txid(tmpctx,
+ chain_mvt->tx_txid));
+ /* some chain ledger entries aren't associated with a utxo
+ * e.g. journal updates (due to penalty/state loss) and
+ * chain_fee entries */
+ if (chain_mvt->outpoint) {
+ json_add_string(stream, "utxo_txid",
+ fmt_bitcoin_txid(tmpctx,
+ &chain_mvt->outpoint->txid));
+ json_add_u32(stream, "vout", chain_mvt->outpoint->n);
}
- abort();
+
+ /* on-chain htlcs include a payment hash */
+ if (chain_mvt->payment_hash)
+ json_add_sha256(stream, "payment_hash", chain_mvt->payment_hash);
+ json_add_amount_msat(stream, "credit_msat", chain_mvt->credit);
+ json_add_amount_msat(stream, "debit_msat", chain_mvt->debit);
+
+ json_add_amount_sat_msat(stream,
+ "output_msat", chain_mvt->output_val);
+ if (chain_mvt->output_count > 0)
+ json_add_num(stream, "output_count", chain_mvt->output_count);
+
+ json_array_start(stream, "tags");
+ for (size_t i = 0; i < tal_count(chain_mvt->tags); i++)
+ json_add_string(stream, NULL, mvt_tag_str(chain_mvt->tags[i]));
+ json_array_end(stream);
+
+ json_add_u32(stream, "blockheight", chain_mvt->blockheight);
+ json_add_u32(stream, "timestamp", time_now().ts.tv_sec);
+ json_add_string(stream, "coin_type", chainparams->lightning_hrp);
}
-static void coin_movement_notification_serialize(struct json_stream *stream,
- const struct coin_mvt *mvt)
+static void channel_movement_notification_serialize(struct json_stream *stream,
+ struct lightningd *ld,
+ const struct channel_coin_mvt *chan_mvt)
{
- json_add_num(stream, "version", mvt->version);
- json_add_node_id(stream, "node_id", mvt->node_id);
- if (mvt->peer_id)
- json_add_node_id(stream, "peer_id", mvt->peer_id);
- json_add_string(stream, "type", mvt_type_str(mvt->type));
- json_add_string(stream, "account_id", mvt->account_id);
- if (mvt->originating_acct)
- json_add_string(stream, "originating_account",
- mvt->originating_acct);
- json_mvt_id(stream, mvt->type, &mvt->id);
- json_add_amount_msat(stream, "credit_msat", mvt->credit);
- json_add_amount_msat(stream, "debit_msat", mvt->debit);
-
- /* Only chain movements */
- if (mvt->output_val)
- json_add_amount_sat_msat(stream,
- "output_msat", *mvt->output_val);
- if (mvt->output_count > 0)
- json_add_num(stream, "output_count",
- mvt->output_count);
-
- if (mvt->fees) {
- json_add_amount_msat(stream, "fees_msat", *mvt->fees);
- }
+ json_add_num(stream, "version", COIN_MVT_VERSION);
+ json_add_string(stream, "type", "channel_mvt");
+ json_add_node_id(stream, "node_id", &ld->our_nodeid);
+ json_add_channel_id(stream, "account_id", &chan_mvt->chan_id);
+ /* push funding / leases don't have a payment_hash */
+ if (chan_mvt->payment_hash)
+ json_add_sha256(stream, "payment_hash", chan_mvt->payment_hash);
+ if (chan_mvt->part_id)
+ json_add_u64(stream, "part_id", *chan_mvt->part_id);
+ json_add_amount_msat(stream, "credit_msat", chan_mvt->credit);
+ json_add_amount_msat(stream, "debit_msat", chan_mvt->debit);
+ json_add_amount_msat(stream, "fees_msat", chan_mvt->fees);
json_array_start(stream, "tags");
- for (size_t i = 0; i < tal_count(mvt->tags); i++)
- json_add_string(stream, NULL, mvt_tag_str(mvt->tags[i]));
+ for (size_t i = 0; i < tal_count(chan_mvt->tags); i++)
+ json_add_string(stream, NULL, mvt_tag_str(chan_mvt->tags[i]));
json_array_end(stream);
- if (mvt->type == CHAIN_MVT)
- json_add_u32(stream, "blockheight", mvt->blockheight);
-
- json_add_u32(stream, "timestamp", mvt->timestamp);
- json_add_string(stream, "coin_type", mvt->hrp_name);
+ json_add_u32(stream, "timestamp", time_now().ts.tv_sec);
+ json_add_string(stream, "coin_type", chainparams->lightning_hrp);
}
REGISTER_NOTIFICATION(coin_movement);
-void notify_coin_mvt(struct lightningd *ld,
- const struct coin_mvt *mvt)
+void notify_channel_mvt(struct lightningd *ld,
+ const struct channel_coin_mvt *chan_mvt)
+{
+ struct jsonrpc_notification *n = notify_start(ld, "coin_movement");
+ if (!n)
+ return;
+ channel_movement_notification_serialize(n->stream, ld, chan_mvt);
+ notify_send(ld, n);
+}
+
+void notify_chain_mvt(struct lightningd *ld,
+ const struct chain_coin_mvt *chain_mvt)
{
struct jsonrpc_notification *n = notify_start(ld, "coin_movement");
if (!n)
return;
- coin_movement_notification_serialize(n->stream, mvt);
+ chain_movement_notification_serialize(n->stream, ld, chain_mvt);
notify_send(ld, n);
}
diff --git a/lightningd/notification.h b/lightningd/notification.h
index 8f37cb70..c90f0160 100644
--- a/lightningd/notification.h
+++ b/lightningd/notification.h
@@ -83,8 +83,11 @@ void notify_sendpay_failure(struct lightningd *ld,
const struct routing_failure *fail,
const char *errmsg);
-void notify_coin_mvt(struct lightningd *ld,
- const struct coin_mvt *mvt);
+void notify_channel_mvt(struct lightningd *ld,
+ const struct channel_coin_mvt *chan_mvt);
+
+void notify_chain_mvt(struct lightningd *ld,
+ const struct chain_coin_mvt *chain_mvt);
void notify_balance_snapshot(struct lightningd *ld,
const struct balance_snapshot *snap);
diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c
index 0fe32e4d..b82ef73d 100644
--- a/lightningd/onchain_control.c
+++ b/lightningd/onchain_control.c
@@ -17,6 +17,7 @@
#include <lightningd/channel_control.h>
#include <lightningd/coin_mvts.h>
#include <lightningd/hsm_control.h>
+#include <lightningd/notification.h>
#include <lightningd/onchain_control.h>
#include <lightningd/peer_control.h>
#include <lightningd/peer_htlcs.h>
diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c
index 6c946f0b..fef1be92 100644
--- a/lightningd/peer_htlcs.c
+++ b/lightningd/peer_htlcs.c
@@ -18,6 +18,7 @@
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/coin_mvts.h>
+#include <lightningd/notification.h>
#include <lightningd/pay.h>
#include <lightningd/peer_control.h>
#include <lightningd/peer_htlcs.h>
diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c
index 29beb0aa..a2ec3571 100644
--- a/wallet/walletrpc.c
+++ b/wallet/walletrpc.c
@@ -17,10 +17,10 @@
#include <hsmd/hsmd_wiregen.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
-#include <lightningd/coin_mvts.h>
#include <lightningd/hsm_control.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/lightningd.h>
+#include <lightningd/notification.h>
#include <lightningd/peer_control.h>
#include <wallet/txfilter.h>
#include <wallet/walletrpc.h>
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.