channeldb: add MarkConfirmationHeight to OpenChannel

Add the MarkConfirmationHeight method to the OpenChannel struct
to record the block height at which the funding transaction was
first confirmed. Also, introduce the ConfirmationHeight field
to persist this information in the database.

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
This commit is contained in:
Nishant Bansal committed 2025-09-11 18:26:16 +05:30
1 parent 7bc6331402
commit 222f49c593
2 files changed
+115 -1

No files matched your search

+47
View File
@@ -261,6 +261,10 @@ type openChannelTlvData struct {
// customBlob is an optional TLV encoded blob of data representing
// custom channel funding information.
customBlob tlv.OptionalRecordT[tlv.TlvType7, tlv.Blob]
// confirmationHeight records the block height at which the funding
// transaction was first confirmed.
confirmationHeight tlv.RecordT[tlv.TlvType8, uint32]
}
// encode serializes the openChannelTlvData to the given io.Writer.
@@ -270,6 +274,7 @@ func (c *openChannelTlvData) encode(w io.Writer) error {
c.initialLocalBalance.Record(),
c.initialRemoteBalance.Record(),
c.realScid.Record(),
c.confirmationHeight.Record(),
}
c.memo.WhenSome(func(memo tlv.RecordT[tlv.TlvType5, []byte]) {
tlvRecords = append(tlvRecords, memo.Record())
@@ -283,6 +288,8 @@ func (c *openChannelTlvData) encode(w io.Writer) error {
tlvRecords = append(tlvRecords, blob.Record())
})
tlv.SortRecords(tlvRecords)
// Create the tlv stream.
tlvStream, err := tlv.NewStream(tlvRecords...)
if err != nil {
@@ -307,6 +314,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error {
memo.Record(),
tapscriptRoot.Record(),
blob.Record(),
c.confirmationHeight.Record(),
)
if err != nil {
return err
@@ -906,6 +914,10 @@ type OpenChannel struct {
// been confirmed before a certain height.
FundingBroadcastHeight uint32
// ConfirmationHeight records the block height at which the funding
// transaction was first confirmed.
ConfirmationHeight uint32
// NumConfsRequired is the number of confirmations a channel's funding
// transaction must have received in order to be considered available
// for normal transactional use.
@@ -1207,6 +1219,7 @@ func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) {
auxData.initialRemoteBalance.Val,
)
c.confirmedScid = auxData.realScid.Val
c.ConfirmationHeight = auxData.confirmationHeight.Val
auxData.memo.WhenSomeV(func(memo []byte) {
c.Memo = memo
@@ -1234,6 +1247,9 @@ func (c *OpenChannel) extractTlvData() openChannelTlvData {
realScid: tlv.NewRecordT[tlv.TlvType4](
c.confirmedScid,
),
confirmationHeight: tlv.NewPrimitiveRecord[tlv.TlvType8](
c.ConfirmationHeight,
),
}
if len(c.Memo) != 0 {
@@ -1501,6 +1517,37 @@ func (c *OpenChannel) fullSync(tx kvdb.RwTx) error {
return putOpenChannel(chanBucket, c)
}
// MarkConfirmationHeight updates the channel's confirmation height once the
// channel opening transaction receives one confirmation.
func (c *OpenChannel) MarkConfirmationHeight(height uint32) error {
c.Lock()
defer c.Unlock()
if err := kvdb.Update(c.Db.backend, func(tx kvdb.RwTx) error {
chanBucket, err := fetchChanBucketRw(
tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash,
)
if err != nil {
return err
}
channel, err := fetchOpenChannel(chanBucket, &c.FundingOutpoint)
if err != nil {
return err
}
channel.ConfirmationHeight = height
return putOpenChannel(chanBucket, channel)
}, func() {}); err != nil {
return err
}
c.ConfirmationHeight = height
return nil
}
// MarkAsOpen marks a channel as fully open given a locator that uniquely
// describes its location within the chain.
func (c *OpenChannel) MarkAsOpen(openLoc lnwire.ShortChannelID) error {
+68 -1
View File
@@ -978,6 +978,73 @@ func TestChannelStateTransition(t *testing.T) {
require.Empty(t, fwdPkgs, "no forwarding packages should exist")
}
// TestOpeningChannelTxConfirmation verifies that calling MarkConfirmationHeight
// correctly updates the confirmed state. It also ensures that calling Refresh
// on a different OpenChannel updates its in-memory state to reflect the prior
// MarkConfirmationHeight call.
func TestOpeningChannelTxConfirmation(t *testing.T) {
t.Parallel()
fullDB, err := MakeTestDB(t)
require.NoError(t, err)
cdb := fullDB.ChannelStateDB()
// Create a pending channel that was broadcast at height 99.
const broadcastHeight = uint32(99)
channelState := createTestChannel(
t, cdb, pendingHeightOption(broadcastHeight),
)
// Fetch pending channels from the database.
pendingChannels, err := cdb.FetchPendingChannels()
require.NoError(t, err)
require.Len(t, pendingChannels, 1)
// Verify the broadcast height of the pending channel.
require.Equal(
t, broadcastHeight, pendingChannels[0].FundingBroadcastHeight,
)
confirmationHeight := broadcastHeight + 1
// Mark the channel's confirmation height.
err = pendingChannels[0].MarkConfirmationHeight(confirmationHeight)
require.NoError(t, err)
// Verify the ConfirmationHeight is updated correctly.
require.Equal(
t, confirmationHeight, pendingChannels[0].ConfirmationHeight,
)
// Re-fetch the pending channels to confirm persistence.
pendingChannels, err = cdb.FetchPendingChannels()
require.NoError(t, err)
require.Len(t, pendingChannels, 1)
// Validate the confirmation and broadcast height.
require.Equal(
t, confirmationHeight, pendingChannels[0].ConfirmationHeight,
)
require.Equal(
t, broadcastHeight, pendingChannels[0].FundingBroadcastHeight,
)
// Ensure the original channel state's confirmation height is not
// updated before refresh.
require.EqualValues(t, channelState.ConfirmationHeight, 0)
// Refresh the original channel state.
err = channelState.Refresh()
require.NoError(t, err)
// Verify that both channel states now have the same ConfirmationHeight.
require.Equal(
t, channelState.ConfirmationHeight,
pendingChannels[0].ConfirmationHeight,
)
}
func TestFetchPendingChannels(t *testing.T) {
t.Parallel()
@@ -1007,7 +1074,7 @@ func TestFetchPendingChannels(t *testing.T) {
}
chanOpenLoc := lnwire.ShortChannelID{
BlockHeight: 5,
BlockHeight: broadcastHeight + 1,
TxIndex: 10,
TxPosition: 15,
}