chainntnfs: notify BlockHeight in Updates event
What changed, and why it matters
This commit is a small API enhancement for LND's internal transaction notification system. It changes the 'Updates' progress channel so that, instead of only reporting how many confirmations are still needed, it also reports the block height where the transaction was included. There is no indication this fixes a security bug; it appears to be a feature improvement to give callers more information.
No security action required. Treat as a normal API/behavior change; downstream consumers of ConfirmationEvent.Updates will need to update to the new TxUpdateInfo type.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces a new TxUpdateInfo struct containing BlockHeight and NumConfsLeft, and changes ConfirmationEvent.Updates from chan uint32 to chan TxUpdateInfo. All call sites in txnotifier.go that previously sent a bare uint32 now send a TxUpdateInfo populated with the confirmation’s block height and the remaining confirmations. Tests are updated to expect the new struct. No logic flaws, bounds issues, or security-sensitive changes are visible in the diff.
Changed components
chainntnfs/interface.gochainntnfs/txnotifier.gochainntnfs/txnotifier_test.goInspect captured patch +87 / −64
diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go
index 798a2f4..342d268 100644
--- a/chainntnfs/interface.go
+++ b/chainntnfs/interface.go
@@ -203,6 +203,20 @@ type TxConfirmation struct {
Block *wire.MsgBlock
}
+// TxUpdateInfo contains information about a transaction before it has reached
+// its required number of confirmations. Transactions are registered for
+// notification for a specific number of "required" confirmations, this struct
+// will update the caller incrementally after each new block is found as long as
+// the transaction is not yet fully regarded as confirmed.
+type TxUpdateInfo struct {
+ // BlockHeight is the height of the block that contains the transaction.
+ BlockHeight uint32
+
+ // NumConfsLeft is the number of confirmations left for the transaction
+ // to be regarded as fully confirmed.
+ NumConfsLeft uint32
+}
+
// ConfirmationEvent encapsulates a confirmation notification. With this struct,
// callers can be notified of: the instance the target txid reaches the targeted
// number of confirmations, how many confirmations are left for the target txid
@@ -229,11 +243,12 @@ type ConfirmationEvent struct {
// Updates is a channel that will sent upon, at every incremental
// confirmation, how many confirmations are left to declare the
- // transaction as fully confirmed.
+ // transaction as fully confirmed, along with the height of the block
+ // that contains the transaction.
//
// NOTE: This channel must be buffered with the number of required
// confirmations.
- Updates chan uint32
+ Updates chan TxUpdateInfo
// NegativeConf is a channel that will be sent upon if the transaction
// confirms, but is later reorged out of the chain. The integer sent
@@ -262,7 +277,7 @@ func NewConfirmationEvent(numConfs uint32, cancel func()) *ConfirmationEvent {
// the channel so we need to create a larger buffer to avoid
// blocking the notifier.
Confirmed: make(chan *TxConfirmation, 1),
- Updates: make(chan uint32, numConfs),
+ Updates: make(chan TxUpdateInfo, numConfs),
NegativeConf: make(chan int32, 1),
Done: make(chan struct{}, 1),
Cancel: cancel,
diff --git a/chainntnfs/txnotifier.go b/chainntnfs/txnotifier.go
index 9055d85..57eee84 100644
--- a/chainntnfs/txnotifier.go
+++ b/chainntnfs/txnotifier.go
@@ -947,8 +947,12 @@ func (n *TxNotifier) dispatchConfDetails(
// We'll send a 0 value to the Updates channel,
// indicating that the transaction/output script has already
- // been confirmed.
- err := n.notifyNumConfsLeft(ntfn, 0)
+ // been confirmed, and include the block height at which the
+ // transaction was included.
+ err := n.notifyNumConfsLeft(ntfn, TxUpdateInfo{
+ NumConfsLeft: 0,
+ BlockHeight: details.BlockHeight,
+ })
if err != nil {
return err
}
@@ -977,7 +981,10 @@ func (n *TxNotifier) dispatchConfDetails(
// confirmations are left for the transaction/output script to
// be confirmed.
numConfsLeft := confHeight - n.currentHeight
- err := n.notifyNumConfsLeft(ntfn, numConfsLeft)
+ err := n.notifyNumConfsLeft(ntfn, TxUpdateInfo{
+ NumConfsLeft: numConfsLeft,
+ BlockHeight: details.BlockHeight,
+ })
if err != nil {
return err
}
@@ -1731,7 +1738,10 @@ func (n *TxNotifier) NotifyHeight(height uint32) error {
for confRequest := range confRequests {
confSet := n.confNotifications[confRequest]
for _, ntfn := range confSet.ntfns {
- txConfHeight := confSet.details.BlockHeight +
+ // blockHeight is the height of the block which
+ // contains the transaction.
+ blockHeight := confSet.details.BlockHeight
+ txConfHeight := blockHeight +
ntfn.NumConfirmations - 1
numConfsLeft := txConfHeight - height
@@ -1744,7 +1754,10 @@ func (n *TxNotifier) NotifyHeight(height uint32) error {
continue
}
- err := n.notifyNumConfsLeft(ntfn, numConfsLeft)
+ err := n.notifyNumConfsLeft(ntfn, TxUpdateInfo{
+ NumConfsLeft: numConfsLeft,
+ BlockHeight: blockHeight,
+ })
if err != nil {
return err
}
@@ -2113,25 +2126,28 @@ func (n *TxNotifier) TearDown() {
}
// notifyNumConfsLeft sends the number of confirmations left to the
-// notification subscriber through the Event.Updates channel.
+// notification subscriber through the Event.Updates channel, along with the
+// block height in which the transaction was included.
//
// NOTE: must be used with the TxNotifier's lock held.
-func (n *TxNotifier) notifyNumConfsLeft(ntfn *ConfNtfn, num uint32) error {
+func (n *TxNotifier) notifyNumConfsLeft(ntfn *ConfNtfn,
+ info TxUpdateInfo) error {
+
// If the number left is no less than the recorded value, we can skip
// sending it as it means this same value has already been sent before.
- if num >= ntfn.numConfsLeft {
+ if info.NumConfsLeft >= ntfn.numConfsLeft {
Log.Debugf("Skipped dispatched update (numConfsLeft=%v) for "+
- "request %v conf_id=%v", num, ntfn.ConfRequest,
- ntfn.ConfID)
+ "request %v conf_id=%v", info.NumConfsLeft,
+ ntfn.ConfRequest, ntfn.ConfID)
return nil
}
// Update the number of confirmations left to the notification.
- ntfn.numConfsLeft = num
+ ntfn.numConfsLeft = info.NumConfsLeft
select {
- case ntfn.Event.Updates <- num:
+ case ntfn.Event.Updates <- info:
case <-n.quit:
return ErrTxNotifierExiting
}
diff --git a/chainntnfs/txnotifier_test.go b/chainntnfs/txnotifier_test.go
index 1279330..1bd9f4f 100644
--- a/chainntnfs/txnotifier_test.go
+++ b/chainntnfs/txnotifier_test.go
@@ -271,13 +271,12 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) {
// We should only receive one update for tx1 since it only requires
// one confirmation and it already met it.
select {
- case numConfsLeft := <-ntfn1.Event.Updates:
- const expected = 0
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx1 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn1.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 0,
+ BlockHeight: 11,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx1")
}
@@ -300,13 +299,12 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) {
// We should only receive one update for tx2 since it only has one
// confirmation so far and it requires two.
select {
- case numConfsLeft := <-ntfn2.Event.Updates:
- const expected = 1
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx2 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn2.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 1,
+ BlockHeight: 11,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx2")
}
@@ -341,13 +339,12 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) {
// We should only receive one update since the last at the new height,
// indicating how many confirmations are still left.
select {
- case numConfsLeft := <-ntfn2.Event.Updates:
- const expected = 0
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx2 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn2.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 0,
+ BlockHeight: 11,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx2")
}
@@ -411,13 +408,12 @@ func TestTxNotifierHistoricalConfDispatch(t *testing.T) {
err = n.UpdateConfDetails(ntfn1.HistoricalDispatch.ConfRequest, &txConf1)
require.NoError(t, err, "unable to update conf details")
select {
- case numConfsLeft := <-ntfn1.Event.Updates:
- const expected = 0
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx1 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn1.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 0,
+ BlockHeight: 9,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx1")
}
@@ -443,13 +439,12 @@ func TestTxNotifierHistoricalConfDispatch(t *testing.T) {
err = n.UpdateConfDetails(ntfn2.HistoricalDispatch.ConfRequest, &txConf2)
require.NoError(t, err, "unable to update conf details")
select {
- case numConfsLeft := <-ntfn2.Event.Updates:
- const expected = 1
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx2 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn2.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 1,
+ BlockHeight: 9,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx2")
}
@@ -485,13 +480,12 @@ func TestTxNotifierHistoricalConfDispatch(t *testing.T) {
// We should only receive one update for tx2 since the last one,
// indicating how many confirmations are still left.
select {
- case numConfsLeft := <-ntfn2.Event.Updates:
- const expected = 0
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx2 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn2.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 0,
+ BlockHeight: 9,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx2")
}
@@ -1490,13 +1484,12 @@ func TestTxNotifierConfReorg(t *testing.T) {
// We should only receive one update for tx2 since it only requires
// one confirmation and it already met it.
select {
- case numConfsLeft := <-ntfn2.Event.Updates:
- const expected = 0
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx2 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn2.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: 0,
+ BlockHeight: 12,
}
+ require.Equal(t, expected, updDetails)
default:
t.Fatal("Expected confirmation update for tx2")
}
@@ -1520,15 +1513,14 @@ func TestTxNotifierConfReorg(t *testing.T) {
// confirmations and it has already met them.
for i := uint32(1); i <= 2; i++ {
select {
- case numConfsLeft := <-ntfn3.Event.Updates:
- expected := tx3NumConfs - i
- if numConfsLeft != expected {
- t.Fatalf("Received incorrect confirmation update: tx3 "+
- "expected %d confirmations left, got %d",
- expected, numConfsLeft)
+ case updDetails := <-ntfn3.Event.Updates:
+ expected := chainntnfs.TxUpdateInfo{
+ NumConfsLeft: tx3NumConfs - i,
+ BlockHeight: 12,
}
+ require.Equal(t, expected, updDetails)
default:
- t.Fatal("Expected confirmation update for tx2")
+ t.Fatal("Expected confirmation update for tx3")
}
}
Why this scored 18/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.