lnrpc+rpcserver: add new info to WaitingCloseChannel
What changed, and why it matters
This commit adds two new informational fields to the PendingChannels RPC response in the LND Lightning node: one showing how many more blocks until a waiting close channel is considered final, and another showing the block height when the closing transaction was first confirmed. It is a user-facing informational enhancement, not a security fix or vulnerability.
No security action required. Treat as a normal feature/API enhancement during routine review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extends the WaitingCloseChannel message in lnrpc/lightning.proto (and generated .pb.go and swagger.json) with blocks_til_close_confirmed and close_height. In rpcserver.go, fetchWaitingCloseChannels now queries the current best block height, computes required confirmations via lnwallet.CloseConfsForCapacity (with a dev override), and derives the remaining confirmations from the close confirmation height. No cryptographic, consensus, or authorization logic is modified.
Changed components
lnrpc/lightning.protolnrpc/lightning.pb.golnrpc/lightning.swagger.jsonrpcserver.goInspect captured patch +105 / −9
diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go
index d987f75..86305fa 100644
--- a/lnrpc/lightning.pb.go
+++ b/lnrpc/lightning.pb.go
@@ -18427,7 +18427,19 @@ type PendingChannelsResponse_WaitingCloseChannel struct {
ClosingTxid string `protobuf:"bytes,4,opt,name=closing_txid,json=closingTxid,proto3" json:"closing_txid,omitempty"`
// The raw hex encoded bytes of the closing transaction. Included if
// include_raw_tx in the request is true.
- ClosingTxHex string `protobuf:"bytes,5,opt,name=closing_tx_hex,json=closingTxHex,proto3" json:"closing_tx_hex,omitempty"`
+ ClosingTxHex string `protobuf:"bytes,5,opt,name=closing_tx_hex,json=closingTxHex,proto3" json:"closing_tx_hex,omitempty"`
+ // Remaining number of confirmations until the channel closure is
+ // considered final and removed from waiting close. Channel closes
+ // require multiple confirmations for reorg protection — the exact
+ // number scales with channel capacity. A closing transaction that
+ // gets reorganized out of the chain resets this counter. When the
+ // closing transaction is not yet confirmed, this value equals the
+ // total number of confirmations required.
+ BlocksTilCloseConfirmed uint32 `protobuf:"varint,6,opt,name=blocks_til_close_confirmed,json=blocksTilCloseConfirmed,proto3" json:"blocks_til_close_confirmed,omitempty"`
+ // The block height at which the closing transaction was first confirmed.
+ // This will be zero if the closing transaction has not yet confirmed, or
+ // if this information is not available for older channels.
+ CloseHeight uint32 `protobuf:"varint,7,opt,name=close_height,json=closeHeight,proto3" json:"close_height,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -18497,6 +18509,20 @@ func (x *PendingChannelsResponse_WaitingCloseChannel) GetClosingTxHex() string {
return ""
}
+func (x *PendingChannelsResponse_WaitingCloseChannel) GetBlocksTilCloseConfirmed() uint32 {
+ if x != nil {
+ return x.BlocksTilCloseConfirmed
+ }
+ return 0
+}
+
+func (x *PendingChannelsResponse_WaitingCloseChannel) GetCloseHeight() uint32 {
+ if x != nil {
+ return x.CloseHeight
+ }
+ return 0
+}
+
type PendingChannelsResponse_Commitments struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Hash of the local version of the commitment tx.
@@ -19407,7 +19433,7 @@ const file_lightning_proto_rawDesc = "" +
"\x13blocks_til_maturity\x18\x05 \x01(\x05R\x11blocksTilMaturity\x12\x14\n" +
"\x05stage\x18\x06 \x01(\rR\x05stage\">\n" +
"\x16PendingChannelsRequest\x12$\n" +
- "\x0einclude_raw_tx\x18\x01 \x01(\bR\fincludeRawTx\"\x80\x15\n" +
+ "\x0einclude_raw_tx\x18\x01 \x01(\bR\fincludeRawTx\"\xe0\x15\n" +
"\x17PendingChannelsResponse\x12.\n" +
"\x13total_limbo_balance\x18\x01 \x01(\x03R\x11totalLimboBalance\x12e\n" +
"\x15pending_open_channels\x18\x02 \x03(\v21.lnrpc.PendingChannelsResponse.PendingOpenChannelR\x13pendingOpenChannels\x12j\n" +
@@ -19439,13 +19465,15 @@ const file_lightning_proto_rawDesc = "" +
"fee_per_kw\x18\x06 \x01(\x03R\bfeePerKw\x122\n" +
"\x15funding_expiry_blocks\x18\x03 \x01(\x05R\x13fundingExpiryBlocks\x12<\n" +
"\x1aconfirmations_until_active\x18\a \x01(\rR\x18confirmationsUntilActive\x12/\n" +
- "\x13confirmation_height\x18\b \x01(\rR\x12confirmationHeightJ\x04\b\x02\x10\x03\x1a\x9a\x02\n" +
+ "\x13confirmation_height\x18\b \x01(\rR\x12confirmationHeightJ\x04\b\x02\x10\x03\x1a\xfa\x02\n" +
"\x13WaitingCloseChannel\x12G\n" +
"\achannel\x18\x01 \x01(\v2-.lnrpc.PendingChannelsResponse.PendingChannelR\achannel\x12#\n" +
"\rlimbo_balance\x18\x02 \x01(\x03R\flimboBalance\x12L\n" +
"\vcommitments\x18\x03 \x01(\v2*.lnrpc.PendingChannelsResponse.CommitmentsR\vcommitments\x12!\n" +
"\fclosing_txid\x18\x04 \x01(\tR\vclosingTxid\x12$\n" +
- "\x0eclosing_tx_hex\x18\x05 \x01(\tR\fclosingTxHex\x1a\xa3\x02\n" +
+ "\x0eclosing_tx_hex\x18\x05 \x01(\tR\fclosingTxHex\x12;\n" +
+ "\x1ablocks_til_close_confirmed\x18\x06 \x01(\rR\x17blocksTilCloseConfirmed\x12!\n" +
+ "\fclose_height\x18\a \x01(\rR\vcloseHeight\x1a\xa3\x02\n" +
"\vCommitments\x12\x1d\n" +
"\n" +
"local_txid\x18\x01 \x01(\tR\tlocalTxid\x12\x1f\n" +
diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto
index b1323db..92ea152 100644
--- a/lnrpc/lightning.proto
+++ b/lnrpc/lightning.proto
@@ -2977,6 +2977,24 @@ message PendingChannelsResponse {
// The raw hex encoded bytes of the closing transaction. Included if
// include_raw_tx in the request is true.
string closing_tx_hex = 5;
+
+ /*
+ Remaining number of confirmations until the channel closure is
+ considered final and removed from waiting close. Channel closes
+ require multiple confirmations for reorg protection — the exact
+ number scales with channel capacity. A closing transaction that
+ gets reorganized out of the chain resets this counter. When the
+ closing transaction is not yet confirmed, this value equals the
+ total number of confirmations required.
+ */
+ uint32 blocks_til_close_confirmed = 6;
+
+ /*
+ The block height at which the closing transaction was first confirmed.
+ This will be zero if the closing transaction has not yet confirmed, or
+ if this information is not available for older channels.
+ */
+ uint32 close_height = 7;
}
message Commitments {
diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json
index 96ded86..75b0552 100644
--- a/lnrpc/lightning.swagger.json
+++ b/lnrpc/lightning.swagger.json
@@ -3455,6 +3455,16 @@
"closing_tx_hex": {
"type": "string",
"description": "The raw hex encoded bytes of the closing transaction. Included if\ninclude_raw_tx in the request is true."
+ },
+ "blocks_til_close_confirmed": {
+ "type": "integer",
+ "format": "int64",
+ "description": "Remaining number of confirmations until the channel closure is\nconsidered final and removed from waiting close. Channel closes\nrequire multiple confirmations for reorg protection — the exact\nnumber scales with channel capacity. A closing transaction that\ngets reorganized out of the chain resets this counter. When the\nclosing transaction is not yet confirmed, this value equals the\ntotal number of confirmations required."
+ },
+ "close_height": {
+ "type": "integer",
+ "format": "int64",
+ "description": "The block height at which the closing transaction was first confirmed.\nThis will be zero if the closing transaction has not yet confirmed, or\nif this information is not available for older channels."
}
}
},
diff --git a/rpcserver.go b/rpcserver.go
index de28c47..2c6a3da 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -4257,6 +4257,12 @@ func (r *rpcServer) fetchWaitingCloseChannels(
return nil, 0, err
}
+ // Get the current block height for calculating remaining confirmations.
+ _, currentHeight, err := r.server.cc.ChainIO.GetBestBlock()
+ if err != nil {
+ return nil, 0, err
+ }
+
result := make(waitingCloseChannels, 0)
limboBalance := int64(0)
@@ -4418,12 +4424,46 @@ func (r *rpcServer) fetchWaitingCloseChannels(
}
}
+ // Calculate remaining confirmations until the channel closure
+ // is considered resolved/confirmed.
+ requiredConfs := lnwallet.CloseConfsForCapacity(
+ waitingClose.Capacity,
+ )
+ if r.cfg.Dev != nil {
+ requiredConfs = r.cfg.Dev.ChannelCloseConfs().
+ UnwrapOr(requiredConfs)
+ }
+
+ blocksTilCloseConfirmed := fn.ElimOption(
+ waitingClose.CloseConfirmationHeight,
+ func() uint32 {
+ // The closing tx is not yet confirmed, show
+ // all required confirmations.
+ return requiredConfs
+ },
+ func(closeConfHeight uint32) uint32 {
+ // The closing tx has at least one
+ // confirmation. Calculate how many more are
+ // needed.
+ targetHeight := closeConfHeight +
+ requiredConfs - 1
+ if uint32(currentHeight) >= targetHeight {
+ return 0
+ }
+
+ return targetHeight - uint32(currentHeight)
+ },
+ )
+
waitingCloseResp := &lnrpc.PendingChannelsResponse_WaitingCloseChannel{
- Channel: channel,
- LimboBalance: channel.LocalBalance,
- Commitments: &commitments,
- ClosingTxid: closingTxid,
- ClosingTxHex: closingTxHex,
+ Channel: channel,
+ LimboBalance: channel.LocalBalance,
+ Commitments: &commitments,
+ ClosingTxid: closingTxid,
+ ClosingTxHex: closingTxHex,
+ BlocksTilCloseConfirmed: blocksTilCloseConfirmed,
+ CloseHeight: waitingClose.CloseConfirmationHeight.
+ UnwrapOr(0),
}
// A close tx has been broadcasted, all our balance will be in
Why this scored 19/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.