From 85f4b13632f7579149f83593cf1ccc73da4c13f7 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 22 Nov 2023 23:12:49 +0800 Subject: [PATCH 1/4] multi: enhance logging around channel reestablishment --- channeldb/channel.go | 36 ++++++++++++++++++++++++++++++++++++ htlcswitch/link.go | 5 +++-- peer/brontide.go | 3 ++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 67a32379e7..dbd796c879 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -849,6 +849,30 @@ type OpenChannel struct { sync.RWMutex } +// String returns a string representation of the channel. +func (c *OpenChannel) String() string { + indexStr := "height=%v, local_htlc_index=%v, local_log_index=%v, " + + "remote_htlc_index=%v, remote_log_index=%v" + + commit := c.LocalCommitment + local := fmt.Sprintf(indexStr, commit.CommitHeight, + commit.LocalHtlcIndex, commit.LocalLogIndex, + commit.RemoteHtlcIndex, commit.RemoteLogIndex, + ) + + commit = c.RemoteCommitment + remote := fmt.Sprintf(indexStr, commit.CommitHeight, + commit.LocalHtlcIndex, commit.LocalLogIndex, + commit.RemoteHtlcIndex, commit.RemoteLogIndex, + ) + + return fmt.Sprintf("SCID=%v, status=%v, initiator=%v, pending=%v, "+ + "local commitment has %s, remote commitment has %s", + c.ShortChannelID, c.chanStatus, c.IsInitiator, c.IsPending, + local, remote, + ) +} + // ShortChanID returns the current ShortChannelID of this channel. func (c *OpenChannel) ShortChanID() lnwire.ShortChannelID { c.RLock() @@ -2100,6 +2124,10 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { // which ones are present on their commitment. remoteHtlcs := make(map[[32]byte]struct{}) for _, htlc := range c.RemoteCommitment.Htlcs { + log.Tracef("RemoteCommitment has htlc: id=%v, update=%v "+ + "incoming=%v", htlc.HtlcIndex, htlc.LogIndex, + htlc.Incoming) + onionHash := sha256.Sum256(htlc.OnionBlob[:]) remoteHtlcs[onionHash] = struct{}{} } @@ -2108,8 +2136,16 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { // as active if *we* know them as well. activeHtlcs := make([]HTLC, 0, len(remoteHtlcs)) for _, htlc := range c.LocalCommitment.Htlcs { + log.Tracef("LocalCommitment has htlc: id=%v, update=%v "+ + "incoming=%v", htlc.HtlcIndex, htlc.LogIndex, + htlc.Incoming) + onionHash := sha256.Sum256(htlc.OnionBlob[:]) if _, ok := remoteHtlcs[onionHash]; !ok { + log.Tracef("Skipped htlc due to onion mismatched: "+ + "id=%v, update=%v incoming=%v", + htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming) + continue } diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 2a1e49923d..e302cf3d8f 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -649,12 +649,13 @@ func (l *channelLink) createFailureWithUpdate(incoming bool, // flow. We'll compare out commitment chains with the remote party, and re-send // either a danging commit signature, a revocation, or both. func (l *channelLink) syncChanStates() error { - l.log.Info("attempting to re-synchronize") + chanState := l.channel.State() + + l.log.Infof("Attempting to re-synchronize channel: %v", chanState) // First, we'll generate our ChanSync message to send to the other // side. Based on this message, the remote party will decide if they // need to retransmit any data or not. - chanState := l.channel.State() localChanSyncMsg, err := chanState.ChanSyncMsg() if err != nil { return fmt.Errorf("unable to generate chan sync message for "+ diff --git a/peer/brontide.go b/peer/brontide.go index d7d93f4fa1..35cdbd8475 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -2009,7 +2009,8 @@ func messageSummary(msg lnwire.Message) string { msg.ChanID, int64(msg.FeePerKw)) case *lnwire.ChannelReestablish: - return fmt.Sprintf("next_local_height=%v, remote_tail_height=%v", + return fmt.Sprintf("chan_id=%v, next_local_height=%v, "+ + "remote_tail_height=%v", msg.ChanID, msg.NextLocalCommitHeight, msg.RemoteCommitTailHeight) case *lnwire.ReplyShortChanIDsEnd: From 81841b7daba4a85cb371fa9f2dd8dc20b0fd8380 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 25 Nov 2023 01:49:32 +0800 Subject: [PATCH 2/4] lnwallet: add unit test for `extractPayDescs` --- lnwallet/channel_test.go | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 0b98b98c1c..80a3df31c0 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -10111,3 +10111,101 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { ) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) } + +// TestExtractPayDescs asserts that `extractPayDescs` can correctly turn a +// slice of htlcs into two slices of PaymentDescriptors. +func TestExtractPayDescs(t *testing.T) { + t.Parallel() + + // Create a testing LightningChannel. + lnChan, _, err := CreateTestChannels( + t, channeldb.SingleFunderTweaklessBit, + ) + require.NoError(t, err) + + // Create two incoming HTLCs. + incomings := []channeldb.HTLC{ + createRandomHTLC(t, true), + createRandomHTLC(t, true), + } + + // Create two outgoing HTLCs. + outgoings := []channeldb.HTLC{ + createRandomHTLC(t, false), + createRandomHTLC(t, false), + } + + // Concatenate incomings and outgoings into a single slice. + htlcs := []channeldb.HTLC{} + htlcs = append(htlcs, incomings...) + htlcs = append(htlcs, outgoings...) + + // Run the method under test. + // + // NOTE: we use nil commitment key rings to avoid checking the htlc + // scripts(`genHtlcScript`) as it should be tested independently. + incomingPDs, outgoingPDs, err := lnChan.extractPayDescs( + 0, 0, htlcs, nil, nil, true, + ) + require.NoError(t, err) + + // Assert the incoming PaymentDescriptors are matched. + for i, pd := range incomingPDs { + htlc := incomings[i] + assertPayDescMatchHTLC(t, pd, htlc) + } + + // Assert the outgoing PaymentDescriptors are matched. + for i, pd := range outgoingPDs { + htlc := outgoings[i] + assertPayDescMatchHTLC(t, pd, htlc) + } +} + +// assertPayDescMatchHTLC compares a PaymentDescriptor to a channeldb.HTLC and +// asserts that the fields are matched. +func assertPayDescMatchHTLC(t *testing.T, pd PaymentDescriptor, + htlc channeldb.HTLC) { + + require := require.New(t) + + require.EqualValues(htlc.RHash, pd.RHash, "RHash") + require.Equal(htlc.RefundTimeout, pd.Timeout, "Timeout") + require.Equal(htlc.Amt, pd.Amount, "Amount") + require.Equal(htlc.HtlcIndex, pd.HtlcIndex, "HtlcIndex") + require.Equal(htlc.LogIndex, pd.LogIndex, "LogIndex") + require.EqualValues(htlc.OnionBlob[:], pd.OnionBlob, "OnionBlob") +} + +// createRandomHTLC creates an HTLC that has random value in every field except +// the `Incoming`. +func createRandomHTLC(t *testing.T, incoming bool) channeldb.HTLC { + var onionBlob [lnwire.OnionPacketSize]byte + _, err := rand.Read(onionBlob[:]) + require.NoError(t, err) + + var rHash [lntypes.HashSize]byte + _, err = rand.Read(rHash[:]) + require.NoError(t, err) + + sig := make([]byte, 64) + _, err = rand.Read(sig) + require.NoError(t, err) + + extra := make([]byte, 1000) + _, err = rand.Read(extra) + require.NoError(t, err) + + return channeldb.HTLC{ + Signature: sig, + RHash: rHash, + Amt: lnwire.MilliSatoshi(rand.Uint64()), + RefundTimeout: rand.Uint32(), + OutputIndex: rand.Int31n(1000), + Incoming: incoming, + OnionBlob: onionBlob, + HtlcIndex: rand.Uint64(), + LogIndex: rand.Uint64(), + ExtraData: extra, + } +} From 1ee665d14c91fce76f6ef14d22134085577d1cbd Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 23 Nov 2023 22:50:45 +0800 Subject: [PATCH 3/4] lnwallet: make sure loop var is properly referenced --- lnwallet/channel.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index faef703a1c..1bf57b21de 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -904,6 +904,8 @@ func (lc *LightningChannel) extractPayDescs(commitHeight uint64, // persist state w.r.t to if forwarded or not, or can // inadvertently trigger replays + htlc := htlc + payDesc, err := lc.diskHtlcToPayDesc( feeRate, commitHeight, &htlc, localCommitKeys, remoteCommitKeys, From 4845b7ade3faa8da46b808ec307906855f526c12 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 25 Nov 2023 07:23:37 +0800 Subject: [PATCH 4/4] docs: add release notes for `v0.17.3` --- docs/release-notes/release-notes-0.17.3.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/release-notes-0.17.3.md b/docs/release-notes/release-notes-0.17.3.md index b00a5d6f76..1cbfe8cf81 100644 --- a/docs/release-notes/release-notes-0.17.3.md +++ b/docs/release-notes/release-notes-0.17.3.md @@ -23,6 +23,9 @@ `musig2Sessions` with a `SyncMap` used in `input` package to avoid concurrent write to this map. +* [Fixed](https://github.com/lightningnetwork/lnd/pull/8220) a loop variable + issue which may affect programs built using go `v1.20` and below. + # New Features ## Functional Enhancements ## RPC Additions