What changed, and why it matters
This commit adds a new integration test that checks whether users subscribed to channel events receive real-time notifications when a Lightning channel is opened, becomes active, or processes a payment. It does not change production code or fix any bug.
No security action needed. This is a test-only addition and can be reviewed as normal QA/test coverage.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit introduces testChannelUpdateNotifications in itest/lnd_open_channel_test.go, registers it in itest/list_on_test.go, and adds a helper AssertChannelEventType in lntest/harness_assertion.go. The test opens a channel between two nodes, subscribes both to channel events, asserts PENDING_OPEN, OPEN, and ACTIVE events, then sends a payment and asserts CHANNEL_UPDATE events for HTLC addition and settlement. No production logic is modified.
Changed components
itest/lnd_open_channel_test.goitest/list_on_test.golntest/harness_assertion.goInspect captured patch +117 / −0
diff --git a/itest/list_on_test.go b/itest/list_on_test.go
index 42471e9..e4aed31 100644
--- a/itest/list_on_test.go
+++ b/itest/list_on_test.go
@@ -206,6 +206,10 @@ var allTestCases = []*lntest.TestCase{
Name: "invoice update subscription",
TestFunc: testInvoiceSubscriptions,
},
+ {
+ Name: "channel update subscription",
+ TestFunc: testChannelUpdateNotifications,
+ },
{
Name: "streaming channel backup update",
TestFunc: testChannelBackupUpdates,
diff --git a/itest/lnd_open_channel_test.go b/itest/lnd_open_channel_test.go
index e6cdca4..7319660 100644
--- a/itest/lnd_open_channel_test.go
+++ b/itest/lnd_open_channel_test.go
@@ -3,6 +3,7 @@ package itest
import (
"fmt"
"strings"
+ "time"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
@@ -1342,3 +1343,101 @@ func testOpenChannelWithShutdownAddr(ht *lntest.HarnessTest) {
require.Equal(ht, bobShutdownAddr.Address, bobUTXOConfirmed.Address)
require.Equal(ht, paymentAmount, bobUTXOConfirmed.AmountSat)
}
+
+// testChannelUpdateNotifications checks that clients subscribed to channel
+// events receive real-time updates when the channel state changes.
+func testChannelUpdateNotifications(ht *lntest.HarnessTest) {
+ // We'll start by creating two nodes, Alice and Bob, and a channel
+ // between them.
+ alice := ht.NewNodeWithCoins("Alice", nil)
+ bob := ht.NewNode("Bob", nil)
+
+ ht.EnsureConnected(alice, bob)
+
+ // We'll subscribe to channel events for both nodes.
+ aliceSub := alice.RPC.SubscribeChannelEvents()
+ bobSub := bob.RPC.SubscribeChannelEvents()
+
+ // We'll then open a channel between Alice and Bob.
+ chanPoint := ht.OpenChannel(
+ alice, bob, lntest.OpenChannelParams{
+ Amt: 1000000,
+ PushAmt: 500000,
+ },
+ )
+
+ // We'll wait for the channel to be active. We expect to receive one
+ // pending, one open, and one active notification.
+ ht.AssertChannelActive(alice, chanPoint)
+ ht.AssertChannelActive(bob, chanPoint)
+
+ ht.AssertChannelEventType(
+ aliceSub, lnrpc.ChannelEventUpdate_PENDING_OPEN_CHANNEL,
+ )
+ ht.AssertChannelEventType(
+ aliceSub, lnrpc.ChannelEventUpdate_OPEN_CHANNEL,
+ )
+ ht.AssertChannelEventType(
+ aliceSub, lnrpc.ChannelEventUpdate_ACTIVE_CHANNEL,
+ )
+
+ ht.AssertChannelEventType(
+ bobSub, lnrpc.ChannelEventUpdate_PENDING_OPEN_CHANNEL,
+ )
+ ht.AssertChannelEventType(
+ bobSub, lnrpc.ChannelEventUpdate_OPEN_CHANNEL,
+ )
+ ht.AssertChannelEventType(
+ bobSub, lnrpc.ChannelEventUpdate_ACTIVE_CHANNEL,
+ )
+
+ // We'll now make a payment from Alice to Bob to trigger a channel
+ // update.
+ payReqs, _, _ := ht.CreatePayReqs(bob, btcutil.Amount(1000), 1)
+ ht.CompletePaymentRequests(alice, payReqs)
+
+ // assertUpdates is a helper function to assert the number of commitment
+ // updates received by a node.
+ assertUpdates := func(sub rpc.ChannelEventsClient, numUpdates int) {
+ event := ht.AssertChannelEventType(
+ sub, lnrpc.ChannelEventUpdate_CHANNEL_UPDATE,
+ )
+ require.IsType(
+ ht, &lnrpc.ChannelEventUpdate_UpdatedChannel{},
+ event.Channel,
+ )
+ channel := event.GetUpdatedChannel().Channel
+ require.EqualValues(ht, numUpdates, channel.NumUpdates)
+ }
+
+ // expectNoMoreUpdates is a helper function to assert that no more
+ // channel updates are received by a node.
+ expectNoMoreUpdates := func(sub rpc.ChannelEventsClient) {
+ updates := make(chan struct{})
+ go func() {
+ _, err := sub.Recv()
+ // Only signal if we successfully received an update.
+ // If Recv fails (e.g., context canceled during test
+ // cleanup), that's fine - it means no update arrived.
+ if err == nil {
+ close(updates)
+ }
+ }()
+
+ select {
+ case <-updates:
+ ht.Fatalf("expected no more updates")
+ case <-time.After(defaultTimeout):
+ }
+ }
+
+ // We expect to see two updates from each node's point of view. One for
+ // the addition of the HTLC, and a second for the settlement.
+ assertUpdates(aliceSub, 1)
+ assertUpdates(aliceSub, 2)
+ expectNoMoreUpdates(aliceSub)
+
+ assertUpdates(bobSub, 1)
+ assertUpdates(bobSub, 2)
+ expectNoMoreUpdates(bobSub)
+}
diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go
index 8c60a27..e2fcdeb 100644
--- a/lntest/harness_assertion.go
+++ b/lntest/harness_assertion.go
@@ -425,6 +425,20 @@ func (h *HarnessTest) assertChannelStatus(hn *node.HarnessNode,
return channel
}
+// AssertChannelEventType consumes one event from a client and asserts the event
+// type is matched.
+func (h *HarnessTest) AssertChannelEventType(sub rpc.ChannelEventsClient,
+ updateType lnrpc.ChannelEventUpdate_UpdateType,
+) *lnrpc.ChannelEventUpdate {
+
+ update := h.ReceiveChannelEvent(sub)
+
+ require.Equalf(h, updateType, update.Type, "wrong event type, "+
+ "want %v got %v", updateType, update.Type)
+
+ return update
+}
+
// AssertOutputScriptClass checks that the specified transaction output has the
// expected script class.
func (h *HarnessTest) AssertOutputScriptClass(tx *btcutil.Tx,
Why this scored 15/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.