From 92406ba72b34d37df7c54761602519c992584e54 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Tue, 16 Jul 2024 15:35:58 -0700 Subject: [PATCH 01/22] lnwallet: pack paymentDescriptor add/remove heights into Duals The purpose of this commit is to begin the process of packing symmetric fields into the newly introduced Dual structure. The reason for this is that the Dual structure has a handy indexing method where we can supply a ChannelParty and get back a value. This will cut down on the amount of branching code in the main lines of the codebase logic, making it easier to follow what is going on. --- lnwallet/channel.go | 307 ++++++++++++++++++--------------- lnwallet/channel_test.go | 194 +++++++++++++-------- lnwallet/payment_descriptor.go | 7 +- lnwallet/update_log.go | 9 +- 4 files changed, 297 insertions(+), 220 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 32492171ff3..1b505ee1ec6 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1053,17 +1053,19 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // as we've included this HTLC in our local commitment chain // for the remote party. pd = &paymentDescriptor{ - ChanID: wireMsg.ChanID, - RHash: wireMsg.PaymentHash, - Timeout: wireMsg.Expiry, - Amount: wireMsg.Amount, - EntryType: Add, - HtlcIndex: wireMsg.ID, - LogIndex: logUpdate.LogIndex, - addCommitHeightRemote: commitHeight, - OnionBlob: wireMsg.OnionBlob, - BlindingPoint: wireMsg.BlindingPoint, - CustomRecords: wireMsg.CustomRecords.Copy(), + ChanID: wireMsg.ChanID, + RHash: wireMsg.PaymentHash, + Timeout: wireMsg.Expiry, + Amount: wireMsg.Amount, + EntryType: Add, + HtlcIndex: wireMsg.ID, + LogIndex: logUpdate.LogIndex, + OnionBlob: wireMsg.OnionBlob, + BlindingPoint: wireMsg.BlindingPoint, + CustomRecords: wireMsg.CustomRecords.Copy(), + addCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, } isDustRemote := HtlcIsDust( @@ -1098,14 +1100,16 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) pd = &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - RPreimage: wireMsg.PaymentPreimage, - LogIndex: logUpdate.LogIndex, - ParentIndex: ogHTLC.HtlcIndex, - EntryType: Settle, - removeCommitHeightRemote: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + RPreimage: wireMsg.PaymentPreimage, + LogIndex: logUpdate.LogIndex, + ParentIndex: ogHTLC.HtlcIndex, + EntryType: Settle, + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, } // If we sent a failure for a prior incoming HTLC, then we'll consult @@ -1116,14 +1120,16 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) pd = &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - ParentIndex: ogHTLC.HtlcIndex, - LogIndex: logUpdate.LogIndex, - EntryType: Fail, - FailReason: wireMsg.Reason[:], - removeCommitHeightRemote: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + ParentIndex: ogHTLC.HtlcIndex, + LogIndex: logUpdate.LogIndex, + EntryType: Fail, + FailReason: wireMsg.Reason[:], + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, } // HTLC fails due to malformed onion blobs are treated the exact same @@ -1133,15 +1139,17 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // TODO(roasbeef): err if nil? pd = &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - ParentIndex: ogHTLC.HtlcIndex, - LogIndex: logUpdate.LogIndex, - EntryType: MalformedFail, - FailCode: wireMsg.FailureCode, - ShaOnionBlob: wireMsg.ShaOnionBlob, - removeCommitHeightRemote: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + ParentIndex: ogHTLC.HtlcIndex, + LogIndex: logUpdate.LogIndex, + EntryType: MalformedFail, + FailCode: wireMsg.FailureCode, + ShaOnionBlob: wireMsg.ShaOnionBlob, + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, } // For fee updates we'll create a FeeUpdate type to add to the log. We @@ -1157,9 +1165,13 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, Amount: lnwire.NewMSatFromSatoshis( btcutil.Amount(wireMsg.FeePerKw), ), - EntryType: FeeUpdate, - addCommitHeightRemote: commitHeight, - removeCommitHeightRemote: commitHeight, + EntryType: FeeUpdate, + addCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, } } @@ -1189,14 +1201,16 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) return &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - RPreimage: wireMsg.PaymentPreimage, - LogIndex: logUpdate.LogIndex, - ParentIndex: ogHTLC.HtlcIndex, - EntryType: Settle, - removeCommitHeightRemote: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + RPreimage: wireMsg.PaymentPreimage, + LogIndex: logUpdate.LogIndex, + ParentIndex: ogHTLC.HtlcIndex, + EntryType: Settle, + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, }, nil // If we sent a failure for a prior incoming HTLC, then we'll consult the @@ -1206,14 +1220,16 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) return &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - ParentIndex: ogHTLC.HtlcIndex, - LogIndex: logUpdate.LogIndex, - EntryType: Fail, - FailReason: wireMsg.Reason[:], - removeCommitHeightRemote: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + ParentIndex: ogHTLC.HtlcIndex, + LogIndex: logUpdate.LogIndex, + EntryType: Fail, + FailReason: wireMsg.Reason[:], + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, }, nil // HTLC fails due to malformed onion blocks are treated the exact same @@ -1222,15 +1238,17 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) return &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - ParentIndex: ogHTLC.HtlcIndex, - LogIndex: logUpdate.LogIndex, - EntryType: MalformedFail, - FailCode: wireMsg.FailureCode, - ShaOnionBlob: wireMsg.ShaOnionBlob, - removeCommitHeightRemote: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + ParentIndex: ogHTLC.HtlcIndex, + LogIndex: logUpdate.LogIndex, + EntryType: MalformedFail, + FailCode: wireMsg.FailureCode, + ShaOnionBlob: wireMsg.ShaOnionBlob, + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, }, nil case *lnwire.UpdateFee: @@ -1240,9 +1258,13 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda Amount: lnwire.NewMSatFromSatoshis( btcutil.Amount(wireMsg.FeePerKw), ), - EntryType: FeeUpdate, - addCommitHeightRemote: commitHeight, - removeCommitHeightRemote: commitHeight, + EntryType: FeeUpdate, + addCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, + removeCommitHeights: lntypes.Dual[uint64]{ + Remote: commitHeight, + }, }, nil default: @@ -1267,17 +1289,19 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd switch wireMsg := logUpdate.UpdateMsg.(type) { case *lnwire.UpdateAddHTLC: pd := &paymentDescriptor{ - ChanID: wireMsg.ChanID, - RHash: wireMsg.PaymentHash, - Timeout: wireMsg.Expiry, - Amount: wireMsg.Amount, - EntryType: Add, - HtlcIndex: wireMsg.ID, - LogIndex: logUpdate.LogIndex, - addCommitHeightLocal: commitHeight, - OnionBlob: wireMsg.OnionBlob, - BlindingPoint: wireMsg.BlindingPoint, - CustomRecords: wireMsg.CustomRecords.Copy(), + ChanID: wireMsg.ChanID, + RHash: wireMsg.PaymentHash, + Timeout: wireMsg.Expiry, + Amount: wireMsg.Amount, + EntryType: Add, + HtlcIndex: wireMsg.ID, + LogIndex: logUpdate.LogIndex, + OnionBlob: wireMsg.OnionBlob, + BlindingPoint: wireMsg.BlindingPoint, + CustomRecords: wireMsg.CustomRecords.Copy(), + addCommitHeights: lntypes.Dual[uint64]{ + Local: commitHeight, + }, } // We don't need to generate an htlc script yet. This will be @@ -1292,14 +1316,16 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) return &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - RPreimage: wireMsg.PaymentPreimage, - LogIndex: logUpdate.LogIndex, - ParentIndex: ogHTLC.HtlcIndex, - EntryType: Settle, - removeCommitHeightLocal: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + RPreimage: wireMsg.PaymentPreimage, + LogIndex: logUpdate.LogIndex, + ParentIndex: ogHTLC.HtlcIndex, + EntryType: Settle, + removeCommitHeights: lntypes.Dual[uint64]{ + Local: commitHeight, + }, }, nil // If we received a failure for a prior outgoing HTLC, then we'll @@ -1309,14 +1335,16 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) return &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - ParentIndex: ogHTLC.HtlcIndex, - LogIndex: logUpdate.LogIndex, - EntryType: Fail, - FailReason: wireMsg.Reason[:], - removeCommitHeightLocal: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + ParentIndex: ogHTLC.HtlcIndex, + LogIndex: logUpdate.LogIndex, + EntryType: Fail, + FailReason: wireMsg.Reason[:], + removeCommitHeights: lntypes.Dual[uint64]{ + Local: commitHeight, + }, }, nil // HTLC fails due to malformed onion blobs are treated the exact same @@ -1325,15 +1353,17 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) return &paymentDescriptor{ - ChanID: wireMsg.ChanID, - Amount: ogHTLC.Amount, - RHash: ogHTLC.RHash, - ParentIndex: ogHTLC.HtlcIndex, - LogIndex: logUpdate.LogIndex, - EntryType: MalformedFail, - FailCode: wireMsg.FailureCode, - ShaOnionBlob: wireMsg.ShaOnionBlob, - removeCommitHeightLocal: commitHeight, + ChanID: wireMsg.ChanID, + Amount: ogHTLC.Amount, + RHash: ogHTLC.RHash, + ParentIndex: ogHTLC.HtlcIndex, + LogIndex: logUpdate.LogIndex, + EntryType: MalformedFail, + FailCode: wireMsg.FailureCode, + ShaOnionBlob: wireMsg.ShaOnionBlob, + removeCommitHeights: lntypes.Dual[uint64]{ + Local: commitHeight, + }, }, nil // For fee updates we'll create a FeeUpdate type to add to the log. We @@ -1349,9 +1379,13 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd Amount: lnwire.NewMSatFromSatoshis( btcutil.Amount(wireMsg.FeePerKw), ), - EntryType: FeeUpdate, - addCommitHeightLocal: commitHeight, - removeCommitHeightLocal: commitHeight, + EntryType: FeeUpdate, + addCommitHeights: lntypes.Dual[uint64]{ + Local: commitHeight, + }, + removeCommitHeights: lntypes.Dual[uint64]{ + Local: commitHeight, + }, }, nil default: @@ -1584,8 +1618,9 @@ func (lc *LightningChannel) restoreStateLogs( // map we created earlier. Note that if this HTLC is not in // incomingRemoteAddHeights, the remote add height will be set // to zero, which indicates that it is not added yet. - htlc.addCommitHeightLocal = localCommitment.height - htlc.addCommitHeightRemote = incomingRemoteAddHeights[htlc.HtlcIndex] + htlc.addCommitHeights.Local = localCommitment.height + htlc.addCommitHeights.Remote = + incomingRemoteAddHeights[htlc.HtlcIndex] // Restore the htlc back to the remote log. lc.updateLogs.Remote.restoreHtlc(&htlc) @@ -1599,8 +1634,9 @@ func (lc *LightningChannel) restoreStateLogs( // As for the incoming HTLCs, we'll use the current remote // commit height as remote add height, and consult the map // created above for the local add height. - htlc.addCommitHeightRemote = remoteCommitment.height - htlc.addCommitHeightLocal = outgoingLocalAddHeights[htlc.HtlcIndex] + htlc.addCommitHeights.Remote = remoteCommitment.height + htlc.addCommitHeights.Local = + outgoingLocalAddHeights[htlc.HtlcIndex] // Restore the htlc back to the local log. lc.updateLogs.Local.restoreHtlc(&htlc) @@ -1695,15 +1731,15 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( switch payDesc.EntryType { case FeeUpdate: if heightSet { - payDesc.addCommitHeightRemote = height - payDesc.removeCommitHeightRemote = height + payDesc.addCommitHeights.Remote = height + payDesc.removeCommitHeights.Remote = height } lc.updateLogs.Remote.restoreUpdate(payDesc) default: if heightSet { - payDesc.removeCommitHeightRemote = height + payDesc.removeCommitHeights.Remote = height } lc.updateLogs.Remote.restoreUpdate(payDesc) @@ -2787,7 +2823,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // number of satoshis we've received within the channel. if mutateState && entry.EntryType == Settle && whoseCommitChain.IsLocal() && - entry.removeCommitHeightLocal == 0 { + entry.removeCommitHeights.Local == 0 { lc.channelState.TotalMSatReceived += entry.Amount } @@ -2827,7 +2863,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // channel. if mutateState && entry.EntryType == Settle && whoseCommitChain.IsLocal() && - entry.removeCommitHeightLocal == 0 { + entry.removeCommitHeights.Local == 0 { lc.channelState.TotalMSatSent += entry.Amount } @@ -2916,14 +2952,14 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // The parent add height should never be zero at this point. If // that's the case we probably forgot to send a new commitment. case whoseCommitChain.IsRemote() && - addEntry.addCommitHeightRemote == 0: + addEntry.addCommitHeights.Remote == 0: return nil, fmt.Errorf("parent entry %d for update %d "+ "had zero remote add height", entry.ParentIndex, entry.LogIndex) case whoseCommitChain.IsLocal() && - addEntry.addCommitHeightLocal == 0: + addEntry.addCommitHeights.Local == 0: return nil, fmt.Errorf("parent entry %d for update %d "+ "had zero local add height", entry.ParentIndex, @@ -2947,9 +2983,9 @@ func processAddEntry(htlc *paymentDescriptor, ourBalance, // height w.r.t the local chain. var addHeight *uint64 if whoseCommitChain.IsRemote() { - addHeight = &htlc.addCommitHeightRemote + addHeight = &htlc.addCommitHeights.Remote } else { - addHeight = &htlc.addCommitHeightLocal + addHeight = &htlc.addCommitHeights.Local } if *addHeight != 0 { @@ -2981,9 +3017,9 @@ func processRemoveEntry(htlc *paymentDescriptor, ourBalance, var removeHeight *uint64 if whoseCommitChain.IsRemote() { - removeHeight = &htlc.removeCommitHeightRemote + removeHeight = &htlc.removeCommitHeights.Remote } else { - removeHeight = &htlc.removeCommitHeightLocal + removeHeight = &htlc.removeCommitHeights.Local } // Ignore any removal entries which have already been processed. @@ -3034,11 +3070,11 @@ func processFeeUpdate(feeUpdate *paymentDescriptor, nextHeight uint64, var addHeight *uint64 var removeHeight *uint64 if whoseCommitChain.IsRemote() { - addHeight = &feeUpdate.addCommitHeightRemote - removeHeight = &feeUpdate.removeCommitHeightRemote + addHeight = &feeUpdate.addCommitHeights.Remote + removeHeight = &feeUpdate.removeCommitHeights.Remote } else { - addHeight = &feeUpdate.addCommitHeightLocal - removeHeight = &feeUpdate.removeCommitHeightLocal + addHeight = &feeUpdate.addCommitHeights.Local + removeHeight = &feeUpdate.removeCommitHeights.Local } if *addHeight != 0 { @@ -3292,8 +3328,8 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, // If this entry wasn't committed at the exact height of this // remote commitment, then we'll skip it as it was already // lingering in the log. - if pd.addCommitHeightRemote != newCommit.height && - pd.removeCommitHeightRemote != newCommit.height { + if pd.addCommitHeights.Remote != newCommit.height && + pd.removeCommitHeights.Remote != newCommit.height { continue } @@ -5353,19 +5389,20 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // both of the remote and local heights are non-zero. If either // of these values is zero, it has yet to be committed in both // the local and remote chains. - committedAdd := pd.addCommitHeightRemote > 0 && - pd.addCommitHeightLocal > 0 - committedRmv := pd.removeCommitHeightRemote > 0 && - pd.removeCommitHeightLocal > 0 + committedAdd := pd.addCommitHeights.Remote > 0 && + pd.addCommitHeights.Local > 0 + committedRmv := pd.removeCommitHeights.Remote > 0 && + pd.removeCommitHeights.Local > 0 // Using the height of the remote and local commitments, // preemptively compute whether or not to forward this HTLC for // the case in which this in an Add HTLC, or if this is a // Settle, Fail, or MalformedFail. - shouldFwdAdd := remoteChainTail == pd.addCommitHeightRemote && - localChainTail >= pd.addCommitHeightLocal - shouldFwdRmv := remoteChainTail == pd.removeCommitHeightRemote && - localChainTail >= pd.removeCommitHeightLocal + shouldFwdAdd := remoteChainTail == pd.addCommitHeights.Remote && + localChainTail >= pd.addCommitHeights.Local + shouldFwdRmv := remoteChainTail == + pd.removeCommitHeights.Remote && + localChainTail >= pd.removeCommitHeights.Local // We'll only forward any new HTLC additions iff, it's "freshly // locked in". Meaning that the HTLC was only *just* considered diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 744be3ae5a7..562610db465 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -7441,13 +7441,13 @@ func TestChannelRestoreCommitHeight(t *testing.T) { t.Fatalf("htlc not found in log") } - if pd.addCommitHeightLocal != expLocal { + if pd.addCommitHeights.Local != expLocal { t.Fatalf("expected local add height to be %d, was %d", - expLocal, pd.addCommitHeightLocal) + expLocal, pd.addCommitHeights.Local) } - if pd.addCommitHeightRemote != expRemote { + if pd.addCommitHeights.Remote != expRemote { t.Fatalf("expected remote add height to be %d, was %d", - expRemote, pd.addCommitHeightRemote) + expRemote, pd.addCommitHeights.Remote) } return newChannel } @@ -8229,16 +8229,20 @@ func TestFetchParent(t *testing.T) { remoteEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { - HtlcIndex: 1, - addCommitHeightLocal: 100, - addCommitHeightRemote: 100, + HtlcIndex: 1, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 100, + }, }, // This entry will be added at log index =1, it // is the parent entry we are looking for. { - HtlcIndex: 2, - addCommitHeightLocal: 100, - addCommitHeightRemote: 0, + HtlcIndex: 2, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 0, + }, }, }, whoseCommitChain: lntypes.Remote, @@ -8251,16 +8255,20 @@ func TestFetchParent(t *testing.T) { remoteEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { - HtlcIndex: 1, - addCommitHeightLocal: 100, - addCommitHeightRemote: 100, + HtlcIndex: 1, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 100, + }, }, // This entry will be added at log index =1, it // is the parent entry we are looking for. { - HtlcIndex: 2, - addCommitHeightLocal: 0, - addCommitHeightRemote: 100, + HtlcIndex: 2, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 0, + Remote: 100, + }, }, }, localEntries: nil, @@ -8274,16 +8282,20 @@ func TestFetchParent(t *testing.T) { localEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { - HtlcIndex: 1, - addCommitHeightLocal: 100, - addCommitHeightRemote: 100, + HtlcIndex: 1, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 100, + }, }, // This entry will be added at log index =1, it // is the parent entry we are looking for. { - HtlcIndex: 2, - addCommitHeightLocal: 0, - addCommitHeightRemote: 100, + HtlcIndex: 2, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 0, + Remote: 100, + }, }, }, remoteEntries: nil, @@ -8298,16 +8310,20 @@ func TestFetchParent(t *testing.T) { localEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { - HtlcIndex: 1, - addCommitHeightLocal: 100, - addCommitHeightRemote: 100, + HtlcIndex: 1, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 100, + }, }, // This entry will be added at log index =1, it // is the parent entry we are looking for. { - HtlcIndex: 2, - addCommitHeightLocal: 100, - addCommitHeightRemote: 0, + HtlcIndex: 2, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 0, + }, }, }, remoteEntries: nil, @@ -8322,16 +8338,20 @@ func TestFetchParent(t *testing.T) { remoteEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { - HtlcIndex: 1, - addCommitHeightLocal: 100, - addCommitHeightRemote: 0, + HtlcIndex: 1, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 0, + }, }, // This entry will be added at log index =1, it // is the parent entry we are looking for. { - HtlcIndex: 2, - addCommitHeightLocal: 100, - addCommitHeightRemote: 100, + HtlcIndex: 2, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 100, + }, }, }, whoseCommitChain: lntypes.Remote, @@ -8345,16 +8365,20 @@ func TestFetchParent(t *testing.T) { localEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { - HtlcIndex: 1, - addCommitHeightLocal: 0, - addCommitHeightRemote: 100, + HtlcIndex: 1, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 0, + Remote: 100, + }, }, // This entry will be added at log index =1, it // is the parent entry we are looking for. { - HtlcIndex: 2, - addCommitHeightLocal: 100, - addCommitHeightRemote: 100, + HtlcIndex: 2, + addCommitHeights: lntypes.Dual[uint64]{ + Local: 100, + Remote: 100, + }, }, }, remoteEntries: nil, @@ -8555,10 +8579,12 @@ func TestEvaluateView(t *testing.T) { mutateState: true, ourHtlcs: []*paymentDescriptor{ { - HtlcIndex: 0, - Amount: htlcAddAmount, - EntryType: Add, - addCommitHeightLocal: addHeight, + HtlcIndex: 0, + Amount: htlcAddAmount, + EntryType: Add, + addCommitHeights: lntypes.Dual[uint64]{ + Local: addHeight, + }, }, }, theirHtlcs: []*paymentDescriptor{ @@ -8590,10 +8616,12 @@ func TestEvaluateView(t *testing.T) { mutateState: false, ourHtlcs: []*paymentDescriptor{ { - HtlcIndex: 0, - Amount: htlcAddAmount, - EntryType: Add, - addCommitHeightLocal: addHeight, + HtlcIndex: 0, + Amount: htlcAddAmount, + EntryType: Add, + addCommitHeights: lntypes.Dual[uint64]{ + Local: addHeight, + }, }, }, theirHtlcs: []*paymentDescriptor{ @@ -8640,16 +8668,20 @@ func TestEvaluateView(t *testing.T) { }, theirHtlcs: []*paymentDescriptor{ { - HtlcIndex: 0, - Amount: htlcAddAmount, - EntryType: Add, - addCommitHeightLocal: addHeight, + HtlcIndex: 0, + Amount: htlcAddAmount, + EntryType: Add, + addCommitHeights: lntypes.Dual[uint64]{ + Local: addHeight, + }, }, { - HtlcIndex: 1, - Amount: htlcAddAmount, - EntryType: Add, - addCommitHeightLocal: addHeight, + HtlcIndex: 1, + Amount: htlcAddAmount, + EntryType: Add, + addCommitHeights: lntypes.Dual[uint64]{ + Local: addHeight, + }, }, }, expectedFee: feePerKw, @@ -8684,10 +8716,12 @@ func TestEvaluateView(t *testing.T) { }, theirHtlcs: []*paymentDescriptor{ { - HtlcIndex: 0, - Amount: htlcAddAmount, - EntryType: Add, - addCommitHeightLocal: addHeight, + HtlcIndex: 0, + Amount: htlcAddAmount, + EntryType: Add, + addCommitHeights: lntypes.Dual[uint64]{ + Local: addHeight, + }, }, }, expectedFee: feePerKw, @@ -8982,12 +9016,16 @@ func TestProcessFeeUpdate(t *testing.T) { // set in the test. heights := test.startHeights update := &paymentDescriptor{ - Amount: ourFeeUpdateAmt, - addCommitHeightRemote: heights.remoteAdd, - addCommitHeightLocal: heights.localAdd, - removeCommitHeightRemote: heights.remoteRemove, - removeCommitHeightLocal: heights.localRemove, - EntryType: FeeUpdate, + Amount: ourFeeUpdateAmt, + addCommitHeights: lntypes.Dual[uint64]{ + Local: heights.localAdd, + Remote: heights.remoteAdd, + }, + removeCommitHeights: lntypes.Dual[uint64]{ + Local: heights.localRemove, + Remote: heights.remoteRemove, + }, + EntryType: FeeUpdate, } view := &HtlcView{ @@ -9010,10 +9048,10 @@ func TestProcessFeeUpdate(t *testing.T) { func checkHeights(t *testing.T, update *paymentDescriptor, expected heights) { updateHeights := heights{ - localAdd: update.addCommitHeightLocal, - localRemove: update.removeCommitHeightLocal, - remoteAdd: update.addCommitHeightRemote, - remoteRemove: update.removeCommitHeightRemote, + localAdd: update.addCommitHeights.Local, + localRemove: update.removeCommitHeights.Local, + remoteAdd: update.addCommitHeights.Remote, + remoteRemove: update.removeCommitHeights.Remote, } if !reflect.DeepEqual(updateHeights, expected) { @@ -9378,12 +9416,16 @@ func TestProcessAddRemoveEntry(t *testing.T) { heights := test.startHeights update := &paymentDescriptor{ - Amount: updateAmount, - addCommitHeightLocal: heights.localAdd, - addCommitHeightRemote: heights.remoteAdd, - removeCommitHeightLocal: heights.localRemove, - removeCommitHeightRemote: heights.remoteRemove, - EntryType: test.updateType, + Amount: updateAmount, + addCommitHeights: lntypes.Dual[uint64]{ + Local: heights.localAdd, + Remote: heights.remoteAdd, + }, + removeCommitHeights: lntypes.Dual[uint64]{ + Local: heights.localRemove, + Remote: heights.remoteRemove, + }, + EntryType: test.updateType, } var ( diff --git a/lnwallet/payment_descriptor.go b/lnwallet/payment_descriptor.go index 5a51f29ce54..ffa4cc8ce1c 100644 --- a/lnwallet/payment_descriptor.go +++ b/lnwallet/payment_descriptor.go @@ -6,6 +6,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" ) @@ -165,16 +166,14 @@ type paymentDescriptor struct { // which included this HTLC on either the remote or local commitment // chain. This value is used to determine when an HTLC is fully // "locked-in". - addCommitHeightRemote uint64 - addCommitHeightLocal uint64 + addCommitHeights lntypes.Dual[uint64] // removeCommitHeight[Remote|Local] encodes the height of the // commitment which removed the parent pointer of this // paymentDescriptor either due to a timeout or a settle. Once both // these heights are below the tail of both chains, the log entries can // safely be removed. - removeCommitHeightRemote uint64 - removeCommitHeightLocal uint64 + removeCommitHeights lntypes.Dual[uint64] // OnionBlob is an opaque blob which is used to complete multi-hop // routing. diff --git a/lnwallet/update_log.go b/lnwallet/update_log.go index 42e5373a22b..2d1f65c9fa3 100644 --- a/lnwallet/update_log.go +++ b/lnwallet/update_log.go @@ -153,6 +153,7 @@ func compactLogs(ourLog, theirLog *updateLog, nextA = e.Next() htlc := e.Value + rmvHeights := htlc.removeCommitHeights // We skip Adds, as they will be removed along with the // fail/settles below. @@ -162,9 +163,7 @@ func compactLogs(ourLog, theirLog *updateLog, // If the HTLC hasn't yet been removed from either // chain, the skip it. - if htlc.removeCommitHeightRemote == 0 || - htlc.removeCommitHeightLocal == 0 { - + if rmvHeights.Remote == 0 || rmvHeights.Local == 0 { continue } @@ -172,8 +171,8 @@ func compactLogs(ourLog, theirLog *updateLog, // is at least the height in which the HTLC was // removed, then evict the settle/timeout entry along // with the original add entry. - if remoteChainTail >= htlc.removeCommitHeightRemote && - localChainTail >= htlc.removeCommitHeightLocal { + if remoteChainTail >= rmvHeights.Remote && + localChainTail >= rmvHeights.Local { // Fee updates have no parent htlcs, so we only // remove the update itself. From d168f69c91988676875064ea624066ab030d1a36 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Tue, 16 Jul 2024 15:47:14 -0700 Subject: [PATCH 02/22] lnwallet: consolidate redundant cases using Dual.ForParty --- lnwallet/channel.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 1b505ee1ec6..265e3fee6d0 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2951,19 +2951,10 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // The parent add height should never be zero at this point. If // that's the case we probably forgot to send a new commitment. - case whoseCommitChain.IsRemote() && - addEntry.addCommitHeights.Remote == 0: - + case addEntry.addCommitHeights.GetForParty(whoseCommitChain) == 0: return nil, fmt.Errorf("parent entry %d for update %d "+ - "had zero remote add height", entry.ParentIndex, - entry.LogIndex) - - case whoseCommitChain.IsLocal() && - addEntry.addCommitHeights.Local == 0: - - return nil, fmt.Errorf("parent entry %d for update %d "+ - "had zero local add height", entry.ParentIndex, - entry.LogIndex) + "had zero %v add height", entry.ParentIndex, + entry.LogIndex, whoseCommitChain) } return addEntry, nil @@ -4613,7 +4604,9 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, ) } - hashCache := input.NewTxSigHashesV0Only(successTx) + hashCache := + input.NewTxSigHashesV0Only(successTx) + sigHash, err := txscript.CalcWitnessSigHash( htlc.ourWitnessScript, hashCache, sigHashType, successTx, 0, From 15456d517d2d404311af19d102c728bc4005a177 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 17 Jul 2024 13:09:04 -0700 Subject: [PATCH 03/22] lnwallet: eliminate inner-most layer of evil mutateState nonsense This commit begins the process of moving towards a more principled means of state tracking. We eliminate the mutateState argument from processAddEntry and processRemoveEntry and move the responsibility of mutating said state to the call-sites. The current call-sites of these functions still have their *own* mutateState argument which will be eliminated during upcoming commits. However, following the principle of micro-commits I opted to break these changes up to make review simpler. --- lnwallet/channel.go | 125 ++++++++++++++++++++++----------------- lnwallet/channel_test.go | 21 +++++-- 2 files changed, 86 insertions(+), 60 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 265e3fee6d0..60b1920f16b 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2837,10 +2837,20 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, skipThem[addEntry.HtlcIndex] = struct{}{} - processRemoveEntry( - entry, ourBalance, theirBalance, nextHeight, - whoseCommitChain, true, mutateState, - ) + rmvHeights := &entry.removeCommitHeights + rmvHeight := rmvHeights.GetForParty(whoseCommitChain) + if rmvHeight == 0 { + processRemoveEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, true, + ) + + if mutateState { + rmvHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } } for _, entry := range view.TheirUpdates { switch entry.EntryType { @@ -2877,10 +2887,20 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, skipUs[addEntry.HtlcIndex] = struct{}{} - processRemoveEntry( - entry, ourBalance, theirBalance, nextHeight, - whoseCommitChain, false, mutateState, - ) + rmvHeights := &entry.removeCommitHeights + rmvHeight := rmvHeights.GetForParty(whoseCommitChain) + if rmvHeight == 0 { + processRemoveEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, false, + ) + + if mutateState { + rmvHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } } // Next we take a second pass through all the log entries, skipping any @@ -2892,10 +2912,25 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, continue } - processAddEntry( - entry, ourBalance, theirBalance, nextHeight, - whoseCommitChain, false, mutateState, - ) + // Skip the entries that have already had their add commit + // height set for this commit chain. + addHeights := &entry.addCommitHeights + addHeight := addHeights.GetForParty(whoseCommitChain) + if addHeight == 0 { + processAddEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, false, + ) + + // If we are mutating the state, then set the add + // height for the appropriate commitment chain to the + // next height. + if mutateState { + addHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } newView.OurUpdates = append(newView.OurUpdates, entry) } @@ -2905,10 +2940,25 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, continue } - processAddEntry( - entry, ourBalance, theirBalance, nextHeight, - whoseCommitChain, true, mutateState, - ) + // Skip the entries that have already had their add commit + // height set for this commit chain. + addHeights := &entry.addCommitHeights + addHeight := addHeights.GetForParty(whoseCommitChain) + if addHeight == 0 { + processAddEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, true, + ) + + // If we are mutating the state, then set the add + // height for the appropriate commitment chain to the + // next height. + if mutateState { + addHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } newView.TheirUpdates = append(newView.TheirUpdates, entry) } @@ -2965,23 +3015,8 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // was committed is updated. Keeping track of this inclusion height allows us to // later compact the log once the change is fully committed in both chains. func processAddEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty, isIncoming, mutateState bool) { - - // If we're evaluating this entry for the remote chain (to create/view - // a new commitment), then we'll may be updating the height this entry - // was added to the chain. Otherwise, we may be updating the entry's - // height w.r.t the local chain. - var addHeight *uint64 - if whoseCommitChain.IsRemote() { - addHeight = &htlc.addCommitHeights.Remote - } else { - addHeight = &htlc.addCommitHeights.Local - } - - if *addHeight != 0 { - return - } + theirBalance *lnwire.MilliSatoshi, _ uint64, + _ lntypes.ChannelParty, isIncoming bool) { if isIncoming { // If this is a new incoming (un-committed) HTLC, then we need @@ -2993,30 +3028,14 @@ func processAddEntry(htlc *paymentDescriptor, ourBalance, // going HTLC to reflect the pending balance. *ourBalance -= htlc.Amount } - - if mutateState { - *addHeight = nextHeight - } } // processRemoveEntry processes a log entry which settles or times out a // previously added HTLC. If the removal entry has already been processed, it // is skipped. func processRemoveEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty, isIncoming, mutateState bool) { - - var removeHeight *uint64 - if whoseCommitChain.IsRemote() { - removeHeight = &htlc.removeCommitHeights.Remote - } else { - removeHeight = &htlc.removeCommitHeights.Local - } - - // Ignore any removal entries which have already been processed. - if *removeHeight != 0 { - return - } + theirBalance *lnwire.MilliSatoshi, _ uint64, + _ lntypes.ChannelParty, isIncoming bool) { switch { // If an incoming HTLC is being settled, then this means that we've @@ -3043,10 +3062,6 @@ func processRemoveEntry(htlc *paymentDescriptor, ourBalance, case !isIncoming && (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): *ourBalance += htlc.Amount } - - if mutateState { - *removeHeight = nextHeight - } } // processFeeUpdate processes a log update that updates the current commitment diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 562610db465..0a3188e0b8b 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9440,15 +9440,26 @@ func TestProcessAddRemoveEntry(t *testing.T) { // update type. Process remove is used for settles, // fails and malformed htlcs. process := processRemoveEntry + heightDual := &update.removeCommitHeights if test.updateType == Add { process = processAddEntry + heightDual = &update.addCommitHeights } - process( - update, &ourBalance, &theirBalance, nextHeight, - test.whoseCommitChain, test.isIncoming, - test.mutateState, - ) + if heightDual.GetForParty(test.whoseCommitChain) == 0 { + process( + update, &ourBalance, &theirBalance, + nextHeight, test.whoseCommitChain, + test.isIncoming, + ) + + if test.mutateState { + heightDual.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + } + } // Check that balances were updated as expected. if ourBalance != test.ourExpectedBalance { From 749448dc115326243f46bacbd514b069a49a9831 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 17 Jul 2024 13:35:32 -0700 Subject: [PATCH 04/22] lnwallet: bring processFeeUpdate in line with process[Add|Remove]Entry This commit redoes the API and semantics of processFeeUpdate to make it consistent with the semantics of it's sister functions. This is part of an ongoing series of commits to remove mutateState arguments pervasively from the codebase. As with the previous commit this makes state mutation the caller's responsibility. This temporarily increases code duplication at the call-sites, but this will unlock other refactor opportunities. --- lnwallet/channel.go | 74 +++++++++++++++++++++++----------------- lnwallet/channel_test.go | 25 ++++++++++++-- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 60b1920f16b..efc82a8a286 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2811,10 +2811,26 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // Process fee updates, updating the current feePerKw. case FeeUpdate: - processFeeUpdate( - entry, nextHeight, whoseCommitChain, - mutateState, newView, + h := entry.addCommitHeights.GetForParty( + whoseCommitChain, ) + + if h == 0 { + processFeeUpdate( + entry, &newView.FeePerKw, nextHeight, + whoseCommitChain, + ) + + if mutateState { + entry.addCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + + entry.removeCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } continue } @@ -2860,10 +2876,26 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // Process fee updates, updating the current feePerKw. case FeeUpdate: - processFeeUpdate( - entry, nextHeight, whoseCommitChain, - mutateState, newView, + h := entry.addCommitHeights.GetForParty( + whoseCommitChain, ) + + if h == 0 { + processFeeUpdate( + entry, &newView.FeePerKw, nextHeight, + whoseCommitChain, + ) + + if mutateState { + entry.addCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + + entry.removeCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } continue } @@ -3066,35 +3098,13 @@ func processRemoveEntry(htlc *paymentDescriptor, ourBalance, // processFeeUpdate processes a log update that updates the current commitment // fee. -func processFeeUpdate(feeUpdate *paymentDescriptor, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty, mutateState bool, - view *HtlcView) { - - // Fee updates are applied for all commitments after they are - // sent/received, so we consider them being added and removed at the - // same height. - var addHeight *uint64 - var removeHeight *uint64 - if whoseCommitChain.IsRemote() { - addHeight = &feeUpdate.addCommitHeights.Remote - removeHeight = &feeUpdate.removeCommitHeights.Remote - } else { - addHeight = &feeUpdate.addCommitHeights.Local - removeHeight = &feeUpdate.removeCommitHeights.Local - } - - if *addHeight != 0 { - return - } +func processFeeUpdate(feeUpdate *paymentDescriptor, + feeRef *chainfee.SatPerKWeight, nextHeight uint64, + whoseCommitChain lntypes.ChannelParty) { // If the update wasn't already locked in, update the current fee rate // to reflect this update. - view.FeePerKw = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis()) - - if mutateState { - *addHeight = nextHeight - *removeHeight = nextHeight - } + *feeRef = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis()) } // generateRemoteHtlcSigJobs generates a series of HTLC signature jobs for the diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 0a3188e0b8b..c52fdfc1f68 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9031,11 +9031,30 @@ func TestProcessFeeUpdate(t *testing.T) { view := &HtlcView{ FeePerKw: chainfee.SatPerKWeight(feePerKw), } - processFeeUpdate( - update, nextHeight, test.whoseCommitChain, - test.mutate, view, + + h := update.addCommitHeights.GetForParty( + test.whoseCommitChain, ) + if h == 0 { + processFeeUpdate( + update, &view.FeePerKw, nextHeight, + test.whoseCommitChain, + ) + + if test.mutate { + update.addCommitHeights.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + + update.removeCommitHeights.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + } + } + if view.FeePerKw != test.expectedFee { t.Fatalf("expected fee: %v, got: %v", test.expectedFee, feePerKw) From b0374dfde5de052e2a4106e2f3e08f0391c4df34 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Thu, 18 Jul 2024 17:14:13 -0700 Subject: [PATCH 05/22] lnwallet: inline processUpdateFee and remove the function entirely In this commit we observe that the previous commit reduced the role of this function to a single assignment statement with numerous newly irrelevant parameters. This commit makes the choice of inlining it at the two call-sites within evaluateHTLCView and removing the funciton definition entirely. This also allows us to drop a huge portion of newly irrelevant test code. --- lnwallet/channel.go | 28 ++--- lnwallet/channel_test.go | 217 --------------------------------------- 2 files changed, 11 insertions(+), 234 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index efc82a8a286..b5f328ffb3e 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2816,9 +2816,11 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, ) if h == 0 { - processFeeUpdate( - entry, &newView.FeePerKw, nextHeight, - whoseCommitChain, + // If the update wasn't already locked in, + // update the current fee rate to reflect this + // update. + newView.FeePerKw = chainfee.SatPerKWeight( + entry.Amount.ToSatoshis(), ) if mutateState { @@ -2881,11 +2883,14 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, ) if h == 0 { - processFeeUpdate( - entry, &newView.FeePerKw, nextHeight, - whoseCommitChain, + // If the update wasn't already locked in, + // update the current fee rate to reflect this + // update. + newView.FeePerKw = chainfee.SatPerKWeight( + entry.Amount.ToSatoshis(), ) + if mutateState { entry.addCommitHeights.SetForParty( whoseCommitChain, nextHeight, @@ -3096,17 +3101,6 @@ func processRemoveEntry(htlc *paymentDescriptor, ourBalance, } } -// processFeeUpdate processes a log update that updates the current commitment -// fee. -func processFeeUpdate(feeUpdate *paymentDescriptor, - feeRef *chainfee.SatPerKWeight, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty) { - - // If the update wasn't already locked in, update the current fee rate - // to reflect this update. - *feeRef = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis()) -} - // generateRemoteHtlcSigJobs generates a series of HTLC signature jobs for the // sig pool, along with a channel that if closed, will cancel any jobs after // they have been submitted to the sigPool. This method is to be used when diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index c52fdfc1f68..164ec7f3511 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8848,223 +8848,6 @@ type heights struct { remoteRemove uint64 } -// TestProcessFeeUpdate tests the applying of fee updates and mutation of -// local and remote add and remove heights on update messages. -func TestProcessFeeUpdate(t *testing.T) { - const ( - // height is a non-zero height that can be used for htlcs - // heights. - height = 200 - - // nextHeight is a constant that we use for the next height in - // all unit tests. - nextHeight = 400 - - // feePerKw is the fee we start all of our unit tests with. - feePerKw = 1 - - // ourFeeUpdateAmt is an amount that we update fees to expressed - // in msat. - ourFeeUpdateAmt = 20000 - - // ourFeeUpdatePerSat is the fee rate *in satoshis* that we - // expect if we update to ourFeeUpdateAmt. - ourFeeUpdatePerSat = chainfee.SatPerKWeight(20) - ) - - tests := []struct { - name string - startHeights heights - expectedHeights heights - whoseCommitChain lntypes.ChannelParty - mutate bool - expectedFee chainfee.SatPerKWeight - }{ - { - // Looking at local chain, local add is non-zero so - // the update has been applied already; no fee change. - name: "non-zero local height, fee unchanged", - startHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: 0, - remoteRemove: height, - }, - expectedHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: 0, - remoteRemove: height, - }, - whoseCommitChain: lntypes.Local, - mutate: false, - expectedFee: feePerKw, - }, - { - // Looking at local chain, local add is zero so the - // update has not been applied yet; we expect a fee - // update. - name: "zero local height, fee changed", - startHeights: heights{ - localAdd: 0, - localRemove: 0, - remoteAdd: height, - remoteRemove: 0, - }, - expectedHeights: heights{ - localAdd: 0, - localRemove: 0, - remoteAdd: height, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - mutate: false, - expectedFee: ourFeeUpdatePerSat, - }, - { - // Looking at remote chain, the remote add height is - // zero, so the update has not been applied so we expect - // a fee change. - name: "zero remote height, fee changed", - startHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: 0, - remoteRemove: 0, - }, - expectedHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - mutate: false, - expectedFee: ourFeeUpdatePerSat, - }, - { - // Looking at remote chain, the remote add height is - // non-zero, so the update has been applied so we expect - // no fee change. - name: "non-zero remote height, no fee change", - startHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: height, - remoteRemove: 0, - }, - expectedHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: height, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - mutate: false, - expectedFee: feePerKw, - }, - { - // Local add height is non-zero, so the update has - // already been applied; we do not expect fee to - // change or any mutations to be applied. - name: "non-zero local height, mutation not applied", - startHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: 0, - remoteRemove: height, - }, - expectedHeights: heights{ - localAdd: height, - localRemove: 0, - remoteAdd: 0, - remoteRemove: height, - }, - whoseCommitChain: lntypes.Local, - mutate: true, - expectedFee: feePerKw, - }, - { - // Local add is zero and we are looking at our local - // chain, so the update has not been applied yet. We - // expect the local add and remote heights to be - // mutated. - name: "zero height, fee changed, mutation applied", - startHeights: heights{ - localAdd: 0, - localRemove: 0, - remoteAdd: 0, - remoteRemove: 0, - }, - expectedHeights: heights{ - localAdd: nextHeight, - localRemove: nextHeight, - remoteAdd: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - mutate: true, - expectedFee: ourFeeUpdatePerSat, - }, - } - - for _, test := range tests { - test := test - - t.Run(test.name, func(t *testing.T) { - // Create a fee update with add and remove heights as - // set in the test. - heights := test.startHeights - update := &paymentDescriptor{ - Amount: ourFeeUpdateAmt, - addCommitHeights: lntypes.Dual[uint64]{ - Local: heights.localAdd, - Remote: heights.remoteAdd, - }, - removeCommitHeights: lntypes.Dual[uint64]{ - Local: heights.localRemove, - Remote: heights.remoteRemove, - }, - EntryType: FeeUpdate, - } - - view := &HtlcView{ - FeePerKw: chainfee.SatPerKWeight(feePerKw), - } - - h := update.addCommitHeights.GetForParty( - test.whoseCommitChain, - ) - - if h == 0 { - processFeeUpdate( - update, &view.FeePerKw, nextHeight, - test.whoseCommitChain, - ) - - if test.mutate { - update.addCommitHeights.SetForParty( - test.whoseCommitChain, - nextHeight, - ) - - update.removeCommitHeights.SetForParty( - test.whoseCommitChain, - nextHeight, - ) - } - } - - if view.FeePerKw != test.expectedFee { - t.Fatalf("expected fee: %v, got: %v", - test.expectedFee, feePerKw) - } - - checkHeights(t, update, test.expectedHeights) - }) - } -} - func checkHeights(t *testing.T, update *paymentDescriptor, expected heights) { updateHeights := heights{ localAdd: update.addCommitHeights.Local, From fddc24b94549f30adbeee12a0fb64ed5d8d0afa1 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 19 Jul 2024 15:29:20 -0700 Subject: [PATCH 06/22] lnwallet: remove extraneous argument from process[Add|Remove]Entry This commit makes the observation that the nextHeight parameter of these two functions is no longer used by those funcitons themselves as a result of extracting the state mutation to the call-sites. This removes the parameter entirely. --- lnwallet/channel.go | 17 ++++++++--------- lnwallet/channel_test.go | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b5f328ffb3e..ef9a9d0a6d1 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2859,7 +2859,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, rmvHeight := rmvHeights.GetForParty(whoseCommitChain) if rmvHeight == 0 { processRemoveEntry( - entry, ourBalance, theirBalance, nextHeight, + entry, ourBalance, theirBalance, whoseCommitChain, true, ) @@ -2890,7 +2890,6 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, entry.Amount.ToSatoshis(), ) - if mutateState { entry.addCommitHeights.SetForParty( whoseCommitChain, nextHeight, @@ -2928,7 +2927,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, rmvHeight := rmvHeights.GetForParty(whoseCommitChain) if rmvHeight == 0 { processRemoveEntry( - entry, ourBalance, theirBalance, nextHeight, + entry, ourBalance, theirBalance, whoseCommitChain, false, ) @@ -2955,7 +2954,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, addHeight := addHeights.GetForParty(whoseCommitChain) if addHeight == 0 { processAddEntry( - entry, ourBalance, theirBalance, nextHeight, + entry, ourBalance, theirBalance, whoseCommitChain, false, ) @@ -2983,7 +2982,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, addHeight := addHeights.GetForParty(whoseCommitChain) if addHeight == 0 { processAddEntry( - entry, ourBalance, theirBalance, nextHeight, + entry, ourBalance, theirBalance, whoseCommitChain, true, ) @@ -3052,8 +3051,8 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // was committed is updated. Keeping track of this inclusion height allows us to // later compact the log once the change is fully committed in both chains. func processAddEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, _ uint64, - _ lntypes.ChannelParty, isIncoming bool) { + theirBalance *lnwire.MilliSatoshi, _ lntypes.ChannelParty, + isIncoming bool) { if isIncoming { // If this is a new incoming (un-committed) HTLC, then we need @@ -3071,8 +3070,8 @@ func processAddEntry(htlc *paymentDescriptor, ourBalance, // previously added HTLC. If the removal entry has already been processed, it // is skipped. func processRemoveEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, _ uint64, - _ lntypes.ChannelParty, isIncoming bool) { + theirBalance *lnwire.MilliSatoshi, _ lntypes.ChannelParty, + isIncoming bool) { switch { // If an incoming HTLC is being settled, then this means that we've diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 164ec7f3511..f45f983415d 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9251,7 +9251,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { if heightDual.GetForParty(test.whoseCommitChain) == 0 { process( update, &ourBalance, &theirBalance, - nextHeight, test.whoseCommitChain, + test.whoseCommitChain, test.isIncoming, ) From f2db72c55e1e1fe3ad0fe9e8e82f5e7dcade674a Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 19 Jul 2024 16:53:58 -0700 Subject: [PATCH 07/22] lnwallet: remove mutateState from evaluateHTLCView In line with previous commits we are progressively removing the mutateState argument from this call stack for a more principled software design approach. NOTE FOR REVIEWERS: We take a naive approach to updating the tests here and simply take the functionality we are removing from evaluateHTLCView and run it directly after the function in the test suite. It's possible that we should instead remove this from the test suite altogether but I opted to take a more conservative approach with respect to reducing the scope of tests. If you have opinions here, please make them known. --- lnwallet/channel.go | 185 +++++++++++++++++++++------------------ lnwallet/channel_test.go | 67 +++++++++++++- 2 files changed, 166 insertions(+), 86 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index ef9a9d0a6d1..7ef354f6a75 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2774,16 +2774,14 @@ func fundingTxIn(chanState *channeldb.OpenChannel) wire.TxIn { // returned reflects the current state of HTLCs within the remote or local // commitment chain, and the current commitment fee rate. // -// If mutateState is set to true, then the add height of all added HTLCs -// will be set to nextHeight, and the remove height of all removed HTLCs -// will be set to nextHeight. This should therefore only be set to true -// once for each height, and only in concert with signing a new commitment. -// TODO(halseth): return htlcs to mutate instead of mutating inside -// method. +// The return values of this function are as follows: +// 1. The new htlcView reflecting the current channel state. +// 2. A Dual of the updates which have not yet been committed in +// 'whoseCommitChain's commitment chain. func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, theirBalance *lnwire.MilliSatoshi, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty, mutateState bool) (*HtlcView, - error) { + whoseCommitChain lntypes.ChannelParty) (*HtlcView, + lntypes.Dual[[]*paymentDescriptor], error) { // We initialize the view's fee rate to the fee rate of the unfiltered // view. If any fee updates are found when evaluating the view, it will @@ -2822,35 +2820,16 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, newView.FeePerKw = chainfee.SatPerKWeight( entry.Amount.ToSatoshis(), ) - - if mutateState { - entry.addCommitHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - - entry.removeCommitHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - } } - continue - } - - // If we're settling an inbound HTLC, and it hasn't been - // processed yet, then increment our state tracking the total - // number of satoshis we've received within the channel. - if mutateState && entry.EntryType == Settle && - whoseCommitChain.IsLocal() && - entry.removeCommitHeights.Local == 0 { - lc.channelState.TotalMSatReceived += entry.Amount + continue } addEntry, err := lc.fetchParent( entry, whoseCommitChain, lntypes.Remote, ) if err != nil { - return nil, err + return nil, lntypes.Dual[[]*paymentDescriptor]{}, err } skipThem[addEntry.HtlcIndex] = struct{}{} @@ -2862,14 +2841,10 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, entry, ourBalance, theirBalance, whoseCommitChain, true, ) - - if mutateState { - rmvHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - } } } + + // Do the same for our peer's updates. for _, entry := range view.TheirUpdates { switch entry.EntryType { // Skip adds for now. They will be processed below. @@ -2889,36 +2864,16 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, newView.FeePerKw = chainfee.SatPerKWeight( entry.Amount.ToSatoshis(), ) - - if mutateState { - entry.addCommitHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - - entry.removeCommitHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - } } - continue - } - - // If the remote party is settling one of our outbound HTLC's, - // and it hasn't been processed, yet, the increment our state - // tracking the total number of satoshis we've sent within the - // channel. - if mutateState && entry.EntryType == Settle && - whoseCommitChain.IsLocal() && - entry.removeCommitHeights.Local == 0 { - lc.channelState.TotalMSatSent += entry.Amount + continue } addEntry, err := lc.fetchParent( entry, whoseCommitChain, lntypes.Local, ) if err != nil { - return nil, err + return nil, lntypes.Dual[[]*paymentDescriptor]{}, err } skipUs[addEntry.HtlcIndex] = struct{}{} @@ -2930,12 +2885,6 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, entry, ourBalance, theirBalance, whoseCommitChain, false, ) - - if mutateState { - rmvHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - } } } @@ -2957,19 +2906,12 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, entry, ourBalance, theirBalance, whoseCommitChain, false, ) - - // If we are mutating the state, then set the add - // height for the appropriate commitment chain to the - // next height. - if mutateState { - addHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - } } newView.OurUpdates = append(newView.OurUpdates, entry) } + + // Again, we do the same for our peer's updates. for _, entry := range view.TheirUpdates { isAdd := entry.EntryType == Add if _, ok := skipThem[entry.HtlcIndex]; !isAdd || ok { @@ -2985,21 +2927,50 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, entry, ourBalance, theirBalance, whoseCommitChain, true, ) - - // If we are mutating the state, then set the add - // height for the appropriate commitment chain to the - // next height. - if mutateState { - addHeights.SetForParty( - whoseCommitChain, nextHeight, - ) - } } newView.TheirUpdates = append(newView.TheirUpdates, entry) } - return newView, nil + // Create a function that is capable of identifying whether or not the + // paymentDescriptor has been committed in the commitment chain + // corresponding to whoseCommitmentChain. + isUncommitted := func(update *paymentDescriptor) bool { + switch update.EntryType { + case Add: + h := update.addCommitHeights.GetForParty( + whoseCommitChain, + ) + + return h == 0 + + case FeeUpdate: + h := update.addCommitHeights.GetForParty( + whoseCommitChain, + ) + + return h == 0 + + case Settle, Fail, MalformedFail: + h := update.removeCommitHeights.GetForParty( + whoseCommitChain, + ) + + return h == 0 + + default: + panic("invalid paymentDescriptor EntryType") + } + } + + // Collect all of the updates that haven't had their commit heights sent + // for the commitment chain corresponding to whoseCommitmentChain. + uncommittedUpdates := lntypes.Dual[[]*paymentDescriptor]{ + Local: fn.Filter(isUncommitted, view.OurUpdates), + Remote: fn.Filter(isUncommitted, view.TheirUpdates), + } + + return newView, uncommittedUpdates, nil } // fetchParent is a helper that looks up update log parent entries in the @@ -4448,13 +4419,59 @@ func (lc *LightningChannel) computeView(view *HtlcView, // channel constraints to the final commitment state. If any fee // updates are found in the logs, the commitment fee rate should be // changed, so we'll also set the feePerKw to this new value. - filteredHTLCView, err := lc.evaluateHTLCView( + filteredHTLCView, uncommitted, err := lc.evaluateHTLCView( view, &ourBalance, &theirBalance, nextHeight, whoseCommitChain, - updateState, ) if err != nil { return 0, 0, 0, nil, err } + if updateState { + state := lc.channelState + received := &state.TotalMSatReceived + sent := &state.TotalMSatSent + + setHeights := func(u *paymentDescriptor) { + switch u.EntryType { + case Add: + u.addCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + case Settle, Fail, MalformedFail: + u.removeCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + case FeeUpdate: + u.addCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + u.removeCommitHeights.SetForParty( + whoseCommitChain, nextHeight, + ) + } + } + + recordFlow := func(acc *lnwire.MilliSatoshi, + u *paymentDescriptor) { + + if u.EntryType == Settle { + *acc += u.Amount + } + } + + for _, u := range uncommitted.Local { + setHeights(u) + if whoseCommitChain == lntypes.Local { + recordFlow(received, u) + } + } + for _, u := range uncommitted.Remote { + setHeights(u) + if whoseCommitChain == lntypes.Local { + recordFlow(sent, u) + } + } + } + feePerKw := filteredHTLCView.FeePerKw // Here we override the view's fee-rate if a dry-run fee-rate was diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index f45f983415d..abad344b3bd 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8783,14 +8783,77 @@ func TestEvaluateView(t *testing.T) { ) // Evaluate the htlc view, mutate as test expects. - result, err := lc.evaluateHTLCView( + result, uncommitted, err := lc.evaluateHTLCView( view, &ourBalance, &theirBalance, nextHeight, - test.whoseCommitChain, test.mutateState, + test.whoseCommitChain, ) + if err != nil { t.Fatalf("unexpected error: %v", err) } + // TODO(proofofkeags): This block is here because we + // extracted this code from a previous implementation + // of evaluateHTLCView, due to a reduced scope of + // responsibility of that function. Consider removing + // it from the test altogether. + if test.mutateState { + state := lc.channelState + received := &state.TotalMSatReceived + sent := &state.TotalMSatSent + + setHeights := func(u *paymentDescriptor) { + switch u.EntryType { + case Add: + u.addCommitHeights.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + case Settle, Fail, MalformedFail: + //nolint:lll + u.removeCommitHeights.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + case FeeUpdate: + u.addCommitHeights.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + //nolint:lll + u.removeCommitHeights.SetForParty( + test.whoseCommitChain, + nextHeight, + ) + } + } + + recordFlow := func(acc *lnwire.MilliSatoshi, + u *paymentDescriptor) { + + if u.EntryType == Settle { + *acc += u.Amount + } + } + + for _, u := range uncommitted.Local { + setHeights(u) + if test.whoseCommitChain == + lntypes.Local { + + recordFlow(received, u) + } + } + for _, u := range uncommitted.Remote { + setHeights(u) + if test.whoseCommitChain == + lntypes.Local { + + recordFlow(sent, u) + } + } + } + if result.FeePerKw != test.expectedFee { t.Fatalf("expected fee: %v, got: %v", test.expectedFee, result.FeePerKw) From 5562911b4a9751ac13219952b5459d4f294caddd Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Mon, 22 Jul 2024 14:12:03 -0700 Subject: [PATCH 08/22] lnwallet: use fn.Set API directly instead of empty struct map. --- lnwallet/channel.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 7ef354f6a75..d5bf130f4bb 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2795,8 +2795,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // keep track of which entries we need to skip when creating the final // htlc view. We skip an entry whenever we find a settle or a timeout // modifying an entry. - skipUs := make(map[uint64]struct{}) - skipThem := make(map[uint64]struct{}) + skipUs := fn.NewSet[uint64]() + skipThem := fn.NewSet[uint64]() // First we run through non-add entries in both logs, populating the // skip sets and mutating the current chain state (crediting balances, @@ -2832,7 +2832,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, return nil, lntypes.Dual[[]*paymentDescriptor]{}, err } - skipThem[addEntry.HtlcIndex] = struct{}{} + skipThem.Add(addEntry.HtlcIndex) rmvHeights := &entry.removeCommitHeights rmvHeight := rmvHeights.GetForParty(whoseCommitChain) @@ -2876,7 +2876,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, return nil, lntypes.Dual[[]*paymentDescriptor]{}, err } - skipUs[addEntry.HtlcIndex] = struct{}{} + skipUs.Add(addEntry.HtlcIndex) rmvHeights := &entry.removeCommitHeights rmvHeight := rmvHeights.GetForParty(whoseCommitChain) @@ -2893,7 +2893,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // added HTLCs. for _, entry := range view.OurUpdates { isAdd := entry.EntryType == Add - if _, ok := skipUs[entry.HtlcIndex]; !isAdd || ok { + if skipUs.Contains(entry.HtlcIndex) || !isAdd { continue } @@ -2914,7 +2914,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // Again, we do the same for our peer's updates. for _, entry := range view.TheirUpdates { isAdd := entry.EntryType == Add - if _, ok := skipThem[entry.HtlcIndex]; !isAdd || ok { + if skipThem.Contains(entry.HtlcIndex) || !isAdd { continue } From ae1a4ff528a7ba9d21d4cb95822363d63ce86d10 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Mon, 22 Jul 2024 14:34:18 -0700 Subject: [PATCH 09/22] lnwallet: change bool isIncoming to new lntypes.ChannelParty This commit removes another raw boolean value and replaces it with a more clear type/name. This will also assist us when we later try and consolidate the logic of evaluateHTLCView into a single coherent computation. --- lnwallet/channel.go | 28 +++++++++++++++------------- lnwallet/channel_test.go | 32 ++++++++++++++++---------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index d5bf130f4bb..332f33e4796 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2839,7 +2839,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if rmvHeight == 0 { processRemoveEntry( entry, ourBalance, theirBalance, - whoseCommitChain, true, + whoseCommitChain, lntypes.Remote, ) } } @@ -2883,7 +2883,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if rmvHeight == 0 { processRemoveEntry( entry, ourBalance, theirBalance, - whoseCommitChain, false, + whoseCommitChain, lntypes.Local, ) } } @@ -2904,7 +2904,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if addHeight == 0 { processAddEntry( entry, ourBalance, theirBalance, - whoseCommitChain, false, + whoseCommitChain, lntypes.Local, ) } @@ -2925,7 +2925,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if addHeight == 0 { processAddEntry( entry, ourBalance, theirBalance, - whoseCommitChain, true, + whoseCommitChain, lntypes.Remote, ) } @@ -3022,10 +3022,9 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // was committed is updated. Keeping track of this inclusion height allows us to // later compact the log once the change is fully committed in both chains. func processAddEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, _ lntypes.ChannelParty, - isIncoming bool) { + theirBalance *lnwire.MilliSatoshi, _, originator lntypes.ChannelParty) { - if isIncoming { + if originator == lntypes.Remote { // If this is a new incoming (un-committed) HTLC, then we need // to update their balance accordingly by subtracting the // amount of the HTLC that are funds pending. @@ -3041,32 +3040,35 @@ func processAddEntry(htlc *paymentDescriptor, ourBalance, // previously added HTLC. If the removal entry has already been processed, it // is skipped. func processRemoveEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, _ lntypes.ChannelParty, - isIncoming bool) { + theirBalance *lnwire.MilliSatoshi, _, originator lntypes.ChannelParty) { switch { // If an incoming HTLC is being settled, then this means that we've // received the preimage either from another subsystem, or the // upstream peer in the route. Therefore, we increase our balance by // the HTLC amount. - case isIncoming && htlc.EntryType == Settle: + case originator == lntypes.Remote && htlc.EntryType == Settle: *ourBalance += htlc.Amount // Otherwise, this HTLC is being failed out, therefore the value of the // HTLC should return to the remote party. - case isIncoming && (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): + case originator == lntypes.Remote && + (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): + *theirBalance += htlc.Amount // If an outgoing HTLC is being settled, then this means that the // downstream party resented the preimage or learned of it via a // downstream peer. In either case, we credit their settled value with // the value of the HTLC. - case !isIncoming && htlc.EntryType == Settle: + case originator == lntypes.Local && htlc.EntryType == Settle: *theirBalance += htlc.Amount // Otherwise, one of our outgoing HTLC's has timed out, so the value of // the HTLC should be returned to our settled balance. - case !isIncoming && (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): + case originator == lntypes.Local && + (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): + *ourBalance += htlc.Amount } } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index abad344b3bd..4183b2ca120 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8953,7 +8953,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { name string startHeights heights whoseCommitChain lntypes.ChannelParty - isIncoming bool + originator lntypes.ChannelParty mutateState bool ourExpectedBalance lnwire.MilliSatoshi theirExpectedBalance lnwire.MilliSatoshi @@ -8969,7 +8969,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance, @@ -8990,7 +8990,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Local, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance, @@ -9011,7 +9011,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Local, - isIncoming: true, + originator: lntypes.Remote, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance - updateAmount, @@ -9032,7 +9032,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Local, - isIncoming: true, + originator: lntypes.Remote, mutateState: true, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance - updateAmount, @@ -9054,7 +9054,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance - updateAmount, theirExpectedBalance: startBalance, @@ -9075,7 +9075,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: false, + originator: lntypes.Local, mutateState: true, ourExpectedBalance: startBalance - updateAmount, theirExpectedBalance: startBalance, @@ -9096,7 +9096,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: removeHeight, }, whoseCommitChain: lntypes.Remote, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance, @@ -9117,7 +9117,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Local, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance, @@ -9140,7 +9140,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: true, + originator: lntypes.Remote, mutateState: false, ourExpectedBalance: startBalance + updateAmount, theirExpectedBalance: startBalance, @@ -9163,7 +9163,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance + updateAmount, @@ -9186,7 +9186,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: true, + originator: lntypes.Remote, mutateState: false, ourExpectedBalance: startBalance, theirExpectedBalance: startBalance + updateAmount, @@ -9209,7 +9209,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: false, + originator: lntypes.Local, mutateState: false, ourExpectedBalance: startBalance + updateAmount, theirExpectedBalance: startBalance, @@ -9234,7 +9234,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Local, - isIncoming: true, + originator: lntypes.Remote, mutateState: true, ourExpectedBalance: startBalance + updateAmount, theirExpectedBalance: startBalance, @@ -9259,7 +9259,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { remoteRemove: 0, }, whoseCommitChain: lntypes.Remote, - isIncoming: true, + originator: lntypes.Remote, mutateState: true, ourExpectedBalance: startBalance + updateAmount, theirExpectedBalance: startBalance, @@ -9315,7 +9315,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { process( update, &ourBalance, &theirBalance, test.whoseCommitChain, - test.isIncoming, + test.originator, ) if test.mutateState { From 2bf7c15e5af49072669859f8d3e4469df46dc1a3 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 14:57:32 -0700 Subject: [PATCH 10/22] lnwallet: pack htlcView.{ourUpdates|theirUpdates} into Dual. This commit moves the collection of updates behind a Dual structure. This allows us in a later commit to index into it via a ChannelParty parameter which will simplify the loops in evaluateHTLCView. --- lnwallet/channel.go | 57 +++++++++++++++++++++------------------- lnwallet/channel_test.go | 13 +++++---- lnwallet/commitment.go | 8 +++--- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 332f33e4796..dfa3eec0a1d 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2571,11 +2571,8 @@ type HtlcView struct { // created using this view. NextHeight uint64 - // OurUpdates are our outgoing HTLCs. - OurUpdates []*paymentDescriptor - - // TheirUpdates are their incoming HTLCs. - TheirUpdates []*paymentDescriptor + // Updates is a Dual of the Local and Remote HTLCs. + Updates lntypes.Dual[[]*paymentDescriptor] // FeePerKw is the fee rate in sat/kw of the commitment transaction. FeePerKw chainfee.SatPerKWeight @@ -2612,8 +2609,10 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, } return &HtlcView{ - OurUpdates: ourHTLCs, - TheirUpdates: theirHTLCs, + Updates: lntypes.Dual[[]*paymentDescriptor]{ + Local: ourHTLCs, + Remote: theirHTLCs, + }, } } @@ -2737,15 +2736,15 @@ func (lc *LightningChannel) fetchCommitmentView( // commitment are mutated, we'll manually copy over each HTLC to its // respective slice. c.outgoingHTLCs = make( - []paymentDescriptor, len(filteredHTLCView.OurUpdates), + []paymentDescriptor, len(filteredHTLCView.Updates.Local), ) - for i, htlc := range filteredHTLCView.OurUpdates { + for i, htlc := range filteredHTLCView.Updates.Local { c.outgoingHTLCs[i] = *htlc } c.incomingHTLCs = make( - []paymentDescriptor, len(filteredHTLCView.TheirUpdates), + []paymentDescriptor, len(filteredHTLCView.Updates.Remote), ) - for i, htlc := range filteredHTLCView.TheirUpdates { + for i, htlc := range filteredHTLCView.Updates.Remote { c.incomingHTLCs[i] = *htlc } @@ -2801,7 +2800,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // First we run through non-add entries in both logs, populating the // skip sets and mutating the current chain state (crediting balances, // etc) to reflect the settle/timeout entry encountered. - for _, entry := range view.OurUpdates { + for _, entry := range view.Updates.Local { switch entry.EntryType { // Skip adds for now. They will be processed below. case Add: @@ -2845,7 +2844,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, } // Do the same for our peer's updates. - for _, entry := range view.TheirUpdates { + for _, entry := range view.Updates.Remote { switch entry.EntryType { // Skip adds for now. They will be processed below. case Add: @@ -2891,7 +2890,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // Next we take a second pass through all the log entries, skipping any // settled HTLCs, and debiting the chain state balance due to any newly // added HTLCs. - for _, entry := range view.OurUpdates { + for _, entry := range view.Updates.Local { isAdd := entry.EntryType == Add if skipUs.Contains(entry.HtlcIndex) || !isAdd { continue @@ -2908,11 +2907,11 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, ) } - newView.OurUpdates = append(newView.OurUpdates, entry) + newView.Updates.Local = append(newView.Updates.Local, entry) } // Again, we do the same for our peer's updates. - for _, entry := range view.TheirUpdates { + for _, entry := range view.Updates.Remote { isAdd := entry.EntryType == Add if skipThem.Contains(entry.HtlcIndex) || !isAdd { continue @@ -2929,7 +2928,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, ) } - newView.TheirUpdates = append(newView.TheirUpdates, entry) + newView.Updates.Remote = append(newView.Updates.Remote, entry) } // Create a function that is capable of identifying whether or not the @@ -2965,10 +2964,12 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // Collect all of the updates that haven't had their commit heights sent // for the commitment chain corresponding to whoseCommitmentChain. - uncommittedUpdates := lntypes.Dual[[]*paymentDescriptor]{ - Local: fn.Filter(isUncommitted, view.OurUpdates), - Remote: fn.Filter(isUncommitted, view.TheirUpdates), - } + uncommittedUpdates := lntypes.MapDual( + view.Updates, + func(us []*paymentDescriptor) []*paymentDescriptor { + return fn.Filter(isUncommitted, us) + }, + ) return newView, uncommittedUpdates, nil } @@ -3575,10 +3576,12 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // appropriate update log, in order to validate the sanity of the // commitment resulting from _actually adding_ this HTLC to the state. if predictOurAdd != nil { - view.OurUpdates = append(view.OurUpdates, predictOurAdd) + view.Updates.Local = append(view.Updates.Local, predictOurAdd) } if predictTheirAdd != nil { - view.TheirUpdates = append(view.TheirUpdates, predictTheirAdd) + view.Updates.Remote = append( + view.Updates.Remote, predictTheirAdd, + ) } ourBalance, theirBalance, commitWeight, filteredView, err := lc.computeView( @@ -3733,7 +3736,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // First check that the remote updates won't violate it's channel // constraints. err = validateUpdates( - filteredView.TheirUpdates, &lc.channelState.RemoteChanCfg, + filteredView.Updates.Remote, &lc.channelState.RemoteChanCfg, ) if err != nil { return err @@ -3742,7 +3745,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // Secondly check that our updates won't violate our channel // constraints. err = validateUpdates( - filteredView.OurUpdates, &lc.channelState.LocalChanCfg, + filteredView.Updates.Local, &lc.channelState.LocalChanCfg, ) if err != nil { return err @@ -4498,7 +4501,7 @@ func (lc *LightningChannel) computeView(view *HtlcView, // Now go through all HTLCs at this stage, to calculate the total // weight, needed to calculate the transaction fee. var totalHtlcWeight lntypes.WeightUnit - for _, htlc := range filteredHTLCView.OurUpdates { + for _, htlc := range filteredHTLCView.Updates.Local { if HtlcIsDust( lc.channelState.ChanType, false, whoseCommitChain, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -4509,7 +4512,7 @@ func (lc *LightningChannel) computeView(view *HtlcView, totalHtlcWeight += input.HTLCWeight } - for _, htlc := range filteredHTLCView.TheirUpdates { + for _, htlc := range filteredHTLCView.Updates.Remote { if HtlcIsDust( lc.channelState.ChanType, true, whoseCommitChain, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 4183b2ca120..cb23399d537 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8768,9 +8768,11 @@ func TestEvaluateView(t *testing.T) { } view := &HtlcView{ - OurUpdates: test.ourHtlcs, - TheirUpdates: test.theirHtlcs, - FeePerKw: feePerKw, + Updates: lntypes.Dual[[]*paymentDescriptor]{ + Local: test.ourHtlcs, + Remote: test.theirHtlcs, + }, + FeePerKw: feePerKw, } var ( @@ -8860,11 +8862,12 @@ func TestEvaluateView(t *testing.T) { } checkExpectedHtlcs( - t, result.OurUpdates, test.ourExpectedHtlcs, + t, result.Updates.Local, test.ourExpectedHtlcs, ) checkExpectedHtlcs( - t, result.TheirUpdates, test.theirExpectedHtlcs, + t, result.Updates.Remote, + test.theirExpectedHtlcs, ) if lc.channelState.TotalMSatSent != test.expectSent { diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 73565ea1848..0f184dc2e74 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -702,7 +702,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, } numHTLCs := int64(0) - for _, htlc := range filteredHTLCView.OurUpdates { + for _, htlc := range filteredHTLCView.Updates.Local { if HtlcIsDust( cb.chanState.ChanType, false, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -713,7 +713,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, numHTLCs++ } - for _, htlc := range filteredHTLCView.TheirUpdates { + for _, htlc := range filteredHTLCView.Updates.Remote { if HtlcIsDust( cb.chanState.ChanType, true, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -827,7 +827,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, // purposes of sorting. cltvs := make([]uint32, len(commitTx.TxOut)) htlcIndexes := make([]input.HtlcIndex, len(commitTx.TxOut)) - for _, htlc := range filteredHTLCView.OurUpdates { + for _, htlc := range filteredHTLCView.Updates.Local { if HtlcIsDust( cb.chanState.ChanType, false, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -855,7 +855,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, cltvs = append(cltvs, htlc.Timeout) //nolint htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint } - for _, htlc := range filteredHTLCView.TheirUpdates { + for _, htlc := range filteredHTLCView.Updates.Remote { if HtlcIsDust( cb.chanState.ChanType, true, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, From 1458ff354b4f99b410d1b481e048da02ac497e7e Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 15:52:41 -0700 Subject: [PATCH 11/22] lnwallet: simplify fee calculation in evaluateHTLCView This commit simplifies how we compute the commitment fee rate based off of the live updates. Prior to this commit we processed all of the FeeUpdate paymentDescriptors of both ChannelParty's. Now we only process the last FeeUpdate of the OpeningParty --- lnwallet/channel.go | 42 ++++++++++++++-------------------------- lnwallet/channel_test.go | 5 +++++ 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index dfa3eec0a1d..518213bb728 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2790,6 +2790,18 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, NextHeight: nextHeight, } + // The fee rate of our view is always the last UpdateFee message from + // the channel's OpeningParty. + openerUpdates := view.Updates.GetForParty(lc.channelState.Initiator()) + feeUpdates := fn.Filter(func(u *paymentDescriptor) bool { + return u.EntryType == FeeUpdate + }, openerUpdates) + if len(feeUpdates) > 0 { + newView.FeePerKw = chainfee.SatPerKWeight( + feeUpdates[len(feeUpdates)-1].Amount.ToSatoshis(), + ) + } + // We use two maps, one for the local log and one for the remote log to // keep track of which entries we need to skip when creating the final // htlc view. We skip an entry whenever we find a settle or a timeout @@ -2806,21 +2818,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, case Add: continue - // Process fee updates, updating the current feePerKw. + // Skip fee updates because we've already dealt with them above. case FeeUpdate: - h := entry.addCommitHeights.GetForParty( - whoseCommitChain, - ) - - if h == 0 { - // If the update wasn't already locked in, - // update the current fee rate to reflect this - // update. - newView.FeePerKw = chainfee.SatPerKWeight( - entry.Amount.ToSatoshis(), - ) - } - continue } @@ -2850,21 +2849,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, case Add: continue - // Process fee updates, updating the current feePerKw. + // Skip fee updates because we've already dealt with them above. case FeeUpdate: - h := entry.addCommitHeights.GetForParty( - whoseCommitChain, - ) - - if h == 0 { - // If the update wasn't already locked in, - // update the current fee rate to reflect this - // update. - newView.FeePerKw = chainfee.SatPerKWeight( - entry.Amount.ToSatoshis(), - ) - } - continue } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index cb23399d537..71c24373891 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8477,6 +8477,7 @@ func TestEvaluateView(t *testing.T) { name string ourHtlcs []*paymentDescriptor theirHtlcs []*paymentDescriptor + channelInitiator lntypes.ChannelParty whoseCommitChain lntypes.ChannelParty mutateState bool @@ -8506,6 +8507,7 @@ func TestEvaluateView(t *testing.T) { }{ { name: "our fee update is applied", + channelInitiator: lntypes.Local, whoseCommitChain: lntypes.Local, mutateState: false, ourHtlcs: []*paymentDescriptor{ @@ -8523,6 +8525,7 @@ func TestEvaluateView(t *testing.T) { }, { name: "their fee update is applied", + channelInitiator: lntypes.Remote, whoseCommitChain: lntypes.Local, mutateState: false, ourHtlcs: []*paymentDescriptor{}, @@ -8738,8 +8741,10 @@ func TestEvaluateView(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { + isInitiator := test.channelInitiator == lntypes.Local lc := LightningChannel{ channelState: &channeldb.OpenChannel{ + IsInitiator: isInitiator, TotalMSatSent: 0, TotalMSatReceived: 0, }, From 728a10cc17539642258225878b0a79d0c51134d3 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 16:16:31 -0700 Subject: [PATCH 12/22] lnwallet: consolidate loops in evaluateHTLCView We had four for-loops in evaluateHTLCView that were exact mirror images of each other. By making use of the new ChannelParty and Dual facilities introduced in prior commits, we consolidate these into two for-loops. --- lnwallet/channel.go | 151 +++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 94 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 518213bb728..756a16b91ea 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2789,6 +2789,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, FeePerKw: view.FeePerKw, NextHeight: nextHeight, } + noUncommitted := lntypes.Dual[[]*paymentDescriptor]{} // The fee rate of our view is always the last UpdateFee message from // the channel's OpeningParty. @@ -2806,115 +2807,77 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // keep track of which entries we need to skip when creating the final // htlc view. We skip an entry whenever we find a settle or a timeout // modifying an entry. - skipUs := fn.NewSet[uint64]() - skipThem := fn.NewSet[uint64]() - - // First we run through non-add entries in both logs, populating the - // skip sets and mutating the current chain state (crediting balances, - // etc) to reflect the settle/timeout entry encountered. - for _, entry := range view.Updates.Local { - switch entry.EntryType { - // Skip adds for now. They will be processed below. - case Add: - continue - - // Skip fee updates because we've already dealt with them above. - case FeeUpdate: - continue - } - - addEntry, err := lc.fetchParent( - entry, whoseCommitChain, lntypes.Remote, - ) - if err != nil { - return nil, lntypes.Dual[[]*paymentDescriptor]{}, err - } + skip := lntypes.Dual[fn.Set[uint64]]{ + Local: fn.NewSet[uint64](), + Remote: fn.NewSet[uint64](), + } + + parties := [2]lntypes.ChannelParty{lntypes.Local, lntypes.Remote} + for _, party := range parties { + // First we run through non-add entries in both logs, + // populating the skip sets and mutating the current chain + // state (crediting balances, etc) to reflect the + // settle/timeout entry encountered. + for _, entry := range view.Updates.GetForParty(party) { + switch entry.EntryType { + // Skip adds for now. They will be processed below. + case Add: + continue - skipThem.Add(addEntry.HtlcIndex) + // Skip fee updates because we've already dealt with + // them above. + case FeeUpdate: + continue + } - rmvHeights := &entry.removeCommitHeights - rmvHeight := rmvHeights.GetForParty(whoseCommitChain) - if rmvHeight == 0 { - processRemoveEntry( - entry, ourBalance, theirBalance, - whoseCommitChain, lntypes.Remote, + addEntry, err := lc.fetchParent( + entry, whoseCommitChain, party.CounterParty(), ) - } - } - - // Do the same for our peer's updates. - for _, entry := range view.Updates.Remote { - switch entry.EntryType { - // Skip adds for now. They will be processed below. - case Add: - continue - - // Skip fee updates because we've already dealt with them above. - case FeeUpdate: - continue - } - - addEntry, err := lc.fetchParent( - entry, whoseCommitChain, lntypes.Local, - ) - if err != nil { - return nil, lntypes.Dual[[]*paymentDescriptor]{}, err - } + if err != nil { + return nil, noUncommitted, err + } - skipUs.Add(addEntry.HtlcIndex) + skipSet := skip.GetForParty(party.CounterParty()) + skipSet.Add(addEntry.HtlcIndex) - rmvHeights := &entry.removeCommitHeights - rmvHeight := rmvHeights.GetForParty(whoseCommitChain) - if rmvHeight == 0 { - processRemoveEntry( - entry, ourBalance, theirBalance, - whoseCommitChain, lntypes.Local, - ) + rmvHeights := &entry.removeCommitHeights + rmvHeight := rmvHeights.GetForParty(whoseCommitChain) + if rmvHeight == 0 { + processRemoveEntry( + entry, ourBalance, theirBalance, + whoseCommitChain, party.CounterParty(), + ) + } } } // Next we take a second pass through all the log entries, skipping any // settled HTLCs, and debiting the chain state balance due to any newly // added HTLCs. - for _, entry := range view.Updates.Local { - isAdd := entry.EntryType == Add - if skipUs.Contains(entry.HtlcIndex) || !isAdd { - continue - } - - // Skip the entries that have already had their add commit - // height set for this commit chain. - addHeights := &entry.addCommitHeights - addHeight := addHeights.GetForParty(whoseCommitChain) - if addHeight == 0 { - processAddEntry( - entry, ourBalance, theirBalance, - whoseCommitChain, lntypes.Local, - ) - } - - newView.Updates.Local = append(newView.Updates.Local, entry) - } + for _, party := range parties { + for _, entry := range view.Updates.GetForParty(party) { + isAdd := entry.EntryType == Add + skipSet := skip.GetForParty(party) + if skipSet.Contains(entry.HtlcIndex) || !isAdd { + continue + } - // Again, we do the same for our peer's updates. - for _, entry := range view.Updates.Remote { - isAdd := entry.EntryType == Add - if skipThem.Contains(entry.HtlcIndex) || !isAdd { - continue - } + // Skip the entries that have already had their add + // commit height set for this commit chain. + addHeights := &entry.addCommitHeights + addHeight := addHeights.GetForParty(whoseCommitChain) + if addHeight == 0 { + processAddEntry( + entry, ourBalance, theirBalance, + whoseCommitChain, party, + ) + } - // Skip the entries that have already had their add commit - // height set for this commit chain. - addHeights := &entry.addCommitHeights - addHeight := addHeights.GetForParty(whoseCommitChain) - if addHeight == 0 { - processAddEntry( - entry, ourBalance, theirBalance, - whoseCommitChain, lntypes.Remote, + prevUpdates := newView.Updates.GetForParty(party) + newView.Updates.SetForParty( + party, append(prevUpdates, entry), ) } - - newView.Updates.Remote = append(newView.Updates.Remote, entry) } // Create a function that is capable of identifying whether or not the From bbc0cef330188d28ff6b5faee3fdd330fce46f7b Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 16:30:55 -0700 Subject: [PATCH 13/22] lnwallet: remove continue statements from evaluateHTLCView loops This further reduces loop complexity in evaluateHTLCView by using explicit filter steps rather than loop continue statements. --- lnwallet/channel.go | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 756a16b91ea..13558f062b6 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2818,18 +2818,16 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // populating the skip sets and mutating the current chain // state (crediting balances, etc) to reflect the // settle/timeout entry encountered. - for _, entry := range view.Updates.GetForParty(party) { - switch entry.EntryType { - // Skip adds for now. They will be processed below. - case Add: - continue - - // Skip fee updates because we've already dealt with - // them above. - case FeeUpdate: - continue + resolutions := fn.Filter(func(pd *paymentDescriptor) bool { + switch pd.EntryType { + case Settle, Fail, MalformedFail: + return true + default: + return false } + }, view.Updates.GetForParty(party)) + for _, entry := range resolutions { addEntry, err := lc.fetchParent( entry, whoseCommitChain, party.CounterParty(), ) @@ -2855,13 +2853,12 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // settled HTLCs, and debiting the chain state balance due to any newly // added HTLCs. for _, party := range parties { - for _, entry := range view.Updates.GetForParty(party) { - isAdd := entry.EntryType == Add - skipSet := skip.GetForParty(party) - if skipSet.Contains(entry.HtlcIndex) || !isAdd { - continue - } + liveAdds := fn.Filter(func(pd *paymentDescriptor) bool { + return pd.EntryType == Add && + !skip.GetForParty(party).Contains(pd.HtlcIndex) + }, view.Updates.GetForParty(party)) + for _, entry := range liveAdds { // Skip the entries that have already had their add // commit height set for this commit chain. addHeights := &entry.addCommitHeights @@ -2872,12 +2869,9 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, whoseCommitChain, party, ) } - - prevUpdates := newView.Updates.GetForParty(party) - newView.Updates.SetForParty( - party, append(prevUpdates, entry), - ) } + + newView.Updates.SetForParty(party, liveAdds) } // Create a function that is capable of identifying whether or not the From 55e81d5dc9a681f29c11b05e340419376c8f4000 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 17:04:08 -0700 Subject: [PATCH 14/22] lnwallet: remove useless argument from process[Add|Remove]Entry --- lnwallet/channel.go | 8 ++++---- lnwallet/channel_test.go | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 13558f062b6..2f6103f906f 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2843,7 +2843,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if rmvHeight == 0 { processRemoveEntry( entry, ourBalance, theirBalance, - whoseCommitChain, party.CounterParty(), + party.CounterParty(), ) } } @@ -2866,7 +2866,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if addHeight == 0 { processAddEntry( entry, ourBalance, theirBalance, - whoseCommitChain, party, + party, ) } } @@ -2966,7 +2966,7 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // was committed is updated. Keeping track of this inclusion height allows us to // later compact the log once the change is fully committed in both chains. func processAddEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, _, originator lntypes.ChannelParty) { + theirBalance *lnwire.MilliSatoshi, originator lntypes.ChannelParty) { if originator == lntypes.Remote { // If this is a new incoming (un-committed) HTLC, then we need @@ -2984,7 +2984,7 @@ func processAddEntry(htlc *paymentDescriptor, ourBalance, // previously added HTLC. If the removal entry has already been processed, it // is skipped. func processRemoveEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, _, originator lntypes.ChannelParty) { + theirBalance *lnwire.MilliSatoshi, originator lntypes.ChannelParty) { switch { // If an incoming HTLC is being settled, then this means that we've diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 71c24373891..81d1b56f070 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9322,7 +9322,6 @@ func TestProcessAddRemoveEntry(t *testing.T) { if heightDual.GetForParty(test.whoseCommitChain) == 0 { process( update, &ourBalance, &theirBalance, - test.whoseCommitChain, test.originator, ) From 62114eada21618be6269b6f9c56628ff2331ddf6 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 17:24:08 -0700 Subject: [PATCH 15/22] lnwallet: inline and remove process[Add|Remove]Entry This commit observes that processAddEntry and processRemoveEntry are only invoked at a single call-site. Here we inline them at their call-sites, which will unlock further simplifications of the code that will allow us to remove pointer mutations in favor of explicit expression oriented programming. We also delete the tests associated with these functions, the overall functionality is implicitly tested by the TestEvaluateHTLCView tests. --- lnwallet/channel.go | 115 +++++------ lnwallet/channel_test.go | 431 --------------------------------------- 2 files changed, 51 insertions(+), 495 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 2f6103f906f..3ad27dcf050 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2841,10 +2841,44 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, rmvHeights := &entry.removeCommitHeights rmvHeight := rmvHeights.GetForParty(whoseCommitChain) if rmvHeight == 0 { - processRemoveEntry( - entry, ourBalance, theirBalance, - party.CounterParty(), - ) + switch { + // If an incoming HTLC is being settled, then + // this means that we've received the preimage + // either from another subsystem, or the + // upstream peer in the route. Therefore, we + // increase our balance by the HTLC amount. + case party.CounterParty() == lntypes.Remote && + entry.EntryType == Settle: + + *ourBalance += entry.Amount + + // Otherwise, this HTLC is being failed out, + // therefore the value of the HTLC should + // return to the remote party. + case party.CounterParty() == lntypes.Remote && + entry.EntryType != Settle: + + *theirBalance += entry.Amount + + // If an outgoing HTLC is being settled, then + // this means that the downstream party + // resented the preimage or learned of it via a + // downstream peer. In either case, we credit + // their settled value with the value of the + // HTLC. + case party.CounterParty() == lntypes.Local && + entry.EntryType == Settle: + + *theirBalance += entry.Amount + + // Otherwise, one of our outgoing HTLC's has + // timed out, so the value of the HTLC should + // be returned to our settled balance. + case party.CounterParty() == lntypes.Local && + entry.EntryType != Settle: + + *ourBalance += entry.Amount + } } } } @@ -2864,10 +2898,19 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, addHeights := &entry.addCommitHeights addHeight := addHeights.GetForParty(whoseCommitChain) if addHeight == 0 { - processAddEntry( - entry, ourBalance, theirBalance, - party, - ) + if party == lntypes.Remote { + // If this is a new incoming + // (un-committed) HTLC, then we need + // to update their balance accordingly + // by subtracting the amount of the + // HTLC that are funds pending. + *theirBalance -= entry.Amount + } else { + // Similarly, we need to debit our + // balance if this is an out going HTLC + // to reflect the pending balance. + *ourBalance -= entry.Amount + } } } @@ -2961,62 +3004,6 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, return addEntry, nil } -// processAddEntry evaluates the effect of an add entry within the HTLC log. -// If the HTLC hasn't yet been committed in either chain, then the height it -// was committed is updated. Keeping track of this inclusion height allows us to -// later compact the log once the change is fully committed in both chains. -func processAddEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, originator lntypes.ChannelParty) { - - if originator == lntypes.Remote { - // If this is a new incoming (un-committed) HTLC, then we need - // to update their balance accordingly by subtracting the - // amount of the HTLC that are funds pending. - *theirBalance -= htlc.Amount - } else { - // Similarly, we need to debit our balance if this is an out - // going HTLC to reflect the pending balance. - *ourBalance -= htlc.Amount - } -} - -// processRemoveEntry processes a log entry which settles or times out a -// previously added HTLC. If the removal entry has already been processed, it -// is skipped. -func processRemoveEntry(htlc *paymentDescriptor, ourBalance, - theirBalance *lnwire.MilliSatoshi, originator lntypes.ChannelParty) { - - switch { - // If an incoming HTLC is being settled, then this means that we've - // received the preimage either from another subsystem, or the - // upstream peer in the route. Therefore, we increase our balance by - // the HTLC amount. - case originator == lntypes.Remote && htlc.EntryType == Settle: - *ourBalance += htlc.Amount - - // Otherwise, this HTLC is being failed out, therefore the value of the - // HTLC should return to the remote party. - case originator == lntypes.Remote && - (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): - - *theirBalance += htlc.Amount - - // If an outgoing HTLC is being settled, then this means that the - // downstream party resented the preimage or learned of it via a - // downstream peer. In either case, we credit their settled value with - // the value of the HTLC. - case originator == lntypes.Local && htlc.EntryType == Settle: - *theirBalance += htlc.Amount - - // Otherwise, one of our outgoing HTLC's has timed out, so the value of - // the HTLC should be returned to our settled balance. - case originator == lntypes.Local && - (htlc.EntryType == Fail || htlc.EntryType == MalformedFail): - - *ourBalance += htlc.Amount - } -} - // generateRemoteHtlcSigJobs generates a series of HTLC signature jobs for the // sig pool, along with a channel that if closed, will cancel any jobs after // they have been submitted to the sigPool. This method is to be used when diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 81d1b56f070..ad83cb2e02d 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8919,437 +8919,6 @@ type heights struct { remoteRemove uint64 } -func checkHeights(t *testing.T, update *paymentDescriptor, expected heights) { - updateHeights := heights{ - localAdd: update.addCommitHeights.Local, - localRemove: update.removeCommitHeights.Local, - remoteAdd: update.addCommitHeights.Remote, - remoteRemove: update.removeCommitHeights.Remote, - } - - if !reflect.DeepEqual(updateHeights, expected) { - t.Fatalf("expected: %v, got: %v", expected, updateHeights) - } -} - -// TestProcessAddRemoveEntry tests the updating of our and their balances when -// we process adds, settles and fails. It also tests the mutating of add and -// remove heights. -func TestProcessAddRemoveEntry(t *testing.T) { - const ( - // addHeight is a non-zero addHeight that is used for htlc - // add heights. - addHeight = 100 - - // removeHeight is a non-zero removeHeight that is used for - // htlc remove heights. - removeHeight = 200 - - // nextHeight is a constant that we use for the nextHeight in - // all unit tests. - nextHeight = 400 - - // updateAmount is the amount that the update is set to. - updateAmount = lnwire.MilliSatoshi(10) - - // startBalance is a balance we start both sides out with - // so that balances can be incremented. - startBalance = lnwire.MilliSatoshi(100) - ) - - tests := []struct { - name string - startHeights heights - whoseCommitChain lntypes.ChannelParty - originator lntypes.ChannelParty - mutateState bool - ourExpectedBalance lnwire.MilliSatoshi - theirExpectedBalance lnwire.MilliSatoshi - expectedHeights heights - updateType updateType - }{ - { - name: "add, remote chain, already processed", - startHeights: heights{ - localAdd: 0, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: 0, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Add, - }, - { - name: "add, local chain, already processed", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Add, - }, - { - name: "incoming add, local chain, not mutated", - startHeights: heights{ - localAdd: 0, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - originator: lntypes.Remote, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance - updateAmount, - expectedHeights: heights{ - localAdd: 0, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Add, - }, - { - name: "incoming add, local chain, mutated", - startHeights: heights{ - localAdd: 0, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - originator: lntypes.Remote, - mutateState: true, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance - updateAmount, - expectedHeights: heights{ - localAdd: nextHeight, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Add, - }, - - { - name: "outgoing add, remote chain, not mutated", - startHeights: heights{ - localAdd: 0, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance - updateAmount, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: 0, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Add, - }, - { - name: "outgoing add, remote chain, mutated", - startHeights: heights{ - localAdd: 0, - remoteAdd: 0, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Local, - mutateState: true, - ourExpectedBalance: startBalance - updateAmount, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: 0, - remoteAdd: nextHeight, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Add, - }, - { - name: "settle, remote chain, already processed", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: removeHeight, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: removeHeight, - }, - updateType: Settle, - }, - { - name: "settle, local chain, already processed", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: removeHeight, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: removeHeight, - remoteRemove: 0, - }, - updateType: Settle, - }, - { - // Remote chain, and not processed yet. Incoming settle, - // so we expect our balance to increase. - name: "incoming settle", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Remote, - mutateState: false, - ourExpectedBalance: startBalance + updateAmount, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Settle, - }, - { - // Remote chain, and not processed yet. Incoming settle, - // so we expect our balance to increase. - name: "outgoing settle", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance + updateAmount, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Settle, - }, - { - // Remote chain, and not processed yet. Incoming fail, - // so we expect their balance to increase. - name: "incoming fail", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Remote, - mutateState: false, - ourExpectedBalance: startBalance, - theirExpectedBalance: startBalance + updateAmount, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Fail, - }, - { - // Remote chain, and not processed yet. Outgoing fail, - // so we expect our balance to increase. - name: "outgoing fail", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Local, - mutateState: false, - ourExpectedBalance: startBalance + updateAmount, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - updateType: Fail, - }, - { - // Local chain, and not processed yet. Incoming settle, - // so we expect our balance to increase. Mutate is - // true, so we expect our remove removeHeight to have - // changed. - name: "fail, our remove height mutated", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Local, - originator: lntypes.Remote, - mutateState: true, - ourExpectedBalance: startBalance + updateAmount, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: nextHeight, - remoteRemove: 0, - }, - updateType: Settle, - }, - { - // Remote chain, and not processed yet. Incoming settle, - // so we expect our balance to increase. Mutate is - // true, so we expect their remove removeHeight to have - // changed. - name: "fail, their remove height mutated", - startHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: 0, - }, - whoseCommitChain: lntypes.Remote, - originator: lntypes.Remote, - mutateState: true, - ourExpectedBalance: startBalance + updateAmount, - theirExpectedBalance: startBalance, - expectedHeights: heights{ - localAdd: addHeight, - remoteAdd: addHeight, - localRemove: 0, - remoteRemove: nextHeight, - }, - updateType: Settle, - }, - } - - for _, test := range tests { - test := test - - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - heights := test.startHeights - update := &paymentDescriptor{ - Amount: updateAmount, - addCommitHeights: lntypes.Dual[uint64]{ - Local: heights.localAdd, - Remote: heights.remoteAdd, - }, - removeCommitHeights: lntypes.Dual[uint64]{ - Local: heights.localRemove, - Remote: heights.remoteRemove, - }, - EntryType: test.updateType, - } - - var ( - // Start both parties off with an initial - // balance. Copy by value here so that we do - // not mutate the startBalance constant. - ourBalance, theirBalance = startBalance, - startBalance - ) - - // Choose the processing function we need based on the - // update type. Process remove is used for settles, - // fails and malformed htlcs. - process := processRemoveEntry - heightDual := &update.removeCommitHeights - if test.updateType == Add { - process = processAddEntry - heightDual = &update.addCommitHeights - } - - if heightDual.GetForParty(test.whoseCommitChain) == 0 { - process( - update, &ourBalance, &theirBalance, - test.originator, - ) - - if test.mutateState { - heightDual.SetForParty( - test.whoseCommitChain, - nextHeight, - ) - } - } - - // Check that balances were updated as expected. - if ourBalance != test.ourExpectedBalance { - t.Fatalf("expected our balance: %v, got: %v", - test.ourExpectedBalance, ourBalance) - } - - if theirBalance != test.theirExpectedBalance { - t.Fatalf("expected their balance: %v, got: %v", - test.theirExpectedBalance, theirBalance) - } - - // Check that heights on the update are as expected. - checkHeights(t, update, test.expectedHeights) - }) - } -} - // TestChannelUnsignedAckedFailure tests that unsigned acked updates are // properly restored after signing for them and disconnecting. // From 1a3d207eace0541d798b6d14eadcf9a49e840333 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 24 Jul 2024 17:53:05 -0700 Subject: [PATCH 16/22] lnwallet: return balance changes rather than modifying references Here we return the balance deltas from evaluateHTLCView rather than passing in references to variables that will be modified. It is a far cleaner and compositional approach which allows readers of this code to more effectively reason about the code without having to keep the whole codebase in their head. --- lnwallet/channel.go | 111 ++++++++++++++++++++------------------- lnwallet/channel_test.go | 14 ++--- 2 files changed, 63 insertions(+), 62 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3ad27dcf050..644ed0e62f0 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2777,10 +2777,9 @@ func fundingTxIn(chanState *channeldb.OpenChannel) wire.TxIn { // 1. The new htlcView reflecting the current channel state. // 2. A Dual of the updates which have not yet been committed in // 'whoseCommitChain's commitment chain. -func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, - theirBalance *lnwire.MilliSatoshi, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty) (*HtlcView, - lntypes.Dual[[]*paymentDescriptor], error) { +func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, + whoseCommitChain lntypes.ChannelParty, nextHeight uint64) (*HtlcView, + lntypes.Dual[[]*paymentDescriptor], lntypes.Dual[int64], error) { // We initialize the view's fee rate to the fee rate of the unfiltered // view. If any fee updates are found when evaluating the view, it will @@ -2812,6 +2811,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, Remote: fn.NewSet[uint64](), } + balanceDeltas := lntypes.Dual[int64]{} + parties := [2]lntypes.ChannelParty{lntypes.Local, lntypes.Remote} for _, party := range parties { // First we run through non-add entries in both logs, @@ -2832,7 +2833,8 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, entry, whoseCommitChain, party.CounterParty(), ) if err != nil { - return nil, noUncommitted, err + noDeltas := lntypes.Dual[int64]{} + return nil, noUncommitted, noDeltas, err } skipSet := skip.GetForParty(party.CounterParty()) @@ -2843,41 +2845,30 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if rmvHeight == 0 { switch { // If an incoming HTLC is being settled, then - // this means that we've received the preimage - // either from another subsystem, or the - // upstream peer in the route. Therefore, we - // increase our balance by the HTLC amount. - case party.CounterParty() == lntypes.Remote && - entry.EntryType == Settle: - - *ourBalance += entry.Amount + // this means that the preimage has been + // received by the settling party Therefore, we + // increase the settling party's balance by the + // HTLC amount. + case entry.EntryType == Settle: + delta := int64(entry.Amount) + balanceDeltas.ModifyForParty( + party, + func(acc int64) int64 { + return acc + delta + }, + ) // Otherwise, this HTLC is being failed out, // therefore the value of the HTLC should - // return to the remote party. - case party.CounterParty() == lntypes.Remote && - entry.EntryType != Settle: - - *theirBalance += entry.Amount - - // If an outgoing HTLC is being settled, then - // this means that the downstream party - // resented the preimage or learned of it via a - // downstream peer. In either case, we credit - // their settled value with the value of the - // HTLC. - case party.CounterParty() == lntypes.Local && - entry.EntryType == Settle: - - *theirBalance += entry.Amount - - // Otherwise, one of our outgoing HTLC's has - // timed out, so the value of the HTLC should - // be returned to our settled balance. - case party.CounterParty() == lntypes.Local && - entry.EntryType != Settle: - - *ourBalance += entry.Amount + // return to the failing party's counterparty. + case entry.EntryType != Settle: + delta := int64(entry.Amount) + balanceDeltas.ModifyForParty( + party.CounterParty(), + func(acc int64) int64 { + return acc + delta + }, + ) } } } @@ -2898,19 +2889,19 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, addHeights := &entry.addCommitHeights addHeight := addHeights.GetForParty(whoseCommitChain) if addHeight == 0 { - if party == lntypes.Remote { - // If this is a new incoming - // (un-committed) HTLC, then we need - // to update their balance accordingly - // by subtracting the amount of the - // HTLC that are funds pending. - *theirBalance -= entry.Amount - } else { - // Similarly, we need to debit our - // balance if this is an out going HTLC - // to reflect the pending balance. - *ourBalance -= entry.Amount - } + // If this is a new incoming (un-committed) + // HTLC, then we need to update their balance + // accordingly by subtracting the amount of + // the HTLC that are funds pending. + // Similarly, we need to debit our balance if + // this is an out going HTLC to reflect the + // pending balance. + balanceDeltas.ModifyForParty( + party, + func(acc int64) int64 { + return acc - int64(entry.Amount) + }, + ) } } @@ -2957,7 +2948,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, }, ) - return newView, uncommittedUpdates, nil + return newView, uncommittedUpdates, balanceDeltas, nil } // fetchParent is a helper that looks up update log parent entries in the @@ -4354,12 +4345,26 @@ func (lc *LightningChannel) computeView(view *HtlcView, // channel constraints to the final commitment state. If any fee // updates are found in the logs, the commitment fee rate should be // changed, so we'll also set the feePerKw to this new value. - filteredHTLCView, uncommitted, err := lc.evaluateHTLCView( - view, &ourBalance, &theirBalance, nextHeight, whoseCommitChain, + filteredHTLCView, uncommitted, deltas, err := lc.evaluateHTLCView( + view, whoseCommitChain, nextHeight, ) if err != nil { return 0, 0, 0, nil, err } + + // Add the balance deltas to the balances we got from the commitment + // state. + if deltas.Local >= 0 { + ourBalance += lnwire.MilliSatoshi(deltas.Local) + } else { + ourBalance -= lnwire.MilliSatoshi(-1 * deltas.Local) + } + if deltas.Remote >= 0 { + theirBalance += lnwire.MilliSatoshi(deltas.Remote) + } else { + theirBalance -= lnwire.MilliSatoshi(-1 * deltas.Remote) + } + if updateState { state := lc.channelState received := &state.TotalMSatReceived diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index ad83cb2e02d..de086c0a347 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8781,18 +8781,14 @@ func TestEvaluateView(t *testing.T) { } var ( - // Create vars to store balance changes. We do - // not check these values in this test because - // balance modification happens on the htlc - // processing level. - ourBalance lnwire.MilliSatoshi - theirBalance lnwire.MilliSatoshi ) // Evaluate the htlc view, mutate as test expects. - result, uncommitted, err := lc.evaluateHTLCView( - view, &ourBalance, &theirBalance, nextHeight, - test.whoseCommitChain, + // We do not check the balance deltas in this test + // because balance modification happens on the htlc + // processing level. + result, uncommitted, _, err := lc.evaluateHTLCView( + view, test.whoseCommitChain, nextHeight, ) if err != nil { From 935b461e4021ec34034264bf3552681475cad4da Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Mon, 19 Aug 2024 15:07:52 -0600 Subject: [PATCH 17/22] multi: pack LocalChanCfg/RemoteChanCfg into Dual --- chanbackup/single.go | 4 +- chanbackup/single_test.go | 7 +- channeldb/channel.go | 19 +-- channeldb/channel_test.go | 8 +- channeldb/db.go | 2 +- channeldb/db_test.go | 20 ++- chanrestore.go | 7 +- contractcourt/breach_arbitrator_test.go | 17 ++- contractcourt/chain_watcher.go | 23 +-- contractcourt/chain_watcher_test.go | 3 +- contractcourt/commit_sweep_resolver.go | 2 +- funding/manager.go | 29 ++-- htlcswitch/link.go | 4 +- htlcswitch/test_utils.go | 12 +- lnwallet/aux_leaf_store.go | 4 +- lnwallet/channel.go | 187 +++++++++++++----------- lnwallet/channel_test.go | 137 +++++++++-------- lnwallet/commitment.go | 22 +-- lnwallet/test_utils.go | 12 +- lnwallet/transactions_test.go | 12 +- lnwallet/wallet.go | 14 +- peer/test_utils.go | 13 +- routing/localchans/manager.go | 4 +- routing/localchans/manager_test.go | 7 +- rpcserver.go | 24 +-- 25 files changed, 328 insertions(+), 265 deletions(-) diff --git a/chanbackup/single.go b/chanbackup/single.go index dd63bf1f65f..9bd1011a3e6 100644 --- a/chanbackup/single.go +++ b/chanbackup/single.go @@ -211,8 +211,8 @@ func NewSingle(channel *channeldb.OpenChannel, RemoteNodePub: channel.IdentityPub, Addresses: nodeAddrs, Capacity: channel.Capacity, - LocalChanCfg: channel.LocalChanCfg, - RemoteChanCfg: channel.RemoteChanCfg, + LocalChanCfg: channel.ChanCfgs.Local, + RemoteChanCfg: channel.ChanCfgs.Remote, ShaChainRootDesc: shaChainRootDesc, } diff --git a/chanbackup/single_test.go b/chanbackup/single_test.go index c1e940740ca..0c008eb1b06 100644 --- a/chanbackup/single_test.go +++ b/chanbackup/single_test.go @@ -15,6 +15,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnencrypt" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" "github.com/stretchr/testify/require" @@ -194,9 +195,11 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) { ), ThawHeight: rand.Uint32(), IdentityPub: pub, - LocalChanCfg: localCfg, - RemoteChanCfg: remoteCfg, RevocationProducer: shaChainProducer, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: localCfg, + Remote: remoteCfg, + }, }, nil } diff --git a/channeldb/channel.go b/channeldb/channel.go index c21716a4560..5a59be91562 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -938,11 +938,8 @@ type OpenChannel struct { // opening. InitialRemoteBalance lnwire.MilliSatoshi - // LocalChanCfg is the channel configuration for the local node. - LocalChanCfg ChannelConfig - - // RemoteChanCfg is the channel configuration for the remote node. - RemoteChanCfg ChannelConfig + // ChanCfgs is the channel configuration for the local and remote nodes. + ChanCfgs lntypes.Dual[ChannelConfig] // LocalCommitment is the current local commitment state for the local // party. This is stored distinct from the state of the remote party @@ -1823,7 +1820,7 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) { } nextNonce, err := NewMusigVerificationNonce( - c.LocalChanCfg.MultiSigKey.PubKey, + c.ChanCfgs.Local.MultiSigKey.PubKey, nextLocalCommitHeight, taprootRevProducer, ) if err != nil { @@ -4112,7 +4109,7 @@ func putChannelCloseSummary(tx kvdb.RwTx, chanID []byte, summary.RemoteCurrentRevocation = lastChanState.RemoteCurrentRevocation summary.RemoteNextRevocation = lastChanState.RemoteNextRevocation - summary.LocalChanConfig = lastChanState.LocalChanCfg + summary.LocalChanConfig = lastChanState.ChanCfgs.Local var b bytes.Buffer if err := serializeChannelCloseSummary(&b, summary); err != nil { @@ -4302,10 +4299,10 @@ func putChanInfo(chanBucket kvdb.RwBucket, channel *OpenChannel) error { } } - if err := writeChanConfig(&w, &channel.LocalChanCfg); err != nil { + if err := writeChanConfig(&w, &channel.ChanCfgs.Local); err != nil { return err } - if err := writeChanConfig(&w, &channel.RemoteChanCfg); err != nil { + if err := writeChanConfig(&w, &channel.ChanCfgs.Remote); err != nil { return err } @@ -4484,10 +4481,10 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error { } } - if err := readChanConfig(r, &channel.LocalChanCfg); err != nil { + if err := readChanConfig(r, &channel.ChanCfgs.Local); err != nil { return err } - if err := readChanConfig(r, &channel.RemoteChanCfg); err != nil { + if err := readChanConfig(r, &channel.ChanCfgs.Remote); err != nil { return err } diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index e92692201d8..91ad13e97ec 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -339,8 +339,10 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { IsPending: true, IdentityPub: pubKey, Capacity: btcutil.Amount(10000), - LocalChanCfg: localCfg, - RemoteChanCfg: remoteCfg, + ChanCfgs: lntypes.Dual[ChannelConfig]{ + Local: localCfg, + Remote: remoteCfg, + }, TotalMSatSent: 8, TotalMSatReceived: 2, LocalCommitment: ChannelCommitment{ @@ -1025,7 +1027,7 @@ func TestFetchClosedChannels(t *testing.T) { TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000, CloseType: RemoteForceClose, IsPending: true, - LocalChanConfig: state.LocalChanCfg, + LocalChanConfig: state.ChanCfgs.Local, } if err := state.CloseChannel(summary); err != nil { t.Fatalf("unable to close channel: %v", err) diff --git a/channeldb/db.go b/channeldb/db.go index 1e210d032ad..88f401c7e26 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -1420,7 +1420,7 @@ func (c *ChannelStateDB) AbandonChannel(chanPoint *wire.OutPoint, ShortChanID: dbChan.ShortChanID(), RemoteCurrentRevocation: dbChan.RemoteCurrentRevocation, RemoteNextRevocation: dbChan.RemoteNextRevocation, - LocalChanConfig: dbChan.LocalChanCfg, + LocalChanConfig: dbChan.ChanCfgs.Local, } // Finally, we'll close the channel in the DB, and return back to the diff --git a/channeldb/db_test.go b/channeldb/db_test.go index d8113db8306..8dda4927588 100644 --- a/channeldb/db_test.go +++ b/channeldb/db_test.go @@ -296,6 +296,16 @@ func genRandomChannelShell() (*ChannelShell, error) { CsvDelay: uint16(rand.Int63()), } + localChanCfg := ChannelConfig{ + CommitmentParams: commitParams, + PaymentBasePoint: keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(rand.Int63()), + Index: uint32(rand.Int63()), + }, + }, + } + return &ChannelShell{ NodeAddrs: []net.Addr{&net.TCPAddr{ IP: net.ParseIP("127.0.0.1"), @@ -309,14 +319,8 @@ func genRandomChannelShell() (*ChannelShell, error) { uint64(rand.Int63()), ), IdentityPub: pub, - LocalChanCfg: ChannelConfig{ - CommitmentParams: commitParams, - PaymentBasePoint: keychain.KeyDescriptor{ - KeyLocator: keychain.KeyLocator{ - Family: keychain.KeyFamily(rand.Int63()), - Index: uint32(rand.Int63()), - }, - }, + ChanCfgs: lntypes.Dual[ChannelConfig]{ + Local: localChanCfg, }, RemoteCurrentRevocation: pub, IsPending: false, diff --git a/chanrestore.go b/chanrestore.go index 27f6d6d9e0b..1823cc64faf 100644 --- a/chanrestore.go +++ b/chanrestore.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" ) @@ -180,12 +181,14 @@ func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) ( ShortChannelID: backup.ShortChannelID, IdentityPub: backup.RemoteNodePub, IsPending: false, - LocalChanCfg: backup.LocalChanCfg, - RemoteChanCfg: backup.RemoteChanCfg, RemoteCurrentRevocation: backup.RemoteNodePub, RevocationStore: shachain.NewRevocationStore(), RevocationProducer: shaChainProducer, ThawHeight: backup.LeaseExpiry, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: backup.LocalChanCfg, + Remote: backup.RemoteChanCfg, + }, }, } diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index 5940ee25bd7..1b260f0b423 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -28,6 +28,7 @@ import ( "github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" @@ -1630,7 +1631,7 @@ func testBreachSpends(t *testing.T, test breachTest) { ShortChanID: state.ShortChanID(), RemoteCurrentRevocation: state.RemoteCurrentRevocation, RemoteNextRevocation: state.RemoteNextRevocation, - LocalChanConfig: state.LocalChanCfg, + LocalChanConfig: state.ChanCfgs.Local, }) require.NoError(t, err, "unable to close channel") @@ -1841,7 +1842,7 @@ func TestBreachDelayedJusticeConfirmation(t *testing.T) { ShortChanID: state.ShortChanID(), RemoteCurrentRevocation: state.RemoteCurrentRevocation, RemoteNextRevocation: state.RemoteNextRevocation, - LocalChanConfig: state.LocalChanCfg, + LocalChanConfig: state.ChanCfgs.Local, }) require.NoError(t, err, "unable to close channel") @@ -2320,8 +2321,10 @@ func createInitChannels(t *testing.T) ( ) aliceChannelState := &channeldb.OpenChannel{ - LocalChanCfg: aliceCfg, - RemoteChanCfg: bobCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: aliceCfg, + Remote: bobCfg, + }, IdentityPub: aliceKeyPub, FundingOutpoint: *prevOut, ShortChannelID: shortChanID, @@ -2338,8 +2341,10 @@ func createInitChannels(t *testing.T) ( FundingTxn: channels.TestFundingTx, } bobChannelState := &channeldb.OpenChannel{ - LocalChanCfg: bobCfg, - RemoteChanCfg: aliceCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: bobCfg, + Remote: aliceCfg, + }, IdentityPub: bobKeyPub, FundingOutpoint: *prevOut, ShortChannelID: shortChanID, diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index b1e3fc1c287..202ae991f58 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -247,13 +247,13 @@ func newChainWatcher(cfg chainWatcherConfig) (*chainWatcher, error) { chanState := cfg.chanState if chanState.IsInitiator { stateHint = lnwallet.DeriveStateHintObfuscator( - chanState.LocalChanCfg.PaymentBasePoint.PubKey, - chanState.RemoteChanCfg.PaymentBasePoint.PubKey, + chanState.ChanCfgs.Local.PaymentBasePoint.PubKey, + chanState.ChanCfgs.Remote.PaymentBasePoint.PubKey, ) } else { stateHint = lnwallet.DeriveStateHintObfuscator( - chanState.RemoteChanCfg.PaymentBasePoint.PubKey, - chanState.LocalChanCfg.PaymentBasePoint.PubKey, + chanState.ChanCfgs.Remote.PaymentBasePoint.PubKey, + chanState.ChanCfgs.Local.PaymentBasePoint.PubKey, ) } @@ -303,8 +303,8 @@ func (c *chainWatcher) Start() error { } } - localKey := chanState.LocalChanCfg.MultiSigKey.PubKey - remoteKey := chanState.RemoteChanCfg.MultiSigKey.PubKey + localKey := chanState.ChanCfgs.Local.MultiSigKey.PubKey + remoteKey := chanState.ChanCfgs.Remote.MultiSigKey.PubKey var ( err error @@ -423,7 +423,8 @@ func (c *chainWatcher) handleUnknownLocalState( // revoke our own commitment. commitKeyRing := lnwallet.DeriveCommitmentKeys( commitPoint, lntypes.Local, c.cfg.chanState.ChanType, - &c.cfg.chanState.LocalChanCfg, &c.cfg.chanState.RemoteChanCfg, + &c.cfg.chanState.ChanCfgs.Local, + &c.cfg.chanState.ChanCfgs.Remote, ) auxResult, err := fn.MapOptionZ( @@ -472,7 +473,7 @@ func (c *chainWatcher) handleUnknownLocalState( localScript, err := lnwallet.CommitScriptToSelf( c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator, commitKeyRing.ToLocalKey, commitKeyRing.RevocationKey, - uint32(c.cfg.chanState.LocalChanCfg.CsvDelay), leaseExpiry, + uint32(c.cfg.chanState.ChanCfgs.Local.CsvDelay), leaseExpiry, localAuxLeaf, ) if err != nil { @@ -1103,7 +1104,7 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet IsPending: true, RemoteCurrentRevocation: c.cfg.chanState.RemoteCurrentRevocation, RemoteNextRevocation: c.cfg.chanState.RemoteNextRevocation, - LocalChanConfig: c.cfg.chanState.LocalChanCfg, + LocalChanConfig: c.cfg.chanState.ChanCfgs.Local, } // Attempt to add a channel sync message to the close summary. @@ -1168,7 +1169,7 @@ func (c *chainWatcher) dispatchLocalForceClose( CloseHeight: uint32(commitSpend.SpendingHeight), RemoteCurrentRevocation: c.cfg.chanState.RemoteCurrentRevocation, RemoteNextRevocation: c.cfg.chanState.RemoteNextRevocation, - LocalChanConfig: c.cfg.chanState.LocalChanCfg, + LocalChanConfig: c.cfg.chanState.ChanCfgs.Local, } // If our commitment output isn't dust or we have active HTLC's on the @@ -1309,7 +1310,7 @@ func (c *chainWatcher) dispatchContractBreach(spendEvent *chainntnfs.SpendDetail ShortChanID: c.cfg.chanState.ShortChanID(), RemoteCurrentRevocation: c.cfg.chanState.RemoteCurrentRevocation, RemoteNextRevocation: c.cfg.chanState.RemoteNextRevocation, - LocalChanConfig: c.cfg.chanState.LocalChanCfg, + LocalChanConfig: c.cfg.chanState.ChanCfgs.Local, } // Attempt to add a channel sync message to the close summary. diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 489a6051854..60f7128787b 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -348,7 +348,8 @@ func TestChainWatcherDataLossProtect(t *testing.T) { // key for this output. sweepTweak := input.SingleTweakBytes( dlpPoint, - aliceChannel.State().LocalChanCfg.PaymentBasePoint.PubKey, + //nolint:lll + aliceChannel.State().ChanCfgs.Local.PaymentBasePoint.PubKey, ) commitResolution := uniClose.CommitResolution resolutionTweak := commitResolution.SelfOutputSignDesc.SingleTweak diff --git a/contractcourt/commit_sweep_resolver.go b/contractcourt/commit_sweep_resolver.go index f2392192e87..0480f8b762e 100644 --- a/contractcourt/commit_sweep_resolver.go +++ b/contractcourt/commit_sweep_resolver.go @@ -459,7 +459,7 @@ func (c *commitSweepResolver) SupplementState(state *channeldb.OpenChannel) { if state.ChanType.HasLeaseExpiration() { c.leaseExpiry = state.ThawHeight } - c.localChanCfg = state.LocalChanCfg + c.localChanCfg = state.ChanCfgs.Local c.channelInitiator = state.IsInitiator c.chanType = state.ChanType } diff --git a/funding/manager.go b/funding/manager.go index fb36bd9903c..752a70caa20 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -2458,7 +2458,7 @@ func (f *Manager) fundeeProcessFundingCreated(peer lnpeer.Peer, SettledBalance: localBalance, RemoteCurrentRevocation: completeChan.RemoteCurrentRevocation, RemoteNextRevocation: completeChan.RemoteNextRevocation, - LocalChanConfig: completeChan.LocalChanCfg, + LocalChanConfig: completeChan.ChanCfgs.Local, } // Close the channel with us as the initiator because we are @@ -2803,7 +2803,7 @@ func (f *Manager) fundingTimeout(c *channeldb.OpenChannel, CloseType: channeldb.FundingCanceled, RemoteCurrentRevocation: c.RemoteCurrentRevocation, RemoteNextRevocation: c.RemoteNextRevocation, - LocalChanConfig: c.LocalChanCfg, + LocalChanConfig: c.ChanCfgs.Local, } // Close the channel with us as the initiator because we are timing the @@ -2903,8 +2903,8 @@ func (f *Manager) waitForFundingWithTimeout( // makeFundingScript re-creates the funding script for the funding transaction // of the target channel. func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { - localKey := channel.LocalChanCfg.MultiSigKey.PubKey - remoteKey := channel.RemoteChanCfg.MultiSigKey.PubKey + localKey := channel.ChanCfgs.Local.MultiSigKey.PubKey + remoteKey := channel.ChanCfgs.Remote.MultiSigKey.PubKey if channel.ChanType.IsTaproot() { pkScript, _, err := input.GenTaprootFundingScript( @@ -3396,7 +3396,7 @@ func (f *Manager) extractAnnounceParams(c *channeldb.OpenChannel) ( // we'll use this value within our ChannelUpdate. This constraint is // originally set by the remote node, as it will be the one that will // need to determine the smallest HTLC it deems economically relevant. - fwdMinHTLC := c.LocalChanCfg.MinHTLC + fwdMinHTLC := c.ChanCfgs.Local.MinHTLC // We don't necessarily want to go as low as the remote party allows. // Check it against our default forwarding policy. @@ -3407,7 +3407,7 @@ func (f *Manager) extractAnnounceParams(c *channeldb.OpenChannel) ( // We'll obtain the max HTLC value we can forward in our direction, as // we'll use this value within our ChannelUpdate. This value must be <= // channel capacity and <= the maximum in-flight msats set by the peer. - fwdMaxHTLC := c.LocalChanCfg.MaxPendingAmount + fwdMaxHTLC := c.ChanCfgs.Local.MaxPendingAmount capacityMSat := lnwire.NewMSatFromSatoshis(c.Capacity) if fwdMaxHTLC > capacityMSat { fwdMaxHTLC = capacityMSat @@ -3435,8 +3435,8 @@ func (f *Manager) addToGraph(completeChan *channeldb.OpenChannel, ann, err := f.newChanAnnouncement( f.cfg.IDKey, completeChan.IdentityPub, - &completeChan.LocalChanCfg.MultiSigKey, - completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, + &completeChan.ChanCfgs.Local.MultiSigKey, + completeChan.ChanCfgs.Remote.MultiSigKey.PubKey, *shortChanID, chanID, fwdMinHTLC, fwdMaxHTLC, ourPolicy, completeChan.ChanType, ) @@ -3628,8 +3628,8 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel, // public and usable for other nodes for routing. err = f.announceChannel( f.cfg.IDKey, completeChan.IdentityPub, - &completeChan.LocalChanCfg.MultiSigKey, - completeChan.RemoteChanCfg.MultiSigKey.PubKey, + &completeChan.ChanCfgs.Local.MultiSigKey, + completeChan.ChanCfgs.Remote.MultiSigKey.PubKey, *shortChanID, chanID, completeChan.ChanType, ) if err != nil { @@ -3749,7 +3749,7 @@ func genFirstStateMusigNonce(channel *channeldb.OpenChannel, // We use the _next_ commitment height here as we need to generate the // nonce for the next state the remote party will sign for us. verNonce, err := channeldb.NewMusigVerificationNonce( - channel.LocalChanCfg.MultiSigKey.PubKey, + channel.ChanCfgs.Local.MultiSigKey.PubKey, channel.LocalCommitment.CommitHeight+1, musig2ShaChain, ) @@ -4109,7 +4109,7 @@ func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID, "falling back to default values: %v", err) forwardingPolicy = f.defaultForwardingPolicy( - channel.LocalChanCfg.ChannelStateBounds, + channel.ChanCfgs.Local.ChannelStateBounds, ) needDBUpdate = true } @@ -4119,11 +4119,12 @@ func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID, // still pending while updating to this version, we'll need to set the // values to the default values. if forwardingPolicy.MinHTLCOut == 0 { - forwardingPolicy.MinHTLCOut = channel.LocalChanCfg.MinHTLC + forwardingPolicy.MinHTLCOut = channel.ChanCfgs.Local.MinHTLC needDBUpdate = true } if forwardingPolicy.MaxHTLC == 0 { - forwardingPolicy.MaxHTLC = channel.LocalChanCfg.MaxPendingAmount + forwardingPolicy.MaxHTLC = + channel.ChanCfgs.Local.MaxPendingAmount needDBUpdate = true } diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 83495f357bf..7cd76be61b2 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2738,8 +2738,8 @@ func (l *channelLink) getFeeRate() chainfee.SatPerKWeight { // // NOTE: Part of the dustHandler interface. func (l *channelLink) getDustClosure() dustClosure { - localDustLimit := l.channel.State().LocalChanCfg.DustLimit - remoteDustLimit := l.channel.State().RemoteChanCfg.DustLimit + localDustLimit := l.channel.State().ChanCfgs.Local.DustLimit + remoteDustLimit := l.channel.State().ChanCfgs.Remote.DustLimit chanType := l.channel.State().ChanType return dustHelper(chanType, localDustLimit, remoteDustLimit) diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index a71577ef2bb..9171af2bacb 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -299,8 +299,10 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, } aliceChannelState := &channeldb.OpenChannel{ - LocalChanCfg: aliceCfg, - RemoteChanCfg: bobCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: aliceCfg, + Remote: bobCfg, + }, IdentityPub: aliceKeyPub, FundingOutpoint: *prevOut, ChanType: channeldb.SingleFunderTweaklessBit, @@ -318,8 +320,10 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, } bobChannelState := &channeldb.OpenChannel{ - LocalChanCfg: bobCfg, - RemoteChanCfg: aliceCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: bobCfg, + Remote: aliceCfg, + }, IdentityPub: bobKeyPub, FundingOutpoint: *prevOut, ChanType: channeldb.SingleFunderTweaklessBit, diff --git a/lnwallet/aux_leaf_store.go b/lnwallet/aux_leaf_store.go index 4558c2f81ce..68bfbc1641e 100644 --- a/lnwallet/aux_leaf_store.go +++ b/lnwallet/aux_leaf_store.go @@ -112,8 +112,8 @@ func NewAuxChanState(chanState *channeldb.OpenChannel) AuxChanState { ShortChannelID: chanState.ShortChannelID, IsInitiator: chanState.IsInitiator, Capacity: chanState.Capacity, - LocalChanCfg: chanState.LocalChanCfg, - RemoteChanCfg: chanState.RemoteChanCfg, + LocalChanCfg: chanState.ChanCfgs.Local, + RemoteChanCfg: chanState.ChanCfgs.Remote, ThawHeight: chanState.ThawHeight, TapscriptRoot: chanState.TapscriptRoot, CustomBlob: chanState.CustomBlob, diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 644ed0e62f0..71ea230bda7 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -501,7 +501,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, // on-disk state snapshot. isDustLocal := HtlcIsDust( chanType, htlc.Incoming, lntypes.Local, feeRate, - htlc.Amt.ToSatoshis(), lc.channelState.LocalChanCfg.DustLimit, + htlc.Amt.ToSatoshis(), lc.channelState.ChanCfgs.Local.DustLimit, ) localCommitKeys := commitKeys.GetForParty(lntypes.Local) if !isDustLocal && localCommitKeys != nil { @@ -518,7 +518,8 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, } isDustRemote := HtlcIsDust( chanType, htlc.Incoming, lntypes.Remote, feeRate, - htlc.Amt.ToSatoshis(), lc.channelState.RemoteChanCfg.DustLimit, + htlc.Amt.ToSatoshis(), + lc.channelState.ChanCfgs.Remote.DustLimit, ) remoteCommitKeys := commitKeys.GetForParty(lntypes.Remote) if !isDustRemote && remoteCommitKeys != nil { @@ -641,16 +642,16 @@ func (lc *LightningChannel) diskCommitToMemCommit( commitKeys.SetForParty(lntypes.Local, DeriveCommitmentKeys( localCommitPoint, lntypes.Local, lc.channelState.ChanType, - &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, )) } if remoteCommitPoint != nil { commitKeys.SetForParty(lntypes.Remote, DeriveCommitmentKeys( remoteCommitPoint, lntypes.Remote, lc.channelState.ChanType, - &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, )) } @@ -702,9 +703,9 @@ func (lc *LightningChannel) diskCommitToMemCommit( customBlob: diskCommit.CustomBlob, } if whoseCommit.IsLocal() { - commit.dustLimit = lc.channelState.LocalChanCfg.DustLimit + commit.dustLimit = lc.channelState.ChanCfgs.Local.DustLimit } else { - commit.dustLimit = lc.channelState.RemoteChanCfg.DustLimit + commit.dustLimit = lc.channelState.ChanCfgs.Remote.DustLimit } return commit, nil @@ -974,8 +975,8 @@ func (lc *LightningChannel) createSignDesc() error { ) chanState := lc.channelState - localKey := chanState.LocalChanCfg.MultiSigKey.PubKey - remoteKey := chanState.RemoteChanCfg.MultiSigKey.PubKey + localKey := chanState.ChanCfgs.Local.MultiSigKey.PubKey + remoteKey := chanState.ChanCfgs.Remote.MultiSigKey.PubKey if chanState.ChanType.IsTaproot() { fundingPkScript, _, err = input.GenTaprootFundingScript( @@ -1005,7 +1006,7 @@ func (lc *LightningChannel) createSignDesc() error { Value: int64(lc.channelState.Capacity), } lc.signDesc = &input.SignDescriptor{ - KeyDesc: lc.channelState.LocalChanCfg.MultiSigKey, + KeyDesc: lc.channelState.ChanCfgs.Local.MultiSigKey, WitnessScript: multiSigScript, Output: &lc.fundingOutput, HashType: txscript.SigHashAll, @@ -1480,8 +1481,8 @@ func (lc *LightningChannel) restoreCommitState( pendingRemoteKeyChain = DeriveCommitmentKeys( pendingCommitPoint, lntypes.Remote, lc.channelState.ChanType, - &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, ) } @@ -1818,7 +1819,7 @@ func (lc *LightningChannel) restorePendingLocalUpdates( &logUpdate, lc.updateLogs.Remote, pendingHeight, chainfee.SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys, - lc.channelState.RemoteChanCfg.DustLimit, + lc.channelState.ChanCfgs.Remote.DustLimit, auxResult.AuxLeaves, ) if err != nil { @@ -2019,7 +2020,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // keys we'll need to reconstruct the commitment state, keyRing := DeriveCommitmentKeys( commitmentPoint, lntypes.Remote, chanState.ChanType, - &chanState.LocalChanCfg, &chanState.RemoteChanCfg, + &chanState.ChanCfgs.Local, &chanState.ChanCfgs.Remote, ) // Next, reconstruct the scripts as they were present at this state @@ -2060,7 +2061,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, return l.LocalAuxLeaf }, )(auxResult.AuxLeaves) - theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay) + theirDelay := uint32(chanState.ChanCfgs.Remote.CsvDelay) theirScript, err := CommitScriptToSelf( chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey, keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf, @@ -2109,7 +2110,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // // If our balance exceeds the remote party's dust limit, instantiate // the sign descriptor for our output. - if ourAmt >= int64(chanState.RemoteChanCfg.DustLimit) { + if ourAmt >= int64(chanState.ChanCfgs.Remote.DustLimit) { // As we're about to sweep our own output w/o a delay, we'll // obtain the witness script for the success/delay path. witnessScript, err := ourScript.WitnessScriptForPath( @@ -2121,7 +2122,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, br.LocalOutputSignDesc = &input.SignDescriptor{ SingleTweak: keyRing.LocalCommitKeyTweak, - KeyDesc: chanState.LocalChanCfg.PaymentBasePoint, + KeyDesc: chanState.ChanCfgs.Local.PaymentBasePoint, //nolint:lll WitnessScript: witnessScript, Output: &wire.TxOut{ PkScript: ourScript.PkScript(), @@ -2154,7 +2155,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // Similarly, if their balance exceeds the remote party's dust limit, // assemble the sign descriptor for their output, which we can sweep. - if theirAmt >= int64(chanState.RemoteChanCfg.DustLimit) { + if theirAmt >= int64(chanState.ChanCfgs.Remote.DustLimit) { // As we're trying to defend the channel against a breach // attempt from the remote party, we want to obain the // revocation witness script here. @@ -2166,7 +2167,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, } br.RemoteOutputSignDesc = &input.SignDescriptor{ - KeyDesc: chanState.LocalChanCfg. + KeyDesc: chanState.ChanCfgs.Local. RevocationBasePoint, DoubleTweak: commitmentSecret, WitnessScript: witnessScript, @@ -2220,7 +2221,7 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, var emptyRetribution HtlcRetribution - theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay) + theirDelay := uint32(chanState.ChanCfgs.Remote.CsvDelay) isRemoteInitiator := !chanState.IsInitiator // We'll generate the original second level witness script now, as @@ -2278,7 +2279,7 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, } signDesc := input.SignDescriptor{ - KeyDesc: chanState.LocalChanCfg. + KeyDesc: chanState.ChanCfgs.Local. RevocationBasePoint, DoubleTweak: commitmentSecret, WitnessScript: scriptInfo.WitnessScriptToSign(), @@ -2484,7 +2485,7 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, chanState.ChanType, htlc.Incoming, lntypes.Remote, chainfee.SatPerKWeight(revokedLog.FeePerKw), htlc.Amt.ToSatoshis(), - chanState.RemoteChanCfg.DustLimit, + chanState.ChanCfgs.Remote.DustLimit, ) { continue @@ -2628,10 +2629,10 @@ func (lc *LightningChannel) fetchCommitmentView( keyRing *CommitmentKeyRing) (*commitment, error) { commitChain := lc.commitChains.Local - dustLimit := lc.channelState.LocalChanCfg.DustLimit + dustLimit := lc.channelState.ChanCfgs.Local.DustLimit if whoseCommitChain.IsRemote() { commitChain = lc.commitChains.Remote - dustLimit = lc.channelState.RemoteChanCfg.DustLimit + dustLimit = lc.channelState.ChanCfgs.Remote.DustLimit } nextHeight := commitChain.tip().height + 1 @@ -3008,8 +3009,8 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, var ( isRemoteInitiator = !chanState.IsInitiator - localChanCfg = chanState.LocalChanCfg - remoteChanCfg = chanState.RemoteChanCfg + localChanCfg = chanState.ChanCfgs.Local + remoteChanCfg = chanState.ChanCfgs.Remote chanType = chanState.ChanType ) @@ -3570,10 +3571,10 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // If the added HTLCs will decrease the balance, make sure they won't // dip the local and remote balances below the channel reserves. ourReserve := lnwire.NewMSatFromSatoshis( - lc.channelState.LocalChanCfg.ChanReserve, + lc.channelState.ChanCfgs.Local.ChanReserve, ) theirReserve := lnwire.NewMSatFromSatoshis( - lc.channelState.RemoteChanCfg.ChanReserve, + lc.channelState.ChanCfgs.Remote.ChanReserve, ) // Calculate the commitment fee to log the information if needed. @@ -3657,7 +3658,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // First check that the remote updates won't violate it's channel // constraints. err = validateUpdates( - filteredView.Updates.Remote, &lc.channelState.RemoteChanCfg, + filteredView.Updates.Remote, &lc.channelState.ChanCfgs.Remote, ) if err != nil { return err @@ -3666,7 +3667,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // Secondly check that our updates won't violate our channel // constraints. err = validateUpdates( - filteredView.Updates.Local, &lc.channelState.LocalChanCfg, + filteredView.Updates.Local, &lc.channelState.ChanCfgs.Local, ) if err != nil { return err @@ -3768,7 +3769,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // construct the commitment state. keyRing := DeriveCommitmentKeys( commitPoint, lntypes.Remote, lc.channelState.ChanType, - &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, ) // Create a new commitment view which will calculate the evaluated @@ -4309,10 +4311,10 @@ func (lc *LightningChannel) computeView(view *HtlcView, lnwire.MilliSatoshi, lntypes.WeightUnit, *HtlcView, error) { commitChain := lc.commitChains.Local - dustLimit := lc.channelState.LocalChanCfg.DustLimit + dustLimit := lc.channelState.ChanCfgs.Local.DustLimit if whoseCommitChain.IsRemote() { commitChain = lc.commitChains.Remote - dustLimit = lc.channelState.RemoteChanCfg.DustLimit + dustLimit = lc.channelState.ChanCfgs.Remote.DustLimit } // Since the fetched htlc view will include all updates added after the @@ -4421,7 +4423,7 @@ func (lc *LightningChannel) computeView(view *HtlcView, } // We need to first check ourBalance and theirBalance to be negative - // because MilliSathoshi is a unsigned type and can underflow in + // because MilliSathosi is a unsigned type and can underflow in // `evaluateHTLCView`. This should never happen for views which do not // include new updates (remote or local). if int64(ourBalance) < 0 { @@ -4477,7 +4479,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, var ( isLocalInitiator = chanState.IsInitiator - localChanCfg = chanState.LocalChanCfg + localChanCfg = chanState.ChanCfgs.Local chanType = chanState.ChanType ) @@ -4866,7 +4868,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { commitPoint := input.ComputeCommitmentPoint(commitSecret[:]) keyRing := DeriveCommitmentKeys( commitPoint, lntypes.Local, lc.channelState.ChanType, - &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, ) // With the current commitment point re-calculated, construct the new @@ -4992,7 +4995,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { return err } - verifyKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey + verifyKey := lc.channelState.ChanCfgs.Remote.MultiSigKey.PubKey cSig, err := commitSigs.CommitSig.ToSignature() if err != nil { @@ -5619,11 +5622,11 @@ func (lc *LightningChannel) GetDustSum(whoseCommit lntypes.ChannelParty, var dustSum lnwire.MilliSatoshi - dustLimit := lc.channelState.LocalChanCfg.DustLimit + dustLimit := lc.channelState.ChanCfgs.Local.DustLimit commit := lc.channelState.LocalCommitment if whoseCommit.IsRemote() { // Calculate dust sum on the remote's commitment. - dustLimit = lc.channelState.RemoteChanCfg.DustLimit + dustLimit = lc.channelState.ChanCfgs.Remote.DustLimit commit = lc.channelState.RemoteCommitment } @@ -5694,8 +5697,8 @@ func (lc *LightningChannel) MayAddOutgoingHtlc(amt lnwire.MilliSatoshi) error { // In absence of a specific amount, we want to use minimum htlc value // for the channel. However certain implementations may set this value // to zero, so we only use this value if it is non-zero. - case lc.channelState.LocalChanCfg.MinHTLC != 0: - mockHtlcAmt = lc.channelState.LocalChanCfg.MinHTLC + case lc.channelState.ChanCfgs.Local.MinHTLC != 0: + mockHtlcAmt = lc.channelState.ChanCfgs.Local.MinHTLC // As a last resort, we just add a non-zero amount. default: @@ -6143,8 +6146,8 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { localCommit := lc.channelState.LocalCommitment commitTx := localCommit.CommitTx.Copy() - ourKey := lc.channelState.LocalChanCfg.MultiSigKey - theirKey := lc.channelState.RemoteChanCfg.MultiSigKey + ourKey := lc.channelState.ChanCfgs.Local.MultiSigKey + theirKey := lc.channelState.ChanCfgs.Remote.MultiSigKey var witness wire.TxWitness switch { @@ -6330,7 +6333,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, commitType := lntypes.Remote keyRing := DeriveCommitmentKeys( commitPoint, commitType, chanState.ChanType, - &chanState.LocalChanCfg, &chanState.RemoteChanCfg, + &chanState.ChanCfgs.Local, &chanState.ChanCfgs.Remote, ) auxResult, err := fn.MapOptionZ( @@ -6354,8 +6357,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, isRemoteInitiator := !chanState.IsInitiator htlcResolutions, err := extractHtlcResolutions( chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType, - signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, - &chanState.RemoteChanCfg, commitSpend.SpendingTx, + signer, remoteCommit.Htlcs, keyRing, &chanState.ChanCfgs.Local, + &chanState.ChanCfgs.Remote, commitSpend.SpendingTx, chanState.ChanType, isRemoteInitiator, leaseExpiry, auxResult.AuxLeaves, ) @@ -6404,7 +6407,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, // non-trimmed balance. var commitResolution *CommitOutputResolution if selfPoint != nil { - localPayBase := chanState.LocalChanCfg.PaymentBasePoint + localPayBase := chanState.ChanCfgs.Local.PaymentBasePoint // As the remote party has force closed, we just need the // success witness script. @@ -6465,7 +6468,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, RemoteCurrentRevocation: chanState.RemoteCurrentRevocation, RemoteNextRevocation: chanState.RemoteNextRevocation, ShortChanID: chanState.ShortChanID(), - LocalChanConfig: chanState.LocalChanCfg, + LocalChanConfig: chanState.ChanCfgs.Local, } // Attempt to add a channel sync message to the close summary. @@ -7318,7 +7321,6 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { return summary, nil } - // NewLocalForceCloseSummary generates a LocalForceCloseSummary from the given // channel state. The passed commitTx must be a fully signed commitment // transaction corresponding to localCommit. @@ -7330,7 +7332,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, // commitment transaction. We'll need this to find the corresponding // output in the commitment transaction and potentially for creating // the sign descriptor. - csvTimeout := uint32(chanState.LocalChanCfg.CsvDelay) + csvTimeout := uint32(chanState.ChanCfgs.Local.CsvDelay) // We use the passed state num to derive our scripts, since in case // this is after recovery, our latest channels state might not be up to @@ -7342,7 +7344,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, commitPoint := input.ComputeCommitmentPoint(revocation[:]) keyRing := DeriveCommitmentKeys( commitPoint, lntypes.Local, chanState.ChanType, - &chanState.LocalChanCfg, &chanState.RemoteChanCfg, + &chanState.ChanCfgs.Local, &chanState.ChanCfgs.Remote, ) auxResult, err := fn.MapOptionZ( @@ -7417,7 +7419,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, Index: delayIndex, }, SelfOutputSignDesc: input.SignDescriptor{ - KeyDesc: chanState.LocalChanCfg.DelayBasePoint, + KeyDesc: chanState.ChanCfgs.Local.DelayBasePoint, //nolint:lll SingleTweak: keyRing.LocalCommitKeyTweak, WitnessScript: witnessScript, Output: &wire.TxOut{ @@ -7458,8 +7460,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, localCommit := chanState.LocalCommitment htlcResolutions, err := extractHtlcResolutions( chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local, - signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg, - &chanState.RemoteChanCfg, commitTx, chanState.ChanType, + signer, localCommit.Htlcs, keyRing, &chanState.ChanCfgs.Local, + &chanState.ChanCfgs.Remote, commitTx, chanState.ChanType, chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves, ) if err != nil { @@ -7556,9 +7558,11 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, } closeTx := CreateCooperativeCloseTx( - fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit, - lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance, - localDeliveryScript, remoteDeliveryScript, closeTxOpts..., + fundingTxIn(lc.channelState), + lc.channelState.ChanCfgs.Local.DustLimit, + lc.channelState.ChanCfgs.Remote.DustLimit, + ourBalance, theirBalance, localDeliveryScript, + remoteDeliveryScript, closeTxOpts..., ) // Ensure that the transaction doesn't explicitly violate any @@ -7641,9 +7645,11 @@ func (lc *LightningChannel) CompleteCooperativeClose( // on this active channel back to both parties. In this current model, // the initiator pays full fees for the cooperative close transaction. closeTx := CreateCooperativeCloseTx( - fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit, - lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance, - localDeliveryScript, remoteDeliveryScript, closeTxOpts..., + fundingTxIn(lc.channelState), + lc.channelState.ChanCfgs.Local.DustLimit, + lc.channelState.ChanCfgs.Remote.DustLimit, + ourBalance, theirBalance, localDeliveryScript, + remoteDeliveryScript, closeTxOpts..., ) // Ensure that the transaction doesn't explicitly validate any @@ -7688,9 +7694,9 @@ func (lc *LightningChannel) CompleteCooperativeClose( } else { // For regular channels, we'll need to , construct the witness // stack minding the order of the pubkeys+sigs on the stack. - ourKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey. + ourKey := lc.channelState.ChanCfgs.Local.MultiSigKey.PubKey. SerializeCompressed() - theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey. + theirKey := lc.channelState.ChanCfgs.Remote.MultiSigKey.PubKey. SerializeCompressed() witness := input.SpendMultiSig( lc.signDesc.WitnessScript, ourKey, localSig, theirKey, @@ -7758,7 +7764,8 @@ func (lc *LightningChannel) NewAnchorResolutions() (*AnchorResolutions, localCommitPoint := input.ComputeCommitmentPoint(revocation[:]) localKeyRing := DeriveCommitmentKeys( localCommitPoint, lntypes.Local, lc.channelState.ChanType, - &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, ) localRes, err := NewAnchorResolution( lc.channelState, lc.channelState.LocalCommitment.CommitTx, @@ -7772,8 +7779,8 @@ func (lc *LightningChannel) NewAnchorResolutions() (*AnchorResolutions, // Add anchor for remote commitment tx, if any. remoteKeyRing := DeriveCommitmentKeys( lc.channelState.RemoteCurrentRevocation, lntypes.Remote, - lc.channelState.ChanType, &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, + lc.channelState.ChanType, &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, ) remoteRes, err := NewAnchorResolution( lc.channelState, lc.channelState.RemoteCommitment.CommitTx, @@ -7793,8 +7800,9 @@ func (lc *LightningChannel) NewAnchorResolutions() (*AnchorResolutions, if remotePendingCommit != nil { pendingRemoteKeyRing := DeriveCommitmentKeys( lc.channelState.RemoteNextRevocation, lntypes.Remote, - lc.channelState.ChanType, &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, + lc.channelState.ChanType, + &lc.channelState.ChanCfgs.Local, + &lc.channelState.ChanCfgs.Remote, ) remotePendingRes, err := NewAnchorResolution( lc.channelState, @@ -7826,8 +7834,8 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel, // will differ depending on if this is our local or remote // commitment. localAnchor, remoteAnchor, err := CommitScriptAnchors( - chanState.ChanType, &chanState.LocalChanCfg, - &chanState.RemoteChanCfg, keyRing, + chanState.ChanType, &chanState.ChanCfgs.Local, + &chanState.ChanCfgs.Remote, keyRing, ) if err != nil { return nil, err @@ -7864,7 +7872,7 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel, // Instantiate the sign descriptor that allows sweeping of the anchor. signDesc := &input.SignDescriptor{ - KeyDesc: chanState.LocalChanCfg.MultiSigKey, + KeyDesc: chanState.ChanCfgs.Local.MultiSigKey, WitnessScript: anchorWitnessScript, Output: &wire.TxOut{ PkScript: localAnchor.PkScript(), @@ -7894,14 +7902,15 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel, // tweak. signDesc.SingleTweak = keyRing.LocalCommitKeyTweak - signDesc.KeyDesc = chanState.LocalChanCfg.DelayBasePoint + signDesc.KeyDesc = + chanState.ChanCfgs.Local.DelayBasePoint } else { // When we're playing the force close of a remote // commitment, as this is a "tweakless" channel type, // we don't need a tweak value at all. // //nolint:lll - signDesc.KeyDesc = chanState.LocalChanCfg.PaymentBasePoint + signDesc.KeyDesc = chanState.ChanCfgs.Local.PaymentBasePoint } // Finally, as this is a keyspend method, we'll need to also @@ -8015,7 +8024,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *HtlcView, // We can never spend from the channel reserve, so we'll subtract it // from our available balance. ourReserve := lnwire.NewMSatFromSatoshis( - lc.channelState.LocalChanCfg.ChanReserve, + lc.channelState.ChanCfgs.Local.ChanReserve, ) if ourReserve <= ourBalance { ourBalance -= ourReserve @@ -8069,7 +8078,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *HtlcView, // enough balance to pay for the fee of our HTLC. We'll start by also // subtracting our counterparty's reserve from their balance. theirReserve := lnwire.NewMSatFromSatoshis( - lc.channelState.RemoteChanCfg.ChanReserve, + lc.channelState.ChanCfgs.Remote.ChanReserve, ) if theirReserve <= theirBalance { theirBalance -= theirReserve @@ -8080,7 +8089,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *HtlcView, // We'll use the dustlimit and htlcFee to find the largest HTLC value // that will be considered dust on the commitment. dustlimit := lnwire.NewMSatFromSatoshis( - lc.channelState.LocalChanCfg.DustLimit, + lc.channelState.ChanCfgs.Local.DustLimit, ) // For an extra HTLC fee to be paid on our commitment, the HTLC must be @@ -8093,7 +8102,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *HtlcView, // dust limit and the fee for adding an HTLC success transaction. if whoseCommitChain.IsRemote() { dustlimit = lnwire.NewMSatFromSatoshis( - lc.channelState.RemoteChanCfg.DustLimit, + lc.channelState.ChanCfgs.Remote.DustLimit, ) htlcFee = lnwire.NewMSatFromSatoshis( HtlcSuccessFee(lc.channelState.ChanType, feePerKw), @@ -8322,7 +8331,7 @@ func (lc *LightningChannel) generateRevocation(height uint64) (*lnwire.RevokeAnd // verification nonce for this target state. if lc.channelState.ChanType.IsTaproot() { nextVerificationNonce, err := channeldb.NewMusigVerificationNonce( //nolint:lll - lc.channelState.LocalChanCfg.MultiSigKey.PubKey, + lc.channelState.ChanCfgs.Local.MultiSigKey.PubKey, revHeight, lc.taprootNonceProducer, ) if err != nil { @@ -8428,7 +8437,7 @@ func (lc *LightningChannel) LocalBalanceDust() bool { localBalance += 2 * AnchorSize } - return localBalance <= chanState.LocalChanCfg.DustLimit + return localBalance <= chanState.ChanCfgs.Local.DustLimit } // RemoteBalanceDust returns true if when creating a co-op close transaction, @@ -8448,7 +8457,7 @@ func (lc *LightningChannel) RemoteBalanceDust() bool { remoteBalance += 2 * AnchorSize } - return remoteBalance <= chanState.RemoteChanCfg.DustLimit + return remoteBalance <= chanState.ChanCfgs.Remote.DustLimit } // CalcFee returns the commitment fee to use for the given fee rate @@ -8464,8 +8473,8 @@ func (lc *LightningChannel) CalcFee(feeRate chainfee.SatPerKWeight) btcutil.Amou // Moreover it returns the share of the total balance in the range of [0,1] // which can be allocated to fees. When our desired fee allocation would lead to // a maximum fee rate below the current commitment fee rate we floor the maximum -// at the current fee rate which leads to different fee allocations than -// initially requested via `maxAllocation`. +// fee rate at the current fee rate which leads to different fee allocations +// than initially requested via `maxAllocation`. // // NOTE: This should only be used for channels in which the local commitment is // the initiator. @@ -8708,7 +8717,7 @@ func (lc *LightningChannel) ActiveHtlcs() []channeldb.HTLC { // LocalChanReserve returns our local ChanReserve requirement for the remote party. func (lc *LightningChannel) LocalChanReserve() btcutil.Amount { - return lc.channelState.LocalChanCfg.ChanReserve + return lc.channelState.ChanCfgs.Local.ChanReserve } // NextLocalHtlcIndex returns the next unallocated local htlc index. To ensure @@ -8725,7 +8734,7 @@ func (lc *LightningChannel) NextLocalHtlcIndex() (uint64, error) { // FwdMinHtlc returns the minimum HTLC value required by the remote node, i.e. // the minimum value HTLC we can forward on this channel. func (lc *LightningChannel) FwdMinHtlc() lnwire.MilliSatoshi { - return lc.channelState.LocalChanCfg.MinHTLC + return lc.channelState.ChanCfgs.Local.MinHTLC } // unsignedLocalUpdates retrieves the unsigned local updates that we should @@ -8778,7 +8787,7 @@ func (lc *LightningChannel) GenMusigNonces() (*musig2.Nonces, error) { // verification nonces we'll send to the party to create our _next_ // state. lc.pendingVerificationNonce, err = channeldb.NewMusigVerificationNonce( - lc.channelState.LocalChanCfg.MultiSigKey.PubKey, + lc.channelState.ChanCfgs.Local.MultiSigKey.PubKey, lc.currentHeight+1, lc.taprootNonceProducer, ) if err != nil { @@ -8812,8 +8821,8 @@ func (lc *LightningChannel) InitRemoteMusigNonces(remoteNonce *musig2.Nonces, // commitment of the remote party. localNonce := lc.pendingVerificationNonce - localChanCfg := lc.channelState.LocalChanCfg - remoteChanCfg := lc.channelState.RemoteChanCfg + localChanCfg := lc.channelState.ChanCfgs.Local + remoteChanCfg := lc.channelState.ChanCfgs.Remote // TODO(roasbeef): propagate rename of signing and verification nonces @@ -8869,8 +8878,8 @@ func (lc *LightningChannel) MultiSigKeys() (keychain.KeyDescriptor, lc.RLock() defer lc.RUnlock() - return lc.channelState.LocalChanCfg.MultiSigKey, - lc.channelState.RemoteChanCfg.MultiSigKey + return lc.channelState.ChanCfgs.Local.MultiSigKey, + lc.channelState.ChanCfgs.Remote.MultiSigKey } // LocalCommitmentBlob returns the custom blob of the local commitment. diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index de086c0a347..6b567859085 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -963,7 +963,8 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { } // The rest of the close summary should have been populated properly. - aliceDelayPoint := aliceChannel.channelState.LocalChanCfg.DelayBasePoint + aliceDelayPoint := + aliceChannel.channelState.ChanCfgs.Local.DelayBasePoint if !aliceCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual( aliceDelayPoint.PubKey, ) { @@ -991,11 +992,11 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // Alice's listed CSV delay should also match the delay that was // pre-committed to at channel opening. if aliceCommitResolution.MaturityDelay != - uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay) { + uint32(aliceChannel.channelState.ChanCfgs.Local.CsvDelay) { t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+ "expected %v, got %v", - aliceChannel.channelState.LocalChanCfg.CsvDelay, + aliceChannel.channelState.ChanCfgs.Local.CsvDelay, aliceCommitResolution.MaturityDelay) } @@ -1039,7 +1040,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { }) htlcResolution.SweepSignDesc.InputIndex = 0 - csvDelay := uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay) + csvDelay := uint32(aliceChannel.channelState.ChanCfgs.Local.CsvDelay) if testCase.chanType.IsTaproot() { sweepTx.TxIn[0].Sequence = input.LockTimeToSequence( false, csvDelay, @@ -1141,7 +1142,8 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { sweepTx.TxIn[0].Witness, err = input.HtlcSpendSuccess( aliceChannel.Signer, &inHtlcResolution.SweepSignDesc, sweepTx, - uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay), + //nolint:lll + uint32(aliceChannel.channelState.ChanCfgs.Local.CsvDelay), ) } require.NoError(t, err, "unable to gen witness for timeout output") @@ -1171,7 +1173,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { if bobCommitResolution == nil { t.Fatalf("bob fails to include to-self output in ForceCloseSummary") } - bobDelayPoint := bobChannel.channelState.LocalChanCfg.DelayBasePoint + bobDelayPoint := bobChannel.channelState.ChanCfgs.Local.DelayBasePoint if !bobCommitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual(bobDelayPoint.PubKey) { t.Fatalf("bob incorrect pubkey in SelfOutputSignDesc") } @@ -1184,11 +1186,11 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { int64(bobCommitResolution.SelfOutputSignDesc.Output.Value)) } if bobCommitResolution.MaturityDelay != - uint32(bobChannel.channelState.LocalChanCfg.CsvDelay) { + uint32(bobChannel.channelState.ChanCfgs.Local.CsvDelay) { t.Fatalf("bob: incorrect local CSV delay in ForceCloseSummary, "+ "expected %v, got %v", - bobChannel.channelState.LocalChanCfg.CsvDelay, + bobChannel.channelState.ChanCfgs.Local.CsvDelay, bobCommitResolution.MaturityDelay) } @@ -1234,10 +1236,10 @@ func TestForceCloseDustOutput(t *testing.T) { // We set both node's channel reserves to 0, to make sure // they can create small dust outputs without going under // their channel reserves. - aliceChannel.channelState.LocalChanCfg.ChanReserve = 0 - bobChannel.channelState.LocalChanCfg.ChanReserve = 0 - aliceChannel.channelState.RemoteChanCfg.ChanReserve = 0 - bobChannel.channelState.RemoteChanCfg.ChanReserve = 0 + aliceChannel.channelState.ChanCfgs.Local.ChanReserve = 0 + bobChannel.channelState.ChanCfgs.Local.ChanReserve = 0 + aliceChannel.channelState.ChanCfgs.Remote.ChanReserve = 0 + bobChannel.channelState.ChanCfgs.Remote.ChanReserve = 0 htlcAmount := lnwire.NewMSatFromSatoshis(500) @@ -1280,7 +1282,7 @@ func TestForceCloseDustOutput(t *testing.T) { "ForceCloseSummary") } if !commitResolution.SelfOutputSignDesc.KeyDesc.PubKey.IsEqual( - aliceChannel.channelState.LocalChanCfg.DelayBasePoint.PubKey, + aliceChannel.channelState.ChanCfgs.Local.DelayBasePoint.PubKey, ) { t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc") } @@ -1293,10 +1295,11 @@ func TestForceCloseDustOutput(t *testing.T) { } if commitResolution.MaturityDelay != - uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay) { + uint32(aliceChannel.channelState.ChanCfgs.Local.CsvDelay) { + t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+ "expected %v, got %v", - aliceChannel.channelState.LocalChanCfg.CsvDelay, + aliceChannel.channelState.ChanCfgs.Local.CsvDelay, commitResolution.MaturityDelay) } @@ -1657,8 +1660,8 @@ func TestChannelBalanceDustLimit(t *testing.T) { // To allow Alice's balance to get beneath her dust limit, set the // channel reserve to be 0. - aliceChannel.channelState.LocalChanCfg.ChanReserve = 0 - bobChannel.channelState.RemoteChanCfg.ChanReserve = 0 + aliceChannel.channelState.ChanCfgs.Local.ChanReserve = 0 + bobChannel.channelState.ChanCfgs.Remote.ChanReserve = 0 // This amount should leave an amount larger than Alice's dust limit // once fees have been subtracted, but smaller than Bob's dust limit. @@ -2180,10 +2183,10 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { ) setDustLimit := func(dustVal btcutil.Amount) { - aliceChannel.channelState.LocalChanCfg.DustLimit = dustVal - aliceChannel.channelState.RemoteChanCfg.DustLimit = dustVal - bobChannel.channelState.LocalChanCfg.DustLimit = dustVal - bobChannel.channelState.RemoteChanCfg.DustLimit = dustVal + aliceChannel.channelState.ChanCfgs.Local.DustLimit = dustVal + aliceChannel.channelState.ChanCfgs.Remote.DustLimit = dustVal + bobChannel.channelState.ChanCfgs.Local.DustLimit = dustVal + bobChannel.channelState.ChanCfgs.Remote.DustLimit = dustVal } resetChannelState := func() { @@ -2871,7 +2874,7 @@ func TestAddHTLCNegativeBalance(t *testing.T) { // We set the channel reserve to 0, such that we can add HTLCs all the // way to a negative balance. - aliceChannel.channelState.LocalChanCfg.ChanReserve = 0 + aliceChannel.channelState.ChanCfgs.Local.ChanReserve = 0 // First, we'll add 3 HTLCs of 1 BTC each to Alice's commitment. const numHTLCs = 3 @@ -4971,7 +4974,7 @@ func TestChanAvailableBandwidth(t *testing.T) { require.NoError(t, err, "unable to create test channels") aliceReserve := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalChanCfg.ChanReserve, + aliceChannel.channelState.ChanCfgs.Local.ChanReserve, ) feeRate := chainfee.SatPerKWeight( aliceChannel.channelState.LocalCommitment.FeePerKw, @@ -5117,14 +5120,14 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) { bobBalance := lnwire.NewMSatFromSatoshis(5 * btcutil.SatoshiPerBitcoin) aliceReserve := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalChanCfg.ChanReserve, + aliceChannel.channelState.ChanCfgs.Local.ChanReserve, ) bobReserve := lnwire.NewMSatFromSatoshis( - bobChannel.channelState.LocalChanCfg.ChanReserve, + bobChannel.channelState.ChanCfgs.Local.ChanReserve, ) aliceDustlimit := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalChanCfg.DustLimit, + aliceChannel.channelState.ChanCfgs.Local.DustLimit, ) feeRate := chainfee.SatPerKWeight( aliceChannel.channelState.LocalCommitment.FeePerKw, @@ -5304,10 +5307,10 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { require.NoError(t, err, "unable to create test channels") aliceDustlimit := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalChanCfg.DustLimit, + aliceChannel.channelState.ChanCfgs.Local.DustLimit, ) bobDustlimit := lnwire.NewMSatFromSatoshis( - bobChannel.channelState.LocalChanCfg.DustLimit, + bobChannel.channelState.ChanCfgs.Local.DustLimit, ) feeRate := chainfee.SatPerKWeight( @@ -6171,14 +6174,14 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // Set the remote's required MaxAcceptedHtlcs. This means that Alice // can only offer the remote up to numHTLCs HTLCs. - aliceChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCs - bobChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCs + aliceChannel.channelState.ChanCfgs.Local.MaxAcceptedHtlcs = numHTLCs + bobChannel.channelState.ChanCfgs.Remote.MaxAcceptedHtlcs = numHTLCs // Similarly, set the remote config's MaxAcceptedHtlcs. This means // that the remote will be aware that Bob will only accept up to // numHTLCs at a time. - aliceChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCs - bobChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCs + aliceChannel.channelState.ChanCfgs.Remote.MaxAcceptedHtlcs = numHTLCs + bobChannel.channelState.ChanCfgs.Local.MaxAcceptedHtlcs = numHTLCs // Each HTLC amount is 0.1 BTC. htlcAmt := lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin) @@ -6298,14 +6301,14 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Set the remote's required MaxAcceptedHtlcs. This means that Alice // can only offer the remote up to numHTLCs HTLCs. - aliceChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCs - bobChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCs + aliceChannel.channelState.ChanCfgs.Local.MaxAcceptedHtlcs = numHTLCs + bobChannel.channelState.ChanCfgs.Remote.MaxAcceptedHtlcs = numHTLCs // Similarly, set the remote config's MaxAcceptedHtlcs. This means // that the remote will be aware that Bob will only accept up to // numHTLCs at a time. - aliceChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCs - bobChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCs + aliceChannel.channelState.ChanCfgs.Remote.MaxAcceptedHtlcs = numHTLCs + bobChannel.channelState.ChanCfgs.Local.MaxAcceptedHtlcs = numHTLCs // Each HTLC amount is 0.1 BTC. htlcAmt := lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin) @@ -6413,8 +6416,8 @@ func TestMaxPendingAmount(t *testing.T) { // We set the max pending amount of Alice's config. This mean that she // cannot offer Bob HTLCs with a total value above this limit at a given // time. - aliceChannel.channelState.LocalChanCfg.MaxPendingAmount = maxPending - bobChannel.channelState.RemoteChanCfg.MaxPendingAmount = maxPending + aliceChannel.channelState.ChanCfgs.Local.MaxPendingAmount = maxPending + bobChannel.channelState.ChanCfgs.Remote.MaxPendingAmount = maxPending // First, we'll add 2 HTLCs of 1.5 BTC each to Alice's commitment. // This won't trigger Alice's ErrMaxPendingAmount error. @@ -6497,20 +6500,24 @@ func TestChanReserve(t *testing.T) { // Alice will need to keep her reserve above aliceMinReserve, // so set this limit to here local config. - aliceChannel.channelState.LocalChanCfg.ChanReserve = aliceMinReserve + aliceChannel.channelState.ChanCfgs.Local.ChanReserve = + aliceMinReserve // During channel opening Bob will also get to know Alice's // minimum reserve, and this will be found in his remote // config. - bobChannel.channelState.RemoteChanCfg.ChanReserve = aliceMinReserve + bobChannel.channelState.ChanCfgs.Remote.ChanReserve = + aliceMinReserve // We set Bob's channel reserve to a value that is larger than // his current balance in the channel. This will ensure that // after a channel is first opened, Bob can still receive HTLCs // even though his balance is less than his channel reserve. bobMinReserve := btcutil.Amount(6 * btcutil.SatoshiPerBitcoin) - bobChannel.channelState.LocalChanCfg.ChanReserve = bobMinReserve - aliceChannel.channelState.RemoteChanCfg.ChanReserve = bobMinReserve + bobChannel.channelState.ChanCfgs.Local.ChanReserve = + bobMinReserve + aliceChannel.channelState.ChanCfgs.Remote.ChanReserve = + bobMinReserve return aliceChannel, bobChannel } @@ -6693,8 +6700,8 @@ func TestChanReserveRemoteInitiator(t *testing.T) { commitFee := aliceChannel.channelState.LocalCommitment.CommitFee aliceMinReserve := 5*btcutil.SatoshiPerBitcoin - commitFee - aliceChannel.channelState.LocalChanCfg.ChanReserve = aliceMinReserve - bobChannel.channelState.RemoteChanCfg.ChanReserve = aliceMinReserve + aliceChannel.channelState.ChanCfgs.Local.ChanReserve = aliceMinReserve + bobChannel.channelState.ChanCfgs.Remote.ChanReserve = aliceMinReserve // Now let Bob attempt to add an HTLC of 0.1 BTC. He has plenty of // money available to spend, but Alice, which is the initiator, cannot @@ -6748,8 +6755,8 @@ func TestChanReserveLocalInitiatorDustHtlc(t *testing.T) { commitFee := aliceChannel.channelState.LocalCommitment.CommitFee aliceMinReserve := 5*btcutil.SatoshiPerBitcoin - commitFee - htlcSat - aliceChannel.channelState.LocalChanCfg.ChanReserve = aliceMinReserve - bobChannel.channelState.RemoteChanCfg.ChanReserve = aliceMinReserve + aliceChannel.channelState.ChanCfgs.Local.ChanReserve = aliceMinReserve + bobChannel.channelState.ChanCfgs.Remote.ChanReserve = aliceMinReserve htlcDustAmt := lnwire.NewMSatFromSatoshis(htlcSat) htlc, _ := createHTLC(0, htlcDustAmt) @@ -6778,8 +6785,8 @@ func TestMinHTLC(t *testing.T) { // Setting the min value in Alice's local config means that the // remote will not accept any HTLCs of value less than specified. - aliceChannel.channelState.LocalChanCfg.MinHTLC = minValue - bobChannel.channelState.RemoteChanCfg.MinHTLC = minValue + aliceChannel.channelState.ChanCfgs.Local.MinHTLC = minValue + bobChannel.channelState.ChanCfgs.Remote.MinHTLC = minValue // First, we will add an HTLC of 0.5 BTC. This will not trigger // ErrBelowMinHTLC. @@ -6823,8 +6830,8 @@ func TestInvalidHTLCAmt(t *testing.T) { // We'll set the min HTLC values for each party to zero, which // technically would permit zero-value HTLCs. - aliceChannel.channelState.LocalChanCfg.MinHTLC = 0 - bobChannel.channelState.RemoteChanCfg.MinHTLC = 0 + aliceChannel.channelState.ChanCfgs.Local.MinHTLC = 0 + bobChannel.channelState.ChanCfgs.Remote.MinHTLC = 0 // Create a zero-value HTLC. htlcAmt := lnwire.MilliSatoshi(0) @@ -6862,10 +6869,10 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { // We'll modify the dust settings on both channels to be a predictable // value for the prurpose of the test. dustValue := btcutil.Amount(200) - aliceChannel.channelState.LocalChanCfg.DustLimit = dustValue - aliceChannel.channelState.RemoteChanCfg.DustLimit = dustValue - bobChannel.channelState.LocalChanCfg.DustLimit = dustValue - bobChannel.channelState.RemoteChanCfg.DustLimit = dustValue + aliceChannel.channelState.ChanCfgs.Local.DustLimit = dustValue + aliceChannel.channelState.ChanCfgs.Remote.DustLimit = dustValue + bobChannel.channelState.ChanCfgs.Local.DustLimit = dustValue + bobChannel.channelState.ChanCfgs.Remote.DustLimit = dustValue // We'll now create a series of dust HTLC's, and send then from Alice // to Bob, finally locking both of them in. @@ -9272,7 +9279,7 @@ func TestMayAddOutgoingHtlc(t *testing.T) { // Hard set alice's min htlc to zero and test the case where we just // fall back to a non-zero value. - aliceChannel.channelState.LocalChanCfg.MinHTLC = 0 + aliceChannel.channelState.ChanCfgs.Local.MinHTLC = 0 require.NoError(t, aliceChannel.MayAddOutgoingHtlc(0)) } @@ -9605,12 +9612,12 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { func deriveDummyRetributionParams(chanState *channeldb.OpenChannel) (uint32, *CommitmentKeyRing, chainhash.Hash) { - config := chanState.RemoteChanCfg + config := chanState.ChanCfgs.Remote commitHash := chanState.RemoteCommitment.CommitTx.TxHash() keyRing := DeriveCommitmentKeys( config.RevocationBasePoint.PubKey, lntypes.Remote, - chanState.ChanType, &chanState.LocalChanCfg, - &chanState.RemoteChanCfg, + chanState.ChanType, &chanState.ChanCfgs.Local, + &chanState.ChanCfgs.Remote, ) leaseExpiry := chanState.ThawHeight return leaseExpiry, keyRing, commitHash @@ -9966,7 +9973,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { breachHeight := uint32(101) stateNum := uint64(0) chainHash := aliceChannel.channelState.ChainHash - theirDelay := uint32(aliceChannel.channelState.RemoteChanCfg.CsvDelay) + theirDelay := uint32(aliceChannel.channelState.ChanCfgs.Remote.CsvDelay) breachTx := aliceChannel.channelState.RemoteCommitment.CommitTx // Create a breach retribution at height 0, which should give us an @@ -10313,7 +10320,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { ) require.NoError(t, err) - aliceReserve := aliceChannel.channelState.LocalChanCfg.ChanReserve + aliceReserve := aliceChannel.channelState.ChanCfgs.Local.ChanReserve capacity := aliceChannel.channelState.Capacity @@ -10348,7 +10355,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { // We need to take the remote dustlimit amount, because it the greater // one. htlcAmt2 := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.RemoteChanCfg.DustLimit + htlcFee, + aliceChannel.channelState.ChanCfgs.Remote.DustLimit + htlcFee, ) htlc2, _ := createHTLC(0, htlcAmt2) @@ -10446,7 +10453,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { ) require.NoError(t, err) - aliceReserve := aliceChannel.channelState.LocalChanCfg.ChanReserve + aliceReserve := aliceChannel.channelState.ChanCfgs.Local.ChanReserve capacity := aliceChannel.channelState.Capacity @@ -10480,7 +10487,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { // make sure this htlc is non-dust for alice. htlcFee := HtlcSuccessFee(channeldb.SingleFunderTweaklessBit, feePerKw) htlcAmt2 := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalChanCfg.DustLimit + htlcFee, + aliceChannel.channelState.ChanCfgs.Local.DustLimit + htlcFee, ) htlc2, _ := createHTLC(0, htlcAmt2) _, err = bobChannel.AddHTLC(htlc2, nil) @@ -10564,7 +10571,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { // Update the non-dust amount because we updated the fee by 100%. htlcFee = HtlcSuccessFee(channeldb.SingleFunderTweaklessBit, feePerKw*2) htlcAmt3 := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalChanCfg.DustLimit + htlcFee, + aliceChannel.channelState.ChanCfgs.Local.DustLimit + htlcFee, ) htlc3, _ := createHTLC(1, htlcAmt3) _, err = bobChannel.AddHTLC(htlc3, nil) @@ -10641,7 +10648,7 @@ func TestEnforceFeeBuffer(t *testing.T) { ) require.NoError(t, err) - aliceReserve := aliceChannel.channelState.LocalChanCfg.ChanReserve + aliceReserve := aliceChannel.channelState.ChanCfgs.Local.ChanReserve capacity := aliceChannel.channelState.Capacity diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 0f184dc2e74..4b7824853aa 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -649,14 +649,14 @@ func NewCommitmentBuilder(chanState *channeldb.OpenChannel, func createStateHintObfuscator(state *channeldb.OpenChannel) [StateHintSize]byte { if state.IsInitiator { return DeriveStateHintObfuscator( - state.LocalChanCfg.PaymentBasePoint.PubKey, - state.RemoteChanCfg.PaymentBasePoint.PubKey, + state.ChanCfgs.Local.PaymentBasePoint.PubKey, + state.ChanCfgs.Remote.PaymentBasePoint.PubKey, ) } return DeriveStateHintObfuscator( - state.RemoteChanCfg.PaymentBasePoint.PubKey, - state.LocalChanCfg.PaymentBasePoint.PubKey, + state.ChanCfgs.Remote.PaymentBasePoint.PubKey, + state.ChanCfgs.Local.PaymentBasePoint.PubKey, ) } @@ -696,9 +696,9 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, filteredHTLCView *HtlcView, keyRing *CommitmentKeyRing, prevCommit *commitment) (*unsignedCommitmentTx, error) { - dustLimit := cb.chanState.LocalChanCfg.DustLimit + dustLimit := cb.chanState.ChanCfgs.Local.DustLimit if whoseCommit.IsRemote() { - dustLimit = cb.chanState.RemoteChanCfg.DustLimit + dustLimit = cb.chanState.ChanCfgs.Remote.DustLimit } numHTLCs := int64(0) @@ -785,7 +785,8 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, if whoseCommit.IsLocal() { commitTx, err = CreateCommitTx( cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing, - &cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg, + &cb.chanState.ChanCfgs.Local, + &cb.chanState.ChanCfgs.Remote, ourBalance.ToSatoshis(), theirBalance.ToSatoshis(), numHTLCs, cb.chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves, @@ -793,7 +794,8 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, } else { commitTx, err = CreateCommitTx( cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing, - &cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg, + &cb.chanState.ChanCfgs.Remote, + &cb.chanState.ChanCfgs.Local, theirBalance.ToSatoshis(), ourBalance.ToSatoshis(), numHTLCs, !cb.chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves, @@ -1288,13 +1290,13 @@ func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash, // which will be used to generate the output scripts. keyRing := DeriveCommitmentKeys( commitmentPoint, lntypes.Remote, chanState.ChanType, - &chanState.LocalChanCfg, &chanState.RemoteChanCfg, + &chanState.ChanCfgs.Local, &chanState.ChanCfgs.Remote, ) // Since it's remote commitment chain, we'd used the mirrored values. // // We use the remote's channel config for the csv delay. - theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay) + theirDelay := uint32(chanState.ChanCfgs.Remote.CsvDelay) // If we are the initiator of this channel, then it's be false from the // remote's PoV. diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index a9f71f24c1e..da8493e3108 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -314,8 +314,10 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, ) aliceChannelState := &channeldb.OpenChannel{ - LocalChanCfg: aliceCfg, - RemoteChanCfg: bobCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: aliceCfg, + Remote: bobCfg, + }, IdentityPub: aliceKeys[0].PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, @@ -332,8 +334,10 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, FundingTxn: testTx, } bobChannelState := &channeldb.OpenChannel{ - LocalChanCfg: bobCfg, - RemoteChanCfg: aliceCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: bobCfg, + Remote: aliceCfg, + }, IdentityPub: bobKeys[0].PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 3588acfebfd..58cb227cd45 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -971,8 +971,10 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp ) remoteChannelState := &channeldb.OpenChannel{ - LocalChanCfg: remoteCfg, - RemoteChanCfg: localCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: remoteCfg, + Remote: localCfg, + }, IdentityPub: remoteDummy2.PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, @@ -989,8 +991,10 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp FundingTxn: tc.fundingTx.MsgTx(), } localChannelState := &channeldb.OpenChannel{ - LocalChanCfg: localCfg, - RemoteChanCfg: remoteCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: localCfg, + Remote: remoteCfg, + }, IdentityPub: localDummy2.PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 7e455ab4857..2c79ce5b9c1 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -2241,8 +2241,8 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // As we've completed the funding process, we'll no convert the // contribution structs into their underlying channel config objects to // he stored within the database. - res.partialState.LocalChanCfg = res.ourContribution.toChanConfig() - res.partialState.RemoteChanCfg = res.theirContribution.toChanConfig() + res.partialState.ChanCfgs.Local = res.ourContribution.toChanConfig() + res.partialState.ChanCfgs.Remote = res.theirContribution.toChanConfig() // We'll also record the finalized funding txn, which will allow us to // rebroadcast on startup in case we fail. @@ -2418,8 +2418,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // Add the complete funding transaction to the DB, in it's open bucket // which will be used for the lifetime of this channel. - chanState.LocalChanCfg = pendingReservation.ourContribution.toChanConfig() - chanState.RemoteChanCfg = pendingReservation.theirContribution.toChanConfig() + chanState.ChanCfgs.Local = + pendingReservation.ourContribution.toChanConfig() + chanState.ChanCfgs.Remote = + pendingReservation.theirContribution.toChanConfig() chanState.RevocationKeyLocator = pendingReservation.nextRevocationKeyLoc @@ -2510,8 +2512,8 @@ func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, return err } - localKey := channelState.LocalChanCfg.MultiSigKey.PubKey - remoteKey := channelState.RemoteChanCfg.MultiSigKey.PubKey + localKey := channelState.ChanCfgs.Local.MultiSigKey.PubKey + remoteKey := channelState.ChanCfgs.Remote.MultiSigKey.PubKey // We'll also need the multi-sig witness script itself so the // chanvalidate package can check it for correctness against the diff --git a/peer/test_utils.go b/peer/test_utils.go index e0ae29be8b6..e35a2745780 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lntest/mock" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" @@ -243,8 +244,10 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, ) aliceChannelState := &channeldb.OpenChannel{ - LocalChanCfg: aliceCfg, - RemoteChanCfg: bobCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: aliceCfg, + Remote: bobCfg, + }, IdentityPub: aliceKeyPub, FundingOutpoint: *prevOut, ShortChannelID: shortChanID, @@ -261,8 +264,10 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, FundingTxn: channels.TestFundingTx, } bobChannelState := &channeldb.OpenChannel{ - LocalChanCfg: bobCfg, - RemoteChanCfg: aliceCfg, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: bobCfg, + Remote: aliceCfg, + }, IdentityPub: bobKeyPub, FundingOutpoint: *prevOut, ChanType: channeldb.SingleFunderTweaklessBit, diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go index f0f9b88de00..dd6202936d2 100644 --- a/routing/localchans/manager.go +++ b/routing/localchans/manager.go @@ -279,9 +279,9 @@ func (r *Manager) getHtlcAmtLimits(tx kvdb.RTx, chanPoint wire.OutPoint) ( // capacity AND less than or equal to the max in-flight HTLC value. // Since the latter is always less than or equal to the former, just // return the max in-flight value. - maxAmt := ch.LocalChanCfg.ChannelStateBounds.MaxPendingAmount + maxAmt := ch.ChanCfgs.Local.ChannelStateBounds.MaxPendingAmount - return ch.LocalChanCfg.MinHTLC, maxAmt, nil + return ch.ChanCfgs.Local.MinHTLC, maxAmt, nil } // makeFailureItem creates a lnrpc.FailedUpdate object. diff --git a/routing/localchans/manager_test.go b/routing/localchans/manager_test.go index 7594eef04ad..9a9fad2fdce 100644 --- a/routing/localchans/manager_test.go +++ b/routing/localchans/manager_test.go @@ -11,6 +11,7 @@ import ( "github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/stretchr/testify/require" @@ -131,8 +132,10 @@ func TestManager(t *testing.T) { } return &channeldb.OpenChannel{ - LocalChanCfg: channeldb.ChannelConfig{ - ChannelStateBounds: bounds, + ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ + Local: channeldb.ChannelConfig{ + ChannelStateBounds: bounds, + }, }, }, nil } diff --git a/rpcserver.go b/rpcserver.go index d46e04741ef..a400b191b2e 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3661,8 +3661,10 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) { Capacity: int64(pendingChan.Capacity), LocalBalance: int64(localCommitment.LocalBalance.ToSatoshis()), RemoteBalance: int64(localCommitment.RemoteBalance.ToSatoshis()), - LocalChanReserveSat: int64(pendingChan.LocalChanCfg.ChanReserve), - RemoteChanReserveSat: int64(pendingChan.RemoteChanCfg.ChanReserve), + //nolint:lll + LocalChanReserveSat: int64(pendingChan.ChanCfgs.Local.ChanReserve), + //nolint:lll + RemoteChanReserveSat: int64(pendingChan.ChanCfgs.Remote.ChanReserve), Initiator: rpcInitiator(pendingChan.IsInitiator), CommitmentType: rpcCommitmentType(pendingChan.ChanType), Private: isPrivate(pendingChan), @@ -3951,8 +3953,10 @@ func (r *rpcServer) fetchWaitingCloseChannels( Capacity: int64(waitingClose.Capacity), LocalBalance: int64(waitingClose.LocalCommitment.LocalBalance.ToSatoshis()), RemoteBalance: int64(waitingClose.LocalCommitment.RemoteBalance.ToSatoshis()), - LocalChanReserveSat: int64(waitingClose.LocalChanCfg.ChanReserve), - RemoteChanReserveSat: int64(waitingClose.RemoteChanCfg.ChanReserve), + //nolint:lll + LocalChanReserveSat: int64(waitingClose.ChanCfgs.Local.ChanReserve), + //nolint:lll + RemoteChanReserveSat: int64(waitingClose.ChanCfgs.Remote.ChanReserve), Initiator: rpcInitiator(waitingClose.IsInitiator), CommitmentType: rpcCommitmentType(waitingClose.ChanType), NumForwardingPackages: int64(len(fwdPkgs)), @@ -4473,10 +4477,10 @@ func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel, CommitmentType: commitmentType, ThawHeight: dbChannel.ThawHeight, LocalConstraints: createChannelConstraint( - &dbChannel.LocalChanCfg, + &dbChannel.ChanCfgs.Local, ), RemoteConstraints: createChannelConstraint( - &dbChannel.RemoteChanCfg, + &dbChannel.ChanCfgs.Remote, ), AliasScids: make([]uint64, 0, len(channelAliases)), PeerScidAlias: peerScidAlias.ToUint64(), @@ -4484,9 +4488,11 @@ func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel, ZeroConfConfirmedScid: dbChannel.ZeroConfRealScid().ToUint64(), Memo: string(dbChannel.Memo), // TODO: remove the following deprecated fields - CsvDelay: uint32(dbChannel.LocalChanCfg.CsvDelay), - LocalChanReserveSat: int64(dbChannel.LocalChanCfg.ChanReserve), - RemoteChanReserveSat: int64(dbChannel.RemoteChanCfg.ChanReserve), + CsvDelay: uint32(dbChannel.ChanCfgs.Local.CsvDelay), + //nolint:lll + LocalChanReserveSat: int64(dbChannel.ChanCfgs.Local.ChanReserve), + //nolint:lll + RemoteChanReserveSat: int64(dbChannel.ChanCfgs.Remote.ChanReserve), } // Look up our channel peer's node alias if the caller requests it. From 6716277f2658648480fb31bd8a563e4ab51cfed2 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Tue, 20 Aug 2024 19:38:05 -0600 Subject: [PATCH 18/22] multi: pack ChannelCommitments into Dual in OpenChannel --- channeldb/channel.go | 50 +++-- channeldb/channel_test.go | 90 ++++----- channeldb/db.go | 15 +- contractcourt/breach_arbitrator_test.go | 22 ++- contractcourt/chain_arbitrator.go | 4 +- contractcourt/chain_watcher.go | 3 +- contractcourt/chain_watcher_test.go | 6 +- funding/manager.go | 7 +- htlcswitch/link.go | 7 +- htlcswitch/test_utils.go | 26 +-- lnrpc/invoicesrpc/addinvoice.go | 6 +- lnrpc/invoicesrpc/addinvoice_test.go | 25 ++- lnrpc/walletrpc/walletkit_server.go | 8 +- lnwallet/channel.go | 38 ++-- lnwallet/channel_test.go | 246 +++++++++++++----------- lnwallet/commitment.go | 2 +- lnwallet/reservation.go | 28 +-- lnwallet/test_utils.go | 22 ++- lnwallet/transactions_test.go | 24 ++- lnwallet/wallet.go | 25 +-- peer/test_utils.go | 22 ++- pilot.go | 4 +- rpcserver.go | 55 +++--- 23 files changed, 400 insertions(+), 335 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 5a59be91562..d775e3abf2d 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -941,17 +941,11 @@ type OpenChannel struct { // ChanCfgs is the channel configuration for the local and remote nodes. ChanCfgs lntypes.Dual[ChannelConfig] - // LocalCommitment is the current local commitment state for the local - // party. This is stored distinct from the state of the remote party - // as there are certain asymmetric parameters which affect the - // structure of each commitment. - LocalCommitment ChannelCommitment - - // RemoteCommitment is the current remote commitment state for the - // remote party. This is stored distinct from the state of the local - // party as there are certain asymmetric parameters which affect the - // structure of each commitment. - RemoteCommitment ChannelCommitment + // Commitments is the pair of ChannelCommitments for both the + // local and remote parties. They are stored distinctly as there are + // certain asymmetric parameters which affect the structure of each + // commitment. + Commitments lntypes.Dual[ChannelCommitment] // RemoteCurrentRevocation is the current revocation for their // commitment transaction. However, since this the derived public key, @@ -1048,13 +1042,13 @@ 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 + commit := c.Commitments.Local local := fmt.Sprintf(indexStr, commit.CommitHeight, commit.LocalHtlcIndex, commit.LocalLogIndex, commit.RemoteHtlcIndex, commit.RemoteLogIndex, ) - commit = c.RemoteCommitment + commit = c.Commitments.Remote remote := fmt.Sprintf(indexStr, commit.CommitHeight, commit.LocalHtlcIndex, commit.LocalLogIndex, commit.RemoteHtlcIndex, commit.RemoteLogIndex, @@ -1760,13 +1754,13 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) { // one. If the receiver thinks that our commitment height is actually // *equal* to this value, then they'll re-send the last commitment that // they sent but we never fully processed. - localHeight := c.LocalCommitment.CommitHeight + localHeight := c.Commitments.Local.CommitHeight nextLocalCommitHeight := localHeight + 1 // The second value we'll send is the height of the remote commitment // from our PoV. If the receiver thinks that their height is actually // *one plus* this value, then they'll re-send their last revocation. - remoteChainTipHeight := c.RemoteCommitment.CommitHeight + remoteChainTipHeight := c.Commitments.Remote.CommitHeight // If this channel has undergone a commitment update, then in order to // prove to the remote party our knowledge of their prior commitment @@ -2410,7 +2404,7 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment, return nil, err } - c.LocalCommitment = *newCommitment + c.Commitments.Local = *newCommitment return finalHtlcs, nil } @@ -2471,7 +2465,7 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { // transactions. So we'll iterate through their set of HTLC's to note // which ones are present on their commitment. remoteHtlcs := make(map[[32]byte]struct{}) - for _, htlc := range c.RemoteCommitment.Htlcs { + for _, htlc := range c.Commitments.Remote.Htlcs { log.Tracef("RemoteCommitment has htlc: id=%v, update=%v "+ "incoming=%v", htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming) @@ -2483,7 +2477,7 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { // Now that we know which HTLC's they have, we'll only mark the HTLC's // as active if *we* know them as well. activeHtlcs := make([]HTLC, 0, len(remoteHtlcs)) - for _, htlc := range c.LocalCommitment.Htlcs { + for _, htlc := range c.Commitments.Local.Htlcs { log.Tracef("LocalCommitment has htlc: id=%v, update=%v "+ "incoming=%v", htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming) @@ -3327,7 +3321,7 @@ func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg, // With the commitment pointer swapped, we can now add the // revoked (prior) state to the revocation log. err = putRevocationLog( - logBucket, &c.RemoteCommitment, ourOutputIndex, + logBucket, &c.Commitments.Remote, ourOutputIndex, theirOutputIndex, c.Db.parent.noRevLogAmtData, ) if err != nil { @@ -3409,7 +3403,7 @@ func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg, // With the db transaction complete, we'll swap over the in-memory // pointer of the new remote commitment, which was previously the tip // of the commit chain. - c.RemoteCommitment = *newRemoteCommit + c.Commitments.Remote = *newRemoteCommit return nil } @@ -3464,7 +3458,7 @@ func (c *OpenChannel) NextLocalHtlcIndex() (uint64, error) { } // Otherwise, fallback to using the local htlc index of their commitment. - return c.RemoteCommitment.LocalHtlcIndex, nil + return c.Commitments.Remote.LocalHtlcIndex, nil } // LoadFwdPkgs scans the forwarding log for any packages that haven't been @@ -3559,7 +3553,7 @@ func (c *OpenChannel) revocationLogTailCommitHeight() (uint64, error) { // If we haven't created any state updates yet, then we'll exit early as // there's nothing to be found on disk in the revocation bucket. - if c.RemoteCommitment.CommitHeight == 0 { + if c.Commitments.Remote.CommitHeight == 0 { return height, nil } @@ -3981,7 +3975,7 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot { c.RLock() defer c.RUnlock() - localCommit := c.LocalCommitment + localCommit := c.Commitments.Local snapshot := &ChannelSnapshot{ RemoteIdentity: *c.IdentityPub, ChannelPoint: c.FundingOutpoint, @@ -4033,7 +4027,7 @@ func (c *OpenChannel) LatestCommitments() (*ChannelCommitment, *ChannelCommitmen return nil, nil, err } - return &c.LocalCommitment, &c.RemoteCommitment, nil + return &c.Commitments.Local, &c.Commitments.Remote, nil } // RemoteRevocationStore returns the most up to date commitment version of the @@ -4412,14 +4406,14 @@ func putChanCommitments(chanBucket kvdb.RwBucket, channel *OpenChannel) error { } err := putChanCommitment( - chanBucket, &channel.LocalCommitment, true, + chanBucket, &channel.Commitments.Local, true, ) if err != nil { return err } return putChanCommitment( - chanBucket, &channel.RemoteCommitment, false, + chanBucket, &channel.Commitments.Remote, false, ) } @@ -4590,11 +4584,11 @@ func fetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error { return nil } - channel.LocalCommitment, err = fetchChanCommitment(chanBucket, true) + channel.Commitments.Local, err = fetchChanCommitment(chanBucket, true) if err != nil { return err } - channel.RemoteCommitment, err = fetchChanCommitment(chanBucket, false) + channel.Commitments.Remote, err = fetchChanCommitment(chanBucket, false) if err != nil { return err } diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 91ad13e97ec..af17297902e 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -118,7 +118,7 @@ func openChannelOption() testChannelOption { // commitment. func localHtlcsOption(htlcs []HTLC) testChannelOption { return func(params *testChannelParams) { - params.channel.LocalCommitment.Htlcs = htlcs + params.channel.Commitments.Local.Htlcs = htlcs } } @@ -126,7 +126,7 @@ func localHtlcsOption(htlcs []HTLC) testChannelOption { // commitment. func remoteHtlcsOption(htlcs []HTLC) testChannelOption { return func(params *testChannelParams) { - params.channel.RemoteCommitment.Htlcs = htlcs + params.channel.Commitments.Remote.Htlcs = htlcs } } @@ -331,39 +331,41 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { copy(tapscriptRoot[:], bytes.Repeat([]byte{1}, 32)) return &OpenChannel{ - ChanType: SingleFunderBit | FrozenBit, - ChainHash: key, - FundingOutpoint: op, - ShortChannelID: chanID, - IsInitiator: true, - IsPending: true, - IdentityPub: pubKey, - Capacity: btcutil.Amount(10000), + ChanType: SingleFunderBit | FrozenBit, + ChainHash: key, + FundingOutpoint: op, + ShortChannelID: chanID, + IsInitiator: true, + IsPending: true, + IdentityPub: pubKey, + Capacity: btcutil.Amount(10000), ChanCfgs: lntypes.Dual[ChannelConfig]{ Local: localCfg, Remote: remoteCfg, }, TotalMSatSent: 8, TotalMSatReceived: 2, - LocalCommitment: ChannelCommitment{ - CommitHeight: 0, - LocalBalance: lnwire.MilliSatoshi(9000), - RemoteBalance: lnwire.MilliSatoshi(3000), - CommitFee: btcutil.Amount(rand.Int63()), - FeePerKw: btcutil.Amount(5000), - CommitTx: channels.TestFundingTx, - CommitSig: bytes.Repeat([]byte{1}, 71), - CustomBlob: fn.Some([]byte{1, 2, 3}), - }, - RemoteCommitment: ChannelCommitment{ - CommitHeight: 0, - LocalBalance: lnwire.MilliSatoshi(3000), - RemoteBalance: lnwire.MilliSatoshi(9000), - CommitFee: btcutil.Amount(rand.Int63()), - FeePerKw: btcutil.Amount(5000), - CommitTx: channels.TestFundingTx, - CommitSig: bytes.Repeat([]byte{1}, 71), - CustomBlob: fn.Some([]byte{4, 5, 6}), + Commitments: lntypes.Dual[ChannelCommitment]{ + Local: ChannelCommitment{ + CommitHeight: 0, + LocalBalance: lnwire.MilliSatoshi(9000), + RemoteBalance: lnwire.MilliSatoshi(3000), + CommitFee: btcutil.Amount(rand.Int63()), + FeePerKw: btcutil.Amount(5000), + CommitTx: channels.TestFundingTx, + CommitSig: bytes.Repeat([]byte{1}, 71), + CustomBlob: fn.Some([]byte{1, 2, 3}), + }, + Remote: ChannelCommitment{ + CommitHeight: 0, + LocalBalance: lnwire.MilliSatoshi(3000), + RemoteBalance: lnwire.MilliSatoshi(9000), + CommitFee: btcutil.Amount(rand.Int63()), + FeePerKw: btcutil.Amount(5000), + CommitTx: channels.TestFundingTx, + CommitSig: bytes.Repeat([]byte{1}, 71), + CustomBlob: fn.Some([]byte{4, 5, 6}), + }, }, NumConfsRequired: 4, RemoteCurrentRevocation: privKey.PubKey(), @@ -661,7 +663,7 @@ func TestChannelStateTransition(t *testing.T) { // Additionally, modify the signature and commitment transaction. newSequence := uint32(129498) newSig := bytes.Repeat([]byte{3}, 71) - newTx := channel.LocalCommitment.CommitTx.Copy() + newTx := channel.Commitments.Local.CommitTx.Copy() newTx.TxIn[0].Sequence = newSequence commitment := ChannelCommitment{ CommitHeight: 1, @@ -714,11 +716,9 @@ func TestChannelStateTransition(t *testing.T) { // have been updated. updatedChannel, err := cdb.FetchOpenChannels(channel.IdentityPub) require.NoError(t, err, "unable to fetch updated channel") - assertCommitmentEqual( - t, &commitment, &updatedChannel[0].LocalCommitment, + t, &commitment, &updatedChannel[0].Commitments.Local, ) - numDiskUpdates, err := updatedChannel[0].CommitmentHeight() require.NoError(t, err, "unable to read commitment height from disk") @@ -790,7 +790,7 @@ func TestChannelStateTransition(t *testing.T) { // We'll save the old remote commitment as this will be added to the // revocation log shortly. - oldRemoteCommit := channel.RemoteCommitment + oldRemoteCommit := channel.Commitments.Remote // Next, write to the log which tracks the necessary revocation state // needed to rectify any fishy behavior by the remote party. Modify the @@ -842,7 +842,7 @@ func TestChannelStateTransition(t *testing.T) { t.Fatal("update number doesn't match") } - oldRemoteCommit = channel.RemoteCommitment + oldRemoteCommit = channel.Commitments.Remote // Next modify the posted diff commitment slightly, then create a new // commitment diff and advance the tail. @@ -1019,15 +1019,17 @@ func TestFetchClosedChannels(t *testing.T) { // Next, close the channel by including a close channel summary in the // database. summary := &ChannelCloseSummary{ - ChanPoint: state.FundingOutpoint, - ClosingTXID: rev, - RemotePub: state.IdentityPub, - Capacity: state.Capacity, - SettledBalance: state.LocalCommitment.LocalBalance.ToSatoshis(), - TimeLockedBalance: state.RemoteCommitment.LocalBalance.ToSatoshis() + 10000, - CloseType: RemoteForceClose, - IsPending: true, - LocalChanConfig: state.ChanCfgs.Local, + ChanPoint: state.FundingOutpoint, + ClosingTXID: rev, + RemotePub: state.IdentityPub, + Capacity: state.Capacity, + SettledBalance: state.Commitments.Local. + LocalBalance.ToSatoshis(), + TimeLockedBalance: state.Commitments.Remote. + LocalBalance.ToSatoshis() + 10000, + CloseType: RemoteForceClose, + IsPending: true, + LocalChanConfig: state.ChanCfgs.Local, } if err := state.CloseChannel(summary); err != nil { t.Fatalf("unable to close channel: %v", err) diff --git a/channeldb/db.go b/channeldb/db.go index 88f401c7e26..d16b6797004 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -1410,13 +1410,14 @@ func (c *ChannelStateDB) AbandonChannel(chanPoint *wire.OutPoint, // channel as possible. We also ensure that we set Pending to false, to // indicate that this channel has been "fully" closed. summary := &ChannelCloseSummary{ - CloseType: Abandoned, - ChanPoint: *chanPoint, - ChainHash: dbChan.ChainHash, - CloseHeight: bestHeight, - RemotePub: dbChan.IdentityPub, - Capacity: dbChan.Capacity, - SettledBalance: dbChan.LocalCommitment.LocalBalance.ToSatoshis(), + CloseType: Abandoned, + ChanPoint: *chanPoint, + ChainHash: dbChan.ChainHash, + CloseHeight: bestHeight, + RemotePub: dbChan.IdentityPub, + Capacity: dbChan.Capacity, + SettledBalance: dbChan.Commitments.Local. + LocalBalance.ToSatoshis(), ShortChanID: dbChan.ShortChanID(), RemoteCurrentRevocation: dbChan.RemoteCurrentRevocation, RemoteNextRevocation: dbChan.RemoteNextRevocation, diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index 1b260f0b423..0ebebe90e0b 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -2334,11 +2334,13 @@ func createInitChannels(t *testing.T) ( RemoteCurrentRevocation: bobCommitPoint, RevocationProducer: alicePreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: aliceCommit, - RemoteCommitment: aliceCommit, - Db: dbAlice.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: channels.TestFundingTx, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: aliceCommit, + Remote: aliceCommit, + }, + Db: dbAlice.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: channels.TestFundingTx, } bobChannelState := &channeldb.OpenChannel{ ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ @@ -2354,10 +2356,12 @@ func createInitChannels(t *testing.T) ( RemoteCurrentRevocation: aliceCommitPoint, RevocationProducer: bobPreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: bobCommit, - RemoteCommitment: bobCommit, - Db: dbBob.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: bobCommit, + Remote: bobCommit, + }, + Db: dbBob.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), } aliceSigner := input.NewMockSigner( diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index dbc97939a94..42bc4b431b7 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -446,8 +446,8 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel, // Finally, we'll need to construct a series of htlc Sets based on all // currently known valid commitments. htlcSets := make(map[HtlcSetKey]htlcSet) - htlcSets[LocalHtlcSet] = newHtlcSet(channel.LocalCommitment.Htlcs) - htlcSets[RemoteHtlcSet] = newHtlcSet(channel.RemoteCommitment.Htlcs) + htlcSets[LocalHtlcSet] = newHtlcSet(channel.Commitments.Local.Htlcs) + htlcSets[RemoteHtlcSet] = newHtlcSet(channel.Commitments.Remote.Htlcs) pendingRemoteCommitment, err := channel.RemoteCommitChainTip() if err != nil && err != channeldb.ErrNoPendingCommit { diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 202ae991f58..bf96e684b70 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -433,7 +433,8 @@ func (c *chainWatcher) handleUnknownLocalState( func(s lnwallet.AuxLeafStore) fn.Result[lnwallet.CommitDiffAuxResult] { return s.FetchLeavesFromCommit( lnwallet.NewAuxChanState(c.cfg.chanState), - c.cfg.chanState.LocalCommitment, *commitKeyRing, + c.cfg.chanState.Commitments.Local, + *commitKeyRing, ) }, ).Unpack() diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 60f7128787b..c70fbbfffe7 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -54,7 +54,7 @@ func TestChainWatcherRemoteUnilateralClose(t *testing.T) { // If we simulate an immediate broadcast of the current commitment by // Bob, then the chain watcher should detect this case. - bobCommit := bobChannel.State().LocalCommitment.CommitTx + bobCommit := bobChannel.State().Commitments.Local.CommitTx bobTxHash := bobCommit.TxHash() bobSpend := &chainntnfs.SpendDetail{ SpenderTxHash: &bobTxHash, @@ -321,7 +321,7 @@ func TestChainWatcherDataLossProtect(t *testing.T) { // Now we'll trigger the channel close event to trigger the // scenario. - bobCommit := bobChannel.State().LocalCommitment.CommitTx + bobCommit := bobChannel.State().Commitments.Local.CommitTx bobTxHash := bobCommit.TxHash() bobSpend := &chainntnfs.SpendDetail{ SpenderTxHash: &bobTxHash, @@ -481,7 +481,7 @@ func TestChainWatcherLocalForceCloseDetect(t *testing.T) { // Next, we'll obtain Alice's commitment transaction and // trigger a force close. This should cause her to detect a // local force close, and dispatch a local close event. - aliceCommit := aliceChannel.State().LocalCommitment.CommitTx + aliceCommit := aliceChannel.State().Commitments.Local.CommitTx // Since this is Alice's commitment, her output is always first // since she's the one creating the HTLCs (lower balance). In diff --git a/funding/manager.go b/funding/manager.go index 752a70caa20..ef45c01247e 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -2448,7 +2448,8 @@ func (f *Manager) fundeeProcessFundingCreated(peer lnpeer.Peer, // we use this convenience method to delete the pending OpenChannel // from the database. deleteFromDatabase := func() { - localBalance := completeChan.LocalCommitment.LocalBalance.ToSatoshis() + localBalance := completeChan.Commitments.Local. + LocalBalance.ToSatoshis() closeInfo := &channeldb.ChannelCloseSummary{ ChanPoint: completeChan.FundingOutpoint, ChainHash: completeChan.ChainHash, @@ -2793,7 +2794,7 @@ func (f *Manager) fundingTimeout(c *channeldb.OpenChannel, // We'll get a timeout if the number of blocks mined since the channel // was initiated reaches MaxWaitNumBlocksFundingConf and we are not the // channel initiator. - localBalance := c.LocalCommitment.LocalBalance.ToSatoshis() + localBalance := c.Commitments.Local.LocalBalance.ToSatoshis() closeInfo := &channeldb.ChannelCloseSummary{ ChainHash: c.ChainHash, ChanPoint: c.FundingOutpoint, @@ -3750,7 +3751,7 @@ func genFirstStateMusigNonce(channel *channeldb.OpenChannel, // nonce for the next state the remote party will sign for us. verNonce, err := channeldb.NewMusigVerificationNonce( channel.ChanCfgs.Local.MultiSigKey.PubKey, - channel.LocalCommitment.CommitHeight+1, + channel.Commitments.Local.CommitHeight+1, musig2ShaChain, ) if err != nil { diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 7cd76be61b2..e5fa06c0a8d 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2361,7 +2361,8 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { chanID := l.ChanID() err = l.cfg.TowerClient.BackupState( - &chanID, state.RemoteCommitment.CommitHeight-1, + &chanID, + state.Commitments.Remote.CommitHeight-1, ) if err != nil { l.failf(LinkFailureError{ @@ -2752,10 +2753,10 @@ func (l *channelLink) getDustClosure() dustClosure { // NOTE: Part of the dustHandler interface. func (l *channelLink) getCommitFee(remote bool) btcutil.Amount { if remote { - return l.channel.State().RemoteCommitment.CommitFee + return l.channel.State().Commitments.Remote.CommitFee } - return l.channel.State().LocalCommitment.CommitFee + return l.channel.State().Commitments.Local.CommitFee } // exceedsFeeExposureLimit returns whether or not the new proposed fee-rate diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 9171af2bacb..bffcadce237 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -311,12 +311,14 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, RemoteCurrentRevocation: bobCommitPoint, RevocationProducer: alicePreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: aliceCommit, - RemoteCommitment: aliceCommit, - ShortChannelID: chanID, - Db: dbAlice.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(chanID), - FundingTxn: channels.TestFundingTx, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: aliceCommit, + Remote: aliceCommit, + }, + ShortChannelID: chanID, + Db: dbAlice.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(chanID), + FundingTxn: channels.TestFundingTx, } bobChannelState := &channeldb.OpenChannel{ @@ -332,11 +334,13 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, RemoteCurrentRevocation: aliceCommitPoint, RevocationProducer: bobPreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: bobCommit, - RemoteCommitment: bobCommit, - ShortChannelID: chanID, - Db: dbBob.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(chanID), + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: bobCommit, + Remote: bobCommit, + }, + ShortChannelID: chanID, + Db: dbBob.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(chanID), } if err := aliceChannelState.SyncPending(bobAddr, broadcastHeight); err != nil { diff --git a/lnrpc/invoicesrpc/addinvoice.go b/lnrpc/invoicesrpc/addinvoice.go index dcb1bef71ee..885ec397f0d 100644 --- a/lnrpc/invoicesrpc/addinvoice.go +++ b/lnrpc/invoicesrpc/addinvoice.go @@ -729,7 +729,7 @@ func newHopHintInfo(c *channeldb.OpenChannel, isActive bool) *HopHintInfo { IsActive: isActive, FundingOutpoint: c.FundingOutpoint, RemotePubkey: c.IdentityPub, - RemoteBalance: c.LocalCommitment.RemoteBalance, + RemoteBalance: c.Commitments.Local.RemoteBalance, ShortChannelID: c.ShortChannelID.ToUint64(), ConfirmedScidZC: c.ZeroConfRealScid().ToUint64(), ScidAliasFeature: c.ChanType.HasScidAliasFeature(), @@ -844,8 +844,8 @@ func getPotentialHints(cfg *SelectHopHintsCfg) ([]*channeldb.OpenChannel, // Sort the channels in descending remote balance. compareRemoteBalance := func(i, j int) bool { - iBalance := privateChannels[i].LocalCommitment.RemoteBalance - jBalance := privateChannels[j].LocalCommitment.RemoteBalance + iBalance := privateChannels[i].Commitments.Local.RemoteBalance + jBalance := privateChannels[j].Commitments.Local.RemoteBalance return iBalance > jBalance } sort.Slice(privateChannels, compareRemoteBalance) diff --git a/lnrpc/invoicesrpc/addinvoice_test.go b/lnrpc/invoicesrpc/addinvoice_test.go index 76a529f8c67..723e31628f5 100644 --- a/lnrpc/invoicesrpc/addinvoice_test.go +++ b/lnrpc/invoicesrpc/addinvoice_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/zpay32" "github.com/stretchr/testify/mock" @@ -588,8 +589,10 @@ var populateHopHintsTestCases = []struct { remoteBalance := lnwire.MilliSatoshi(10_000_000) allChannels := []*channeldb.OpenChannel{ { - LocalCommitment: channeldb.ChannelCommitment{ - RemoteBalance: remoteBalance, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ //nolint:lll + Local: channeldb.ChannelCommitment{ + RemoteBalance: remoteBalance, + }, }, FundingOutpoint: fundingOutpoint, ShortChannelID: lnwire.NewShortChanIDFromInt(9), @@ -642,8 +645,10 @@ var populateHopHintsTestCases = []struct { // enough bandwidth we should never use this one. {}, { - LocalCommitment: channeldb.ChannelCommitment{ - RemoteBalance: remoteBalance, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ //nolint:lll + Local: channeldb.ChannelCommitment{ + RemoteBalance: remoteBalance, + }, }, FundingOutpoint: fundingOutpoint, ShortChannelID: lnwire.NewShortChanIDFromInt(9), @@ -840,16 +845,20 @@ func setupMockTwoChannels(h *hopHintsConfigMock) (lnwire.ChannelID, // After sorting we will first process chanID1 and then // chanID2. { - LocalCommitment: channeldb.ChannelCommitment{ - RemoteBalance: remoteBalance2, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: channeldb.ChannelCommitment{ + RemoteBalance: remoteBalance2, + }, }, FundingOutpoint: fundingOutpoint2, ShortChannelID: lnwire.NewShortChanIDFromInt(2), IdentityPub: getTestPubKey(), }, { - LocalCommitment: channeldb.ChannelCommitment{ - RemoteBalance: remoteBalance1, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: channeldb.ChannelCommitment{ + RemoteBalance: remoteBalance1, + }, }, FundingOutpoint: fundingOutpoint1, ShortChannelID: lnwire.NewShortChanIDFromInt(9), diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index 5ed33cdc8b9..2ff55cb3b8d 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -1185,13 +1185,13 @@ func (w *WalletKit) BumpForceCloseFee(_ context.Context, // bumping the fee. commitSet := fn.NewSet[chainhash.Hash]() - if channel.LocalCommitment.CommitTx != nil { - localTxID := channel.LocalCommitment.CommitTx.TxHash() + if channel.Commitments.Local.CommitTx != nil { + localTxID := channel.Commitments.Local.CommitTx.TxHash() commitSet.Add(localTxID) } - if channel.RemoteCommitment.CommitTx != nil { - remoteTxID := channel.RemoteCommitment.CommitTx.TxHash() + if channel.Commitments.Remote.CommitTx != nil { + remoteTxID := channel.Commitments.Remote.CommitTx.TxHash() commitSet.Add(remoteTxID) } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 71ea230bda7..3605ba42b74 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -879,8 +879,8 @@ func NewLightningChannel(signer input.Signer, optFunc(opts) } - localCommit := state.LocalCommitment - remoteCommit := state.RemoteCommitment + localCommit := state.Commitments.Local + remoteCommit := state.Commitments.Remote // First, initialize the update logs with their current counter values // from the local and remote commitments. @@ -5502,7 +5502,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( remoteChainTail, ) - remoteHTLCs := lc.channelState.RemoteCommitment.Htlcs + remoteHTLCs := lc.channelState.Commitments.Remote.Htlcs return fwdPkg, remoteHTLCs, nil } @@ -5623,11 +5623,11 @@ func (lc *LightningChannel) GetDustSum(whoseCommit lntypes.ChannelParty, var dustSum lnwire.MilliSatoshi dustLimit := lc.channelState.ChanCfgs.Local.DustLimit - commit := lc.channelState.LocalCommitment + commit := lc.channelState.Commitments.Local if whoseCommit.IsRemote() { // Calculate dust sum on the remote's commitment. dustLimit = lc.channelState.ChanCfgs.Remote.DustLimit - commit = lc.channelState.RemoteCommitment + commit = lc.channelState.Commitments.Remote } chanType := lc.channelState.ChanType @@ -6143,7 +6143,7 @@ func (lc *LightningChannel) AbsoluteThawHeight() (uint32, error) { func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { // Fetch the current commitment transaction, along with their signature // for the transaction. - localCommit := lc.channelState.LocalCommitment + localCommit := lc.channelState.Commitments.Local commitTx := localCommit.CommitTx.Copy() ourKey := lc.channelState.ChanCfgs.Local.MultiSigKey @@ -7306,7 +7306,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { return nil, err } - localCommitment := lc.channelState.LocalCommitment + localCommitment := lc.channelState.Commitments.Local summary, err := NewLocalForceCloseSummary( lc.channelState, lc.Signer, commitTx, localCommitment.CommitHeight, lc.leafStore, @@ -7351,7 +7351,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( NewAuxChanState(chanState), - chanState.LocalCommitment, *keyRing, + chanState.Commitments.Local, *keyRing, ) }, ).Unpack() @@ -7457,7 +7457,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, // outgoing HTLC's that we'll need to claim as well. If this is after // recovery there is not much we can do with HTLCs, so we'll always // use what we have in our latest state when extracting resolutions. - localCommit := chanState.LocalCommitment + localCommit := chanState.Commitments.Local htlcResolutions, err := extractHtlcResolutions( chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local, signer, localCommit.Htlcs, keyRing, &chanState.ChanCfgs.Local, @@ -7544,7 +7544,7 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, // during the channel closing process. ourBalance, theirBalance, err := CoopCloseBalance( lc.channelState.ChanType, lc.channelState.IsInitiator, - proposedFee, lc.channelState.LocalCommitment, + proposedFee, lc.channelState.Commitments.Local, ) if err != nil { return nil, nil, 0, err @@ -7628,7 +7628,7 @@ func (lc *LightningChannel) CompleteCooperativeClose( // Get the final balances after subtracting the proposed fee. ourBalance, theirBalance, err := CoopCloseBalance( lc.channelState.ChanType, lc.channelState.IsInitiator, - proposedFee, lc.channelState.LocalCommitment, + proposedFee, lc.channelState.Commitments.Local, ) if err != nil { return nil, 0, err @@ -7768,7 +7768,7 @@ func (lc *LightningChannel) NewAnchorResolutions() (*AnchorResolutions, &lc.channelState.ChanCfgs.Remote, ) localRes, err := NewAnchorResolution( - lc.channelState, lc.channelState.LocalCommitment.CommitTx, + lc.channelState, lc.channelState.Commitments.Local.CommitTx, localKeyRing, lntypes.Local, ) if err != nil { @@ -7783,7 +7783,7 @@ func (lc *LightningChannel) NewAnchorResolutions() (*AnchorResolutions, &lc.channelState.ChanCfgs.Remote, ) remoteRes, err := NewAnchorResolution( - lc.channelState, lc.channelState.RemoteCommitment.CommitTx, + lc.channelState, lc.channelState.Commitments.Remote.CommitTx, remoteKeyRing, lntypes.Remote, ) if err != nil { @@ -8428,7 +8428,7 @@ func (lc *LightningChannel) LocalBalanceDust() bool { defer lc.RUnlock() chanState := lc.channelState - localBalance := chanState.LocalCommitment.LocalBalance.ToSatoshis() + localBalance := chanState.Commitments.Local.LocalBalance.ToSatoshis() // If this is an anchor channel, and we're the initiator, then we'll // regain the stats allocated to the anchor outputs with the co-op @@ -8448,7 +8448,7 @@ func (lc *LightningChannel) RemoteBalanceDust() bool { defer lc.RUnlock() chanState := lc.channelState - remoteBalance := chanState.RemoteCommitment.RemoteBalance.ToSatoshis() + remoteBalance := chanState.Commitments.Remote.RemoteBalance.ToSatoshis() // If this is an anchor channel, and they're the initiator, then we'll // regain the stats allocated to the anchor outputs with the co-op @@ -8615,7 +8615,9 @@ func (lc *LightningChannel) CommitFeeRate() chainfee.SatPerKWeight { lc.RLock() defer lc.RUnlock() - return chainfee.SatPerKWeight(lc.channelState.LocalCommitment.FeePerKw) + return chainfee.SatPerKWeight( + lc.channelState.Commitments.Local.FeePerKw, + ) } // WorstCaseFeeRate returns the higher feerate from either the local commitment @@ -8624,8 +8626,8 @@ func (lc *LightningChannel) WorstCaseFeeRate() chainfee.SatPerKWeight { lc.RLock() defer lc.RUnlock() - localFeeRate := lc.channelState.LocalCommitment.FeePerKw - remoteFeeRate := lc.channelState.RemoteCommitment.FeePerKw + localFeeRate := lc.channelState.Commitments.Local.FeePerKw + remoteFeeRate := lc.channelState.Commitments.Remote.FeePerKw if localFeeRate > remoteFeeRate { return chainfee.SatPerKWeight(localFeeRate) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 6b567859085..a71c6cec32f 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -210,21 +210,21 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // an anchor output. numOutputs = 5 } - if len(aliceChanState.LocalCommitment.CommitTx.TxOut) != numOutputs { + if len(aliceChanState.Commitments.Local.CommitTx.TxOut) != numOutputs { t.Fatalf("alice should have three commitment outputs, instead "+ "have %v", - len(aliceChanState.LocalCommitment.CommitTx.TxOut)) + len(aliceChanState.Commitments.Local.CommitTx.TxOut)) } - if len(bobChanState.LocalCommitment.CommitTx.TxOut) != numOutputs { + if len(bobChanState.Commitments.Local.CommitTx.TxOut) != numOutputs { t.Fatalf("bob should have three commitment outputs, instead "+ "have %v", - len(bobChanState.LocalCommitment.CommitTx.TxOut)) + len(bobChanState.Commitments.Local.CommitTx.TxOut)) } assertOutputExistsByValue(t, - aliceChannel.channelState.LocalCommitment.CommitTx, + aliceChannel.channelState.Commitments.Local.CommitTx, htlcAmt.ToSatoshis()) assertOutputExistsByValue(t, - bobChannel.channelState.LocalCommitment.CommitTx, + bobChannel.channelState.Commitments.Local.CommitTx, htlcAmt.ToSatoshis()) // Now we'll repeat a similar exchange, this time with Bob settling the @@ -740,10 +740,10 @@ func testCoopClose(t *testing.T, testCase *coopCloseTestCase) { bobDeliveryScript := testHdSeed[:] aliceFeeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) bobFeeRate := chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) // We'll start with both Alice and Bob creating a new close proposal @@ -783,14 +783,14 @@ func testCoopClose(t *testing.T, testCase *coopCloseTestCase) { // Finally, make sure the final balances are correct from both's // perspective. - aliceBalance := aliceChannel.channelState.LocalCommitment. + aliceBalance := aliceChannel.channelState.Commitments.Local. LocalBalance.ToSatoshis() // The commit balance have had the initiator's (Alice) commitfee and // any anchors subtracted, so add that back to the final expected // balance. Alice also pays the coop close fee, so that must be // subtracted. - commitFee := aliceChannel.channelState.LocalCommitment.CommitFee + commitFee := aliceChannel.channelState.Commitments.Local.CommitFee expBalanceAlice := aliceBalance + commitFee + testCase.anchorAmt - bobFee if aliceTxBalance != expBalanceAlice { @@ -800,7 +800,7 @@ func testCoopClose(t *testing.T, testCase *coopCloseTestCase) { // Bob is not the initiator, so his final balance should simply be // equal to the latest commitment balance. - expBalanceBob := bobChannel.channelState.LocalCommitment. + expBalanceBob := bobChannel.channelState.Commitments.Local. LocalBalance.ToSatoshis() if bobTxBalance != expBalanceBob { t.Fatalf("expected bob's balance to be %v got %v", @@ -866,7 +866,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { ) require.NoError(t, err, "unable to create test channels") - bobAmount := bobChannel.channelState.LocalCommitment.LocalBalance + bobAmount := bobChannel.channelState.Commitments.Local.LocalBalance // First, we'll add an outgoing HTLC from Alice to Bob, such that it // will still be present within the broadcast commitment transaction. @@ -976,7 +976,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { totalCommitWeight := testCase.expectedCommitWeight + (input.HTLCWeight * 2) feePerKw := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) commitFee := feePerKw.FeeForWeight(totalCommitWeight) @@ -1079,7 +1079,8 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // Finally, the txid of the commitment transaction and the one returned // as the closing transaction should also match. closeTxHash := closeSummary.CloseTx.TxHash() - commitTxHash := aliceChannel.channelState.LocalCommitment.CommitTx.TxHash() + commitTxHash := + aliceChannel.channelState.Commitments.Local.CommitTx.TxHash() if !bytes.Equal(closeTxHash[:], commitTxHash[:]) { t.Fatalf("alice: incorrect close transaction txid") } @@ -1195,7 +1196,8 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { } closeTxHash = closeSummary.CloseTx.TxHash() - commitTxHash = bobChannel.channelState.LocalCommitment.CommitTx.TxHash() + commitTxHash = + bobChannel.channelState.Commitments.Local.CommitTx.TxHash() if !bytes.Equal(closeTxHash[:], commitTxHash[:]) { t.Fatalf("bob: incorrect close transaction txid") } @@ -1243,8 +1245,8 @@ func TestForceCloseDustOutput(t *testing.T) { htlcAmount := lnwire.NewMSatFromSatoshis(500) - aliceAmount := aliceChannel.channelState.LocalCommitment.LocalBalance - bobAmount := bobChannel.channelState.LocalCommitment.LocalBalance + aliceAmount := aliceChannel.channelState.Commitments.Local.LocalBalance + bobAmount := bobChannel.channelState.Commitments.Local.LocalBalance // Have Bobs' to-self output be below her dust limit and check // ForceCloseSummary again on both peers. @@ -1268,8 +1270,7 @@ func TestForceCloseDustOutput(t *testing.T) { t.Fatalf("Can't update the channel state: %v", err) } - aliceAmount = aliceChannel.channelState.LocalCommitment.LocalBalance - bobAmount = bobChannel.channelState.LocalCommitment.RemoteBalance + aliceAmount = aliceChannel.channelState.Commitments.Local.LocalBalance closeSummary, err := aliceChannel.ForceClose() require.NoError(t, err, "unable to force close channel") @@ -1290,7 +1291,7 @@ func TestForceCloseDustOutput(t *testing.T) { int64(aliceAmount.ToSatoshis()) { t.Fatalf("alice incorrect output value in SelfOutputSignDesc, "+ "expected %v, got %v", - aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis(), + aliceChannel.channelState.Commitments.Local.LocalBalance.ToSatoshis(), //nolint:lll commitResolution.SelfOutputSignDesc.Output.Value) } @@ -1304,7 +1305,8 @@ func TestForceCloseDustOutput(t *testing.T) { } closeTxHash := closeSummary.CloseTx.TxHash() - commitTxHash := aliceChannel.channelState.LocalCommitment.CommitTx.TxHash() + commitTxHash := + aliceChannel.channelState.Commitments.Local.CommitTx.TxHash() if !bytes.Equal(closeTxHash[:], commitTxHash[:]) { t.Fatalf("alice: incorrect close transaction txid") } @@ -1321,7 +1323,8 @@ func TestForceCloseDustOutput(t *testing.T) { } closeTxHash = closeSummary.CloseTx.TxHash() - commitTxHash = bobChannel.channelState.LocalCommitment.CommitTx.TxHash() + commitTxHash = + bobChannel.channelState.Commitments.Local.CommitTx.TxHash() if !bytes.Equal(closeTxHash[:], commitTxHash[:]) { t.Fatalf("bob: incorrect close transaction txid") } @@ -1341,7 +1344,8 @@ func TestDustHTLCFees(t *testing.T) { ) require.NoError(t, err, "unable to create test channels") - aliceStartingBalance := aliceChannel.channelState.LocalCommitment.LocalBalance + aliceStartingBalance := + aliceChannel.channelState.Commitments.Local.LocalBalance // This HTLC amount should be lower than the dust limits of both nodes. htlcAmount := lnwire.NewMSatFromSatoshis(100) @@ -1360,17 +1364,19 @@ func TestDustHTLCFees(t *testing.T) { // properly. Namely, the local+remote+commitfee values should add up to // the total capacity of the channel. This same should hold for both // sides. - totalSatoshisAlice := (aliceChannel.channelState.LocalCommitment.LocalBalance + - aliceChannel.channelState.LocalCommitment.RemoteBalance + - lnwire.NewMSatFromSatoshis(aliceChannel.channelState.LocalCommitment.CommitFee)) + aliceChanState := &aliceChannel.channelState.Commitments.Local + totalSatoshisAlice := aliceChanState.LocalBalance + + aliceChanState.RemoteBalance + + lnwire.NewMSatFromSatoshis(aliceChanState.CommitFee) if totalSatoshisAlice+htlcAmount != lnwire.NewMSatFromSatoshis(aliceChannel.Capacity) { t.Fatalf("alice's funds leaked: total satoshis are %v, but channel "+ "capacity is %v", int64(totalSatoshisAlice), int64(aliceChannel.Capacity)) } - totalSatoshisBob := (bobChannel.channelState.LocalCommitment.LocalBalance + - bobChannel.channelState.LocalCommitment.RemoteBalance + - lnwire.NewMSatFromSatoshis(bobChannel.channelState.LocalCommitment.CommitFee)) + bobChanState := bobChannel.channelState.Commitments.Local + totalSatoshisBob := bobChanState.LocalBalance + + bobChanState.RemoteBalance + + lnwire.NewMSatFromSatoshis(bobChanState.CommitFee) if totalSatoshisBob+htlcAmount != lnwire.NewMSatFromSatoshis(bobChannel.Capacity) { t.Fatalf("bob's funds leaked: total satoshis are %v, but channel "+ "capacity is %v", int64(totalSatoshisBob), @@ -1380,20 +1386,20 @@ func TestDustHTLCFees(t *testing.T) { // The commitment fee paid should be the same, as there have been no // new material outputs added. defaultFee := calcStaticFee(channeldb.SingleFunderTweaklessBit, 0) - if aliceChannel.channelState.LocalCommitment.CommitFee != defaultFee { + if aliceChanState.CommitFee != defaultFee { t.Fatalf("dust htlc amounts not subtracted from commitment fee "+ "expected %v, got %v", defaultFee, - aliceChannel.channelState.LocalCommitment.CommitFee) + aliceChanState.CommitFee) } - if bobChannel.channelState.LocalCommitment.CommitFee != defaultFee { + if bobChanState.CommitFee != defaultFee { t.Fatalf("dust htlc amounts not subtracted from commitment fee "+ "expected %v, got %v", defaultFee, - bobChannel.channelState.LocalCommitment.CommitFee) + bobChanState.CommitFee) } // Alice's final balance should reflect the HTLC deficit even though // the HTLC was paid to fees as it was trimmed. - aliceEndBalance := aliceChannel.channelState.LocalCommitment.LocalBalance + aliceEndBalance := aliceChanState.LocalBalance aliceExpectedBalance := aliceStartingBalance - htlcAmount if aliceEndBalance != aliceExpectedBalance { t.Fatalf("alice not credited for dust: expected %v, got %v", @@ -1422,7 +1428,7 @@ func TestHTLCDustLimit(t *testing.T) { htlcSat := (btcutil.Amount(500) + HtlcTimeoutFee( aliceChannel.channelState.ChanType, chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ), )) htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat) @@ -1451,10 +1457,10 @@ func TestHTLCDustLimit(t *testing.T) { 2, len(bobCommitment.txn.TxOut)) } defaultFee := calcStaticFee(channeldb.SingleFunderTweaklessBit, 0) - if bobChannel.channelState.LocalCommitment.CommitFee != defaultFee { + if bobChannel.channelState.Commitments.Local.CommitFee != defaultFee { t.Fatalf("dust htlc amount was subtracted from commitment fee "+ "expected %v, got %v", defaultFee, - bobChannel.channelState.LocalCommitment.CommitFee) + bobChannel.channelState.Commitments.Local.CommitFee) } // Settle HTLC and create a new commitment state. @@ -1667,12 +1673,12 @@ func TestChannelBalanceDustLimit(t *testing.T) { // once fees have been subtracted, but smaller than Bob's dust limit. // We account in fees for the HTLC we will be adding. defaultFee := calcStaticFee(channeldb.SingleFunderTweaklessBit, 1) - aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis() + aliceBalance := aliceChannel.channelState.Commitments.Local.LocalBalance.ToSatoshis() //nolint:lll htlcSat := aliceBalance - defaultFee htlcSat += HtlcSuccessFee( aliceChannel.channelState.ChanType, chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ), ) @@ -2089,11 +2095,12 @@ func TestCancelHTLC(t *testing.T) { // of the new HTLC. aliceExpectedBalance := btcutil.Amount(btcutil.SatoshiPerBitcoin*4) - calcStaticFee(channeldb.SingleFunderTweaklessBit, 1) - if aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis() != + aliceChanState := &aliceChannel.channelState.Commitments.Local + if aliceChanState.LocalBalance.ToSatoshis() != aliceExpectedBalance { t.Fatalf("Alice's balance is wrong: expected %v, got %v", aliceExpectedBalance, - aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis()) + aliceChanState.LocalBalance.ToSatoshis()) } // Now, with the HTLC committed on both sides, trigger a cancellation @@ -2134,32 +2141,33 @@ func TestCancelHTLC(t *testing.T) { expectedBalance := btcutil.Amount(btcutil.SatoshiPerBitcoin * 5) staticFee := calcStaticFee(channeldb.SingleFunderTweaklessBit, 0) - if aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis() != + if aliceChanState.LocalBalance.ToSatoshis() != expectedBalance-staticFee { t.Fatalf("balance is wrong: expected %v, got %v", - aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis(), + aliceChanState.LocalBalance.ToSatoshis(), expectedBalance-staticFee) } - if aliceChannel.channelState.LocalCommitment.RemoteBalance.ToSatoshis() != + if aliceChanState.RemoteBalance.ToSatoshis() != expectedBalance { t.Fatalf("balance is wrong: expected %v, got %v", - aliceChannel.channelState.LocalCommitment.RemoteBalance.ToSatoshis(), + aliceChanState.RemoteBalance.ToSatoshis(), expectedBalance) } - if bobChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis() != + bobChanState := bobChannel.channelState.Commitments.Local + if bobChanState.LocalBalance.ToSatoshis() != expectedBalance { t.Fatalf("balance is wrong: expected %v, got %v", - bobChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis(), + bobChanState.LocalBalance.ToSatoshis(), expectedBalance) } - if bobChannel.channelState.LocalCommitment.RemoteBalance.ToSatoshis() != + if bobChanState.RemoteBalance.ToSatoshis() != expectedBalance-staticFee { t.Fatalf("balance is wrong: expected %v, got %v", - bobChannel.channelState.LocalCommitment.RemoteBalance.ToSatoshis(), + bobChanState.RemoteBalance.ToSatoshis(), expectedBalance-staticFee) } } @@ -2176,10 +2184,10 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { require.NoError(t, err, "unable to create test channels") aliceFeeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) bobFeeRate := chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) setDustLimit := func(dustVal btcutil.Amount) { @@ -2195,10 +2203,12 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { } setBalances := func(aliceBalance, bobBalance lnwire.MilliSatoshi) { - aliceChannel.channelState.LocalCommitment.LocalBalance = aliceBalance - aliceChannel.channelState.LocalCommitment.RemoteBalance = bobBalance - bobChannel.channelState.LocalCommitment.LocalBalance = bobBalance - bobChannel.channelState.LocalCommitment.RemoteBalance = aliceBalance + aliceChanState := &aliceChannel.channelState.Commitments.Local + aliceChanState.LocalBalance = aliceBalance + aliceChanState.RemoteBalance = bobBalance + bobChanState := &bobChannel.channelState.Commitments.Local + bobChanState.LocalBalance = bobBalance + bobChanState.RemoteBalance = aliceBalance } aliceDeliveryScript := bobsPrivKey[:] @@ -2269,7 +2279,7 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { t.Fatalf("close tx has wrong number of outputs: expected %v "+ "got %v", 1, len(closeTx.TxOut)) } - commitFee := aliceChannel.channelState.LocalCommitment.CommitFee + commitFee := aliceChannel.channelState.Commitments.Local.CommitFee aliceExpectedBalance := aliceBal.ToSatoshis() - aliceFee + commitFee if closeTx.TxOut[0].Value != int64(aliceExpectedBalance) { t.Fatalf("alice's balance is incorrect: expected %v, got %v", @@ -2339,7 +2349,7 @@ func TestUpdateFeeAdjustments(t *testing.T) { // First, we'll grab the current base fee rate as we'll be using this // to make relative adjustments int he fee rate. - baseFeeRate := aliceChannel.channelState.LocalCommitment.FeePerKw + baseFeeRate := aliceChannel.channelState.Commitments.Local.FeePerKw // We'll first try to increase the fee rate 5x, this should be able to // be committed without any issue. @@ -2469,7 +2479,9 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs) require.NoError(t, err, "bob unable to process alice's new commitment") - if chainfee.SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee { + if chainfee.SatPerKWeight( + bobChannel.channelState.Commitments.Local.FeePerKw, + ) == fee { t.Fatalf("bob's feePerKw was unexpectedly locked in") } @@ -2478,7 +2490,9 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { _, _, _, err = bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to generate bob revocation") - if chainfee.SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee { + if chainfee.SatPerKWeight( + bobChannel.channelState.Commitments.Local.FeePerKw, + ) != fee { t.Fatalf("bob's feePerKw was not locked in") } } @@ -2533,7 +2547,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { require.NoError(t, err, "bob unable to process alice's new commitment") if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) == fee { t.Fatalf("bob's feePerKw was unexpectedly locked in") @@ -2545,7 +2559,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { require.NoError(t, err, "unable to generate bob revocation") if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) != fee { t.Fatalf("bob's feePerKw was not locked in") @@ -2568,7 +2582,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { require.NoError(t, err, "alice unable to process bob's new commitment") if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) == fee { t.Fatalf("alice's feePerKw was unexpectedly locked in") @@ -2580,7 +2594,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { require.NoError(t, err, "unable to revoke alice channel") if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) != fee { t.Fatalf("alice's feePerKw was not locked in") @@ -2662,7 +2676,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { require.NoError(t, err, "alice unable to process bob's new commitment") if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) == fee { t.Fatalf("bob's feePerKw was unexpectedly locked in") @@ -2675,7 +2689,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { require.NoError(t, err, "unable to revoke alice channel") if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) != fee { t.Fatalf("bob's feePerKw was not locked in") @@ -2697,7 +2711,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { require.NoError(t, err, "alice unable to process bob's new commitment") if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) == fee { t.Fatalf("alice's feePerKw was unexpectedly locked in") @@ -2709,7 +2723,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { require.NoError(t, err, "unable to generate bob revocation") if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) != fee { t.Fatalf("Alice's feePerKw was not locked in") @@ -2787,7 +2801,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { require.NoError(t, err, "bob unable to process alice's new commitment") if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) == fee { t.Fatalf("bob's feePerKw was unexpectedly locked in") @@ -2811,7 +2825,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { require.NoError(t, err, "unable to generate bob revocation") if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) != fee { t.Fatalf("bob's feePerKw was not locked in") @@ -2836,7 +2850,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { } if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) == fee { t.Fatalf("alice's feePerKw was unexpectedly locked in") @@ -2848,7 +2862,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { require.NoError(t, err, "unable to revoke alice channel") if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) != fee { t.Fatalf("alice's feePerKw was not locked in") @@ -4457,7 +4471,7 @@ func TestFeeUpdateRejectInsaneFee(t *testing.T) { // Next, we'll try to add a fee rate to Alice which is 1,000,000x her // starting fee rate. startingFeeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) newFeeRate := startingFeeRate * 1000000 @@ -4484,7 +4498,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // First, we'll fetch the current fee rate present within the // commitment transactions. startingFeeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) // Next, we'll start a commitment update, with Alice sending a new @@ -4598,13 +4612,13 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // Both parties should now have the latest fee rate locked-in. if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) != newFeeRate { t.Fatalf("alice's feePerKw was not locked in") } if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) != newFeeRate { t.Fatalf("bob's feePerKw was not locked in") @@ -4687,7 +4701,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // First, we'll fetch the current fee rate present within the // commitment transactions. startingFeeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) newFeeRate := startingFeeRate @@ -4796,13 +4810,13 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // Both parties should now have the latest fee rate locked-in. if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) != newFeeRate { t.Fatalf("alice's feePerKw was not locked in") } if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) != newFeeRate { t.Fatalf("bob's feePerKw was not locked in") @@ -4827,13 +4841,13 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // ...and the final fee rate locked in. if chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) != newFeeRate { t.Fatalf("alice's feePerKw was not locked in") } if chainfee.SatPerKWeight( - bobChannel.channelState.LocalCommitment.FeePerKw, + bobChannel.channelState.Commitments.Local.FeePerKw, ) != newFeeRate { t.Fatalf("bob's feePerKw was not locked in") @@ -4977,7 +4991,7 @@ func TestChanAvailableBandwidth(t *testing.T) { aliceChannel.channelState.ChanCfgs.Local.ChanReserve, ) feeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) assertBandwidthEstimateCorrect := func(aliceInitiate bool, @@ -5130,7 +5144,7 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) { aliceChannel.channelState.ChanCfgs.Local.DustLimit, ) feeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) // When calculating the fee buffer sending an htlc we need to account @@ -5143,7 +5157,7 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) { feeRate.FeeForWeight(input.HTLCWeight), ) commitFee := lnwire.NewMSatFromSatoshis( - aliceChannel.channelState.LocalCommitment.CommitFee, + aliceChannel.channelState.Commitments.Local.CommitFee, ) htlcTimeoutFee := lnwire.NewMSatFromSatoshis( HtlcTimeoutFee(aliceChannel.channelState.ChanType, feeRate), @@ -5314,7 +5328,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { ) feeRate := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) htlcTimeoutFee := lnwire.NewMSatFromSatoshis( HtlcTimeoutFee(aliceChannel.channelState.ChanType, feeRate), @@ -5400,7 +5414,8 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // In addition, we expect this weight to result in the fee we currently // see being paid on the remote commitent. calcFee := feeRate.FeeForWeight(weight2) - remoteCommitFee := aliceChannel.channelState.RemoteCommitment.CommitFee + remoteCommitFee := + aliceChannel.channelState.Commitments.Remote.CommitFee require.Equal(t, calcFee, remoteCommitFee) // Settle the HTLC, bringing commitment weight back to base. @@ -5422,7 +5437,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // Ensure the current remote commit has the expected commitfee. calcFee = feeRate.FeeForWeight(weight2) - remoteCommitFee = bobChannel.channelState.RemoteCommitment.CommitFee + remoteCommitFee = bobChannel.channelState.Commitments.Remote.CommitFee require.Equal(t, calcFee, remoteCommitFee) settleHtlc(preimg) @@ -5874,7 +5889,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { aliceCloseSummary, err := NewUnilateralCloseSummary( aliceChannel.channelState, aliceChannel.Signer, spendDetail, - aliceChannel.channelState.RemoteCommitment, + aliceChannel.channelState.Commitments.Remote, aliceChannel.channelState.RemoteCurrentRevocation, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) @@ -6024,7 +6039,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { aliceWrongCloseSummary, err := NewUnilateralCloseSummary( aliceChannel.channelState, aliceChannel.Signer, spendDetail, - aliceChannel.channelState.RemoteCommitment, + aliceChannel.channelState.Commitments.Remote, aliceChannel.channelState.RemoteCurrentRevocation, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) @@ -6453,8 +6468,10 @@ func assertChannelBalances(t *testing.T, alice, bob *LightningChannel, _, _, line, _ := runtime.Caller(1) - aliceSelfBalance := alice.channelState.LocalCommitment.LocalBalance.ToSatoshis() - aliceBobBalance := alice.channelState.LocalCommitment.RemoteBalance.ToSatoshis() + aliceSelfBalance := + alice.channelState.Commitments.Local.LocalBalance.ToSatoshis() + aliceBobBalance := + alice.channelState.Commitments.Local.RemoteBalance.ToSatoshis() if aliceSelfBalance != aliceBalance { t.Fatalf("line #%v: wrong alice self balance: expected %v, got %v", line, aliceBalance, aliceSelfBalance) @@ -6464,8 +6481,10 @@ func assertChannelBalances(t *testing.T, alice, bob *LightningChannel, line, bobBalance, aliceBobBalance) } - bobSelfBalance := bob.channelState.LocalCommitment.LocalBalance.ToSatoshis() - bobAliceBalance := bob.channelState.LocalCommitment.RemoteBalance.ToSatoshis() + bobSelfBalance := + bob.channelState.Commitments.Local.LocalBalance.ToSatoshis() + bobAliceBalance := + bob.channelState.Commitments.Local.RemoteBalance.ToSatoshis() if bobSelfBalance != bobBalance { t.Fatalf("line #%v: wrong bob self balance: expected %v, got %v", line, bobBalance, bobSelfBalance) @@ -6549,7 +6568,7 @@ func TestChanReserve(t *testing.T) { t.Fatalf("unable to complete state update: %v", err) } - commitFee := aliceChannel.channelState.LocalCommitment.CommitFee + commitFee := aliceChannel.channelState.Commitments.Local.CommitFee assertChannelBalances( t, aliceChannel, bobChannel, btcutil.SatoshiPerBitcoin*4.5-commitFee, btcutil.SatoshiPerBitcoin*5, @@ -6631,7 +6650,7 @@ func TestChanReserve(t *testing.T) { t.Fatalf("unable to complete state update: %v", err) } - commitFee = aliceChannel.channelState.LocalCommitment.CommitFee + commitFee = aliceChannel.channelState.Commitments.Local.CommitFee assertChannelBalances( t, aliceChannel, bobChannel, btcutil.SatoshiPerBitcoin*3-commitFee, btcutil.SatoshiPerBitcoin*5, @@ -6647,7 +6666,7 @@ func TestChanReserve(t *testing.T) { t.Fatalf("unable to complete state update: %v", err) } - commitFee = aliceChannel.channelState.LocalCommitment.CommitFee + commitFee = aliceChannel.channelState.Commitments.Local.CommitFee assertChannelBalances( t, aliceChannel, bobChannel, btcutil.SatoshiPerBitcoin*3-commitFee, btcutil.SatoshiPerBitcoin*7, @@ -6671,7 +6690,7 @@ func TestChanReserve(t *testing.T) { t.Fatalf("unable to complete state update: %v", err) } - commitFee = aliceChannel.channelState.LocalCommitment.CommitFee + commitFee = aliceChannel.channelState.Commitments.Local.CommitFee assertChannelBalances( t, aliceChannel, bobChannel, btcutil.SatoshiPerBitcoin*3-commitFee, btcutil.SatoshiPerBitcoin*6, @@ -6697,7 +6716,7 @@ func TestChanReserveRemoteInitiator(t *testing.T) { // to add any more HTLCs to the commitment. Although a reserve this // high is unrealistic, a channel can easily get into a situation // where the initiator cannot pay for the fee of any more HTLCs. - commitFee := aliceChannel.channelState.LocalCommitment.CommitFee + commitFee := aliceChannel.channelState.Commitments.Local.CommitFee aliceMinReserve := 5*btcutil.SatoshiPerBitcoin - commitFee aliceChannel.channelState.ChanCfgs.Local.ChanReserve = aliceMinReserve @@ -6745,14 +6764,14 @@ func TestChanReserveLocalInitiatorDustHtlc(t *testing.T) { htlcSat := btcutil.Amount(500) + HtlcTimeoutFee( aliceChannel.channelState.ChanType, chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ), ) // Set Alice's channel reserve to be low enough to carry the value of // the HTLC, but not low enough to allow the extra fee from adding the // HTLC to the commitment. - commitFee := aliceChannel.channelState.LocalCommitment.CommitFee + commitFee := aliceChannel.channelState.Commitments.Local.CommitFee aliceMinReserve := 5*btcutil.SatoshiPerBitcoin - commitFee - htlcSat aliceChannel.channelState.ChanCfgs.Local.ChanReserve = aliceMinReserve @@ -6906,7 +6925,8 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { // At this point, we'll capture the current state number, as well as // the current commitment. - revokedStateNum := aliceChannel.channelState.LocalCommitment.CommitHeight + revokedStateNum := + aliceChannel.channelState.Commitments.Local.CommitHeight // We'll now have Bob settle those HTLC's to Alice and then advance // forward to a new state. @@ -6926,7 +6946,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { // At this point, we'll now simulate a contract breach by Bob using the // NewBreachRetribution method. - breachTx := aliceChannel.channelState.RemoteCommitment.CommitTx + breachTx := aliceChannel.channelState.Commitments.Remote.CommitTx breachRet, err := NewBreachRetribution( aliceChannel.channelState, revokedStateNum, 100, breachTx, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), @@ -7832,7 +7852,7 @@ func TestChannelMaxFeeRate(t *testing.T) { ) currentFeeRate := chainfee.SatPerKWeight( - tc.channel.channelState.LocalCommitment.FeePerKw, + tc.channel.channelState.Commitments.Local.FeePerKw, ) // When the fee allocation would push our max fee rate below our @@ -8787,8 +8807,7 @@ func TestEvaluateView(t *testing.T) { FeePerKw: feePerKw, } - var ( - ) + var () // Evaluate the htlc view, mutate as test expects. // We do not check the balance deltas in this test @@ -9613,7 +9632,7 @@ func deriveDummyRetributionParams(chanState *channeldb.OpenChannel) (uint32, *CommitmentKeyRing, chainhash.Hash) { config := chanState.ChanCfgs.Remote - commitHash := chanState.RemoteCommitment.CommitTx.TxHash() + commitHash := chanState.Commitments.Remote.CommitTx.TxHash() keyRing := DeriveCommitmentKeys( config.RevocationBasePoint.PubKey, lntypes.Remote, chanState.ChanType, &chanState.ChanCfgs.Local, @@ -9903,7 +9922,7 @@ func TestCreateBreachRetributionLegacy(t *testing.T) { ) // Use the remote commitment as our revocation log. - revokedLog := aliceChannel.channelState.RemoteCommitment + revokedLog := aliceChannel.channelState.Commitments.Remote ourOp := revokedLog.CommitTx.TxOut[0] theirOp := revokedLog.CommitTx.TxOut[1] @@ -9974,7 +9993,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { stateNum := uint64(0) chainHash := aliceChannel.channelState.ChainHash theirDelay := uint32(aliceChannel.channelState.ChanCfgs.Remote.CsvDelay) - breachTx := aliceChannel.channelState.RemoteCommitment.CommitTx + breachTx := aliceChannel.channelState.Commitments.Remote.CommitTx // Create a breach retribution at height 0, which should give us an // error as there are no past delta state saved as revocation logs yet. @@ -9994,7 +10013,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // We now force a state transition which will give us a revocation log // at height 0. - txid := aliceChannel.channelState.RemoteCommitment.CommitTx.TxHash() + txid := aliceChannel.channelState.Commitments.Remote.CommitTx.TxHash() err = ForceStateTransition(aliceChannel, bobChannel) require.NoError(t, err) @@ -10326,7 +10345,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { // Static fee rate of 6000 sats/kw. feePerKw := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) additionalHtlc := feePerKw.FeeForWeight(input.HTLCWeight) @@ -10459,7 +10478,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { // Static fee rate of 6000 sats/kw. feePerKw := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) // Calculate the fee buffer for the current commitment tx including @@ -10597,7 +10616,8 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { // All of alice's balance is used up in fees and htlcs so the local // balance equals exactly the local reserve. - require.Equal(t, aliceChannel.channelState.LocalCommitment.LocalBalance, + require.Equal(t, + aliceChannel.channelState.Commitments.Local.LocalBalance, lnwire.NewMSatFromSatoshis(aliceReserve)) } @@ -10654,7 +10674,7 @@ func TestEnforceFeeBuffer(t *testing.T) { // Static fee rate of 6000 sats/kw. feePerKw := chainfee.SatPerKWeight( - aliceChannel.channelState.LocalCommitment.FeePerKw, + aliceChannel.channelState.Commitments.Local.FeePerKw, ) // Commitment Fee of the channel state (with 1 pending htlc). @@ -10741,7 +10761,7 @@ func TestEnforceFeeBuffer(t *testing.T) { feeHTLCMsat := lnwire.NewMSatFromSatoshis(feeHTLC) aliceBalance := aliceReserveMsat + bufferAmt - 2*feeHTLCMsat - expectedAmt := aliceChannel.channelState.LocalCommitment.LocalBalance + expectedAmt := aliceChannel.channelState.Commitments.Local.LocalBalance require.Equal(t, aliceBalance, expectedAmt) } diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 4b7824853aa..dcea1526799 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -1283,7 +1283,7 @@ func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash, ourIndex := uint32(channeldb.OutputIndexEmpty) theirIndex := uint32(channeldb.OutputIndexEmpty) - chanCommit := chanState.RemoteCommitment + chanCommit := chanState.Commitments.Remote _, commitmentPoint := btcec.PrivKeyFromBytes(revocationPreimage[:]) // With the commitment point generated, we can now derive the king ring diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 1f0000e8e47..e550f55cd87 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -435,17 +435,23 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, IsInitiator: initiator, ChannelFlags: req.Flags, Capacity: capacity, - LocalCommitment: channeldb.ChannelCommitment{ - LocalBalance: ourBalance, - RemoteBalance: theirBalance, - FeePerKw: btcutil.Amount(req.CommitFeePerKw), - CommitFee: commitFee, - }, - RemoteCommitment: channeldb.ChannelCommitment{ - LocalBalance: ourBalance, - RemoteBalance: theirBalance, - FeePerKw: btcutil.Amount(req.CommitFeePerKw), - CommitFee: commitFee, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: channeldb.ChannelCommitment{ + LocalBalance: ourBalance, + RemoteBalance: theirBalance, + FeePerKw: btcutil.Amount( + req.CommitFeePerKw, + ), + CommitFee: commitFee, + }, + Remote: channeldb.ChannelCommitment{ + LocalBalance: ourBalance, + RemoteBalance: theirBalance, + FeePerKw: btcutil.Amount( + req.CommitFeePerKw, + ), + CommitFee: commitFee, + }, }, ThawHeight: thawHeight, Db: wallet.Cfg.Database, diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index da8493e3108..1d93f755154 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -327,11 +327,13 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, RemoteCurrentRevocation: bobCommitPoint, RevocationProducer: alicePreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: aliceLocalCommit, - RemoteCommitment: aliceRemoteCommit, - Db: dbAlice.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: testTx, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: aliceLocalCommit, + Remote: aliceRemoteCommit, + }, + Db: dbAlice.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: testTx, } bobChannelState := &channeldb.OpenChannel{ ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ @@ -347,10 +349,12 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, RemoteCurrentRevocation: aliceCommitPoint, RevocationProducer: bobPreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: bobLocalCommit, - RemoteCommitment: bobRemoteCommit, - Db: dbBob.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: bobLocalCommit, + Remote: bobRemoteCommit, + }, + Db: dbBob.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), } // If the channel type has a tapscript root, then we'll also specify diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 58cb227cd45..cd2aaf9fd22 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -984,11 +984,13 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp RemoteCurrentRevocation: localCommitPoint, RevocationProducer: remotePreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: remoteCommit, - RemoteCommitment: remoteCommit, - Db: dbRemote.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: tc.fundingTx.MsgTx(), + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: remoteCommit, + Remote: remoteCommit, + }, + Db: dbRemote.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: tc.fundingTx.MsgTx(), } localChannelState := &channeldb.OpenChannel{ ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ @@ -1004,11 +1006,13 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp RemoteCurrentRevocation: remoteCommitPoint, RevocationProducer: localPreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: localCommit, - RemoteCommitment: localCommit, - Db: dbLocal.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: tc.fundingTx.MsgTx(), + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: localCommit, + Remote: localCommit, + }, + Db: dbLocal.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: tc.fundingTx.MsgTx(), } // Create mock signers that can sign for the keys that are used. diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 2c79ce5b9c1..1812f887121 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -1865,8 +1865,9 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { } // With the funding tx complete, create both commitment transactions. - localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis() - remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis() + localCommitment := pendingReservation.partialState.Commitments.Local + localBalance := localCommitment.LocalBalance.ToSatoshis() + remoteBalance := localCommitment.RemoteBalance.ToSatoshis() var leaseExpiry uint32 if pendingReservation.partialState.ChanType.HasLeaseExpiration() { leaseExpiry = pendingReservation.partialState.ThawHeight @@ -1928,8 +1929,8 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { // Record newly available information within the open channel state. chanState.FundingOutpoint = chanPoint - chanState.LocalCommitment.CommitTx = ourCommitTx - chanState.RemoteCommitment.CommitTx = theirCommitTx + chanState.Commitments.Local.CommitTx = ourCommitTx + chanState.Commitments.Remote.CommitTx = theirCommitTx // Next, we'll obtain the funding witness script, and the funding // output itself so we can generate a valid signature for the remote @@ -2203,7 +2204,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // At this point, we can also record and verify their signature for our // commitment transaction. res.theirCommitmentSig = msg.theirCommitmentSig - commitTx := res.partialState.LocalCommitment.CommitTx + commitTx := res.partialState.Commitments.Local.CommitTx err := l.verifyCommitSig(res, msg.theirCommitmentSig, commitTx) if err != nil { @@ -2214,7 +2215,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs } theirCommitSigBytes := msg.theirCommitmentSig.Serialize() - res.partialState.LocalCommitment.CommitSig = theirCommitSigBytes + res.partialState.Commitments.Local.CommitSig = theirCommitSigBytes // Funding complete, this entry can be removed from limbo. l.limboMtx.Lock() @@ -2298,8 +2299,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // Now that we have the funding outpoint, we can generate both versions // of the commitment transaction, and generate a signature for the // remote node's commitment transactions. - localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis() - remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis() + localBalance := pendingReservation.partialState.Commitments. + Local.LocalBalance.ToSatoshis() + remoteBalance := pendingReservation.partialState.Commitments. + Local.RemoteBalance.ToSatoshis() var leaseExpiry uint32 if pendingReservation.partialState.ChanType.HasLeaseExpiration() { leaseExpiry = pendingReservation.partialState.ThawHeight @@ -2338,8 +2341,8 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // without further synchronization. txsort.InPlaceSort(ourCommitTx) txsort.InPlaceSort(theirCommitTx) - chanState.LocalCommitment.CommitTx = ourCommitTx - chanState.RemoteCommitment.CommitTx = theirCommitTx + chanState.Commitments.Local.CommitTx = ourCommitTx + chanState.Commitments.Remote.CommitTx = theirCommitTx walletLog.Debugf("Local commit tx for ChannelPoint(%v): %v", req.fundingOutpoint, spew.Sdump(ourCommitTx)) @@ -2358,7 +2361,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { } theirCommitSigBytes := req.theirCommitmentSig.Serialize() - chanState.LocalCommitment.CommitSig = theirCommitSigBytes + chanState.Commitments.Local.CommitSig = theirCommitSigBytes channelValue := int64(pendingReservation.partialState.Capacity) theirKey := pendingReservation.theirContribution.MultiSigKey diff --git a/peer/test_utils.go b/peer/test_utils.go index e35a2745780..8a5d7e4bf55 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -257,11 +257,13 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, RemoteCurrentRevocation: bobCommitPoint, RevocationProducer: alicePreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: aliceCommit, - RemoteCommitment: aliceCommit, - Db: dbAlice.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: channels.TestFundingTx, + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: aliceCommit, + Remote: aliceCommit, + }, + Db: dbAlice.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: channels.TestFundingTx, } bobChannelState := &channeldb.OpenChannel{ ChanCfgs: lntypes.Dual[channeldb.ChannelConfig]{ @@ -276,10 +278,12 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, RemoteCurrentRevocation: aliceCommitPoint, RevocationProducer: bobPreimageProducer, RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: bobCommit, - RemoteCommitment: bobCommit, - Db: dbBob.ChannelStateDB(), - Packager: channeldb.NewChannelPackager(shortChanID), + Commitments: lntypes.Dual[channeldb.ChannelCommitment]{ + Local: bobCommit, + Remote: bobCommit, + }, + Db: dbBob.ChannelStateDB(), + Packager: channeldb.NewChannelPackager(shortChanID), } // Set custom values on the channel states. diff --git a/pilot.go b/pilot.go index 2a37b080d08..a203f57280e 100644 --- a/pilot.go +++ b/pilot.go @@ -265,7 +265,7 @@ func initAutoPilot(svr *server, cfg *lncfg.AutoPilot, chanState := make([]autopilot.LocalChannel, len(activeChannels)) for i, channel := range activeChannels { - localCommit := channel.LocalCommitment + localCommit := channel.Commitments.Local balance := localCommit.LocalBalance.ToSatoshis() chanState[i] = autopilot.LocalChannel{ @@ -287,7 +287,7 @@ func initAutoPilot(svr *server, cfg *lncfg.AutoPilot, return nil, err } - localCommit := channel.LocalCommitment + localCommit := channel.Commitments.Local return &autopilot.LocalChannel{ ChanID: channel.ShortChanID(), Balance: localCommit.LocalBalance.ToSatoshis(), diff --git a/rpcserver.go b/rpcserver.go index a400b191b2e..6c1fc2cd71c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3537,7 +3537,7 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, } for _, channel := range openChannels { - c := channel.LocalCommitment + c := channel.Commitments.Local localBalance += c.LocalBalance remoteBalance += c.RemoteBalance @@ -3557,7 +3557,7 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, } for _, channel := range pendingChannels { - c := channel.LocalCommitment + c := channel.Commitments.Local pendingOpenLocalBalance += c.LocalBalance pendingOpenRemoteBalance += c.RemoteBalance } @@ -3643,7 +3643,7 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) { witnessWeight = input.WitnessCommitmentTxWeight } - localCommitment := pendingChan.LocalCommitment + localCommitment := pendingChan.Commitments.Local utx := btcutil.NewTx(localCommitment.CommitTx) commitBaseWeight := blockchain.GetTransactionWeight(utx) commitWeight := commitBaseWeight + witnessWeight @@ -3656,11 +3656,14 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) { result[i] = &lnrpc.PendingChannelsResponse_PendingOpenChannel{ Channel: &lnrpc.PendingChannelsResponse_PendingChannel{ - RemoteNodePub: hex.EncodeToString(pub), - ChannelPoint: pendingChan.FundingOutpoint.String(), - Capacity: int64(pendingChan.Capacity), - LocalBalance: int64(localCommitment.LocalBalance.ToSatoshis()), - RemoteBalance: int64(localCommitment.RemoteBalance.ToSatoshis()), + RemoteNodePub: hex.EncodeToString(pub), + //nolint:lll + ChannelPoint: pendingChan.FundingOutpoint.String(), + Capacity: int64(pendingChan.Capacity), + //nolint:lll + LocalBalance: int64(localCommitment.LocalBalance.ToSatoshis()), + //nolint:lll + RemoteBalance: int64(localCommitment.RemoteBalance.ToSatoshis()), //nolint:lll LocalChanReserveSat: int64(pendingChan.ChanCfgs.Local.ChanReserve), //nolint:lll @@ -3753,9 +3756,8 @@ func (r *rpcServer) fetchPendingForceCloseChannels() (pendingForceClose, } channel.NumForwardingPackages = int64(len(fwdPkgs)) - channel.RemoteBalance = int64( - historical.LocalCommitment.RemoteBalance.ToSatoshis(), - ) + //nolint:lll + channel.RemoteBalance = int64(historical.Commitments.Local.RemoteBalance.ToSatoshis()) channel.Private = isPrivate(historical) channel.Memo = string(historical.Memo) @@ -3886,24 +3888,25 @@ func (r *rpcServer) fetchWaitingCloseChannels( var commitments lnrpc.PendingChannelsResponse_Commitments // Report local commit. May not be present when DLP is active. - if waitingClose.LocalCommitment.CommitTx != nil { + if waitingClose.Commitments.Local.CommitTx != nil { commitments.LocalTxid = - waitingClose.LocalCommitment.CommitTx.TxHash(). - String() + waitingClose.Commitments.Local.CommitTx. + TxHash().String() commitments.LocalCommitFeeSat = uint64( - waitingClose.LocalCommitment.CommitFee, + waitingClose.Commitments.Local.CommitFee, ) } // Report remote commit. May not be present when DLP is active. - if waitingClose.RemoteCommitment.CommitTx != nil { + if waitingClose.Commitments.Remote.CommitTx != nil { commitments.RemoteTxid = - waitingClose.RemoteCommitment.CommitTx.TxHash(). - String() + waitingClose.Commitments.Remote.CommitTx. + TxHash().String() commitments.RemoteCommitFeeSat = uint64( - waitingClose.RemoteCommitment.CommitFee, + waitingClose.Commitments.Remote. + CommitFee, ) } @@ -3948,11 +3951,13 @@ func (r *rpcServer) fetchWaitingCloseChannels( } channel := &lnrpc.PendingChannelsResponse_PendingChannel{ - RemoteNodePub: hex.EncodeToString(pub), - ChannelPoint: chanPoint.String(), - Capacity: int64(waitingClose.Capacity), - LocalBalance: int64(waitingClose.LocalCommitment.LocalBalance.ToSatoshis()), - RemoteBalance: int64(waitingClose.LocalCommitment.RemoteBalance.ToSatoshis()), + RemoteNodePub: hex.EncodeToString(pub), + ChannelPoint: chanPoint.String(), + Capacity: int64(waitingClose.Capacity), + //nolint:lll + LocalBalance: int64(waitingClose.Commitments.Local.LocalBalance.ToSatoshis()), + //nolint:lll + RemoteBalance: int64(waitingClose.Commitments.Local.RemoteBalance.ToSatoshis()), //nolint:lll LocalChanReserveSat: int64(waitingClose.ChanCfgs.Local.ChanReserve), //nolint:lll @@ -4422,7 +4427,7 @@ func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel, witnessWeight = input.WitnessCommitmentTxWeight } - localCommit := dbChannel.LocalCommitment + localCommit := dbChannel.Commitments.Local utx := btcutil.NewTx(localCommit.CommitTx) commitBaseWeight := blockchain.GetTransactionWeight(utx) commitWeight := commitBaseWeight + witnessWeight From f75c1cddc0bced86d3f23a886e2d8da4d5cddee6 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 21 Aug 2024 11:07:31 -0600 Subject: [PATCH 19/22] channeldb+contractcourt: tidy type signature for LatestCommitments --- channeldb/channel.go | 8 +++++--- contractcourt/chain_watcher.go | 16 ++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index d775e3abf2d..b85a8a52ac9 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -4012,7 +4012,9 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot { // remote party. These commitments are read from disk to ensure that only the // latest fully committed state is returned. The first commitment returned is // the local commitment, and the second returned is the remote commitment. -func (c *OpenChannel) LatestCommitments() (*ChannelCommitment, *ChannelCommitment, error) { +// +//nolint:lll +func (c *OpenChannel) LatestCommitments() fn.Result[*lntypes.Dual[ChannelCommitment]] { err := kvdb.View(c.Db.backend, func(tx kvdb.RTx) error { chanBucket, err := fetchChanBucket( tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash, @@ -4024,10 +4026,10 @@ func (c *OpenChannel) LatestCommitments() (*ChannelCommitment, *ChannelCommitmen return fetchChanCommitments(chanBucket, c) }, func() {}) if err != nil { - return nil, nil, err + return fn.Err[*lntypes.Dual[ChannelCommitment]](err) } - return &c.Commitments.Local, &c.Commitments.Remote, nil + return fn.Ok(&c.Commitments) } // RemoteRevocationStore returns the most up to date commitment version of the diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index bf96e684b70..6d8c41906a8 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -550,7 +550,7 @@ type chainSet struct { func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { // First, we'll grab the current unrevoked commitments for ourselves // and the remote party. - localCommit, remoteCommit, err := chanState.LatestCommitments() + commitments, err := chanState.LatestCommitments().Unpack() if err != nil { return nil, fmt.Errorf("unable to fetch channel state for "+ "chan_point=%v", chanState.FundingOutpoint) @@ -558,14 +558,14 @@ func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { log.Tracef("ChannelPoint(%v): local_commit_type=%v, local_commit=%v", chanState.FundingOutpoint, chanState.ChanType, - spew.Sdump(localCommit)) + spew.Sdump(commitments.Local)) log.Tracef("ChannelPoint(%v): remote_commit_type=%v, remote_commit=%v", chanState.FundingOutpoint, chanState.ChanType, - spew.Sdump(remoteCommit)) + spew.Sdump(commitments.Remote)) // Fetch the current known commit height for the remote party, and // their pending commitment chain tip if it exists. - remoteStateNum := remoteCommit.CommitHeight + remoteStateNum := commitments.Remote.CommitHeight remoteChainTip, err := chanState.RemoteCommitChainTip() if err != nil && err != channeldb.ErrNoPendingCommit { return nil, fmt.Errorf("unable to obtain chain tip for "+ @@ -578,8 +578,8 @@ func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { // duty. commitSet := CommitSet{ HtlcSets: map[HtlcSetKey][]channeldb.HTLC{ - LocalHtlcSet: localCommit.Htlcs, - RemoteHtlcSet: remoteCommit.Htlcs, + LocalHtlcSet: commitments.Local.Htlcs, + RemoteHtlcSet: commitments.Remote.Htlcs, }, } @@ -609,8 +609,8 @@ func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { return &chainSet{ remoteStateNum: remoteStateNum, commitSet: commitSet, - localCommit: *localCommit, - remoteCommit: *remoteCommit, + localCommit: commitments.Local, + remoteCommit: commitments.Remote, remotePendingCommit: remotePendingCommit, }, nil } From fa4c8365b32222bd21a230ba79e41bec42acefbe Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 21 Aug 2024 11:42:25 -0600 Subject: [PATCH 20/22] channeldb: add ChannelEpochs component for historical params --- channeldb/channel_epochs.go | 387 ++++++++++++++++++++++++++++++++++++ lntypes/channel_party.go | 24 +++ 2 files changed, 411 insertions(+) create mode 100644 channeldb/channel_epochs.go diff --git a/channeldb/channel_epochs.go b/channeldb/channel_epochs.go new file mode 100644 index 00000000000..8c35880bcf7 --- /dev/null +++ b/channeldb/channel_epochs.go @@ -0,0 +1,387 @@ +package channeldb + +import ( + "io" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/tlv" +) + +// CommitChainEpoch refers to a single period of time where a particular set +// of commitment parameters are in effect. +type CommitChainEpoch struct { + // LastHeight is the last commit height that marks the end of this + // epoch. + LastHeight uint64 + + // normalizedParams are the commitment parameters that affect the + // rendering of the commitment transaction. They are referred to as + // "normalized" because they indicate the parameters that actually + // apply to the party in question, irrespective of which party specified + // the parameter. + normalizedParams CommitmentParams +} + +// encode encodes a CommitChainEpoch to a writer. +func (c CommitChainEpoch) encode(w io.Writer) error { + return WriteElements( + w, c.LastHeight, c.normalizedParams.DustLimit, + c.normalizedParams.CsvDelay, + ) +} + +// decodeCommitChainEpoch decodes a CommitChainEpoch from a reader. +func decodeCommitChainEpoch(r io.Reader) (CommitChainEpoch, error) { + var lastHeight uint64 + var dustLimit btcutil.Amount + var csvDelay uint16 + + err := ReadElements(r, &lastHeight, &dustLimit, &csvDelay) + if err != nil { + return CommitChainEpoch{}, err + } + + return CommitChainEpoch{ + LastHeight: lastHeight, + normalizedParams: CommitmentParams{ + DustLimit: dustLimit, + CsvDelay: csvDelay, + }, + }, nil +} + +// CommitChainEpochHistory is a data structure designed to maintain the +// CommitmentParams history of both commitment chains. +type CommitChainEpochHistory struct { + // normalizedCurrent is the current commitment parameters that are + // in effect. They are separate from the history because we do not + // yet have the final heights that close these epochs out. + normalizedCurrent lntypes.Dual[CommitmentParams] + + // historical is a pair of lists of CommitChainEpochs that are sorted + // by LastHeight. + historical lntypes.Dual[[]CommitChainEpoch] +} + +// Size returns the size of the CommitChainEpochHistory in bytes. +func (c *CommitChainEpochHistory) Size() uint64 { + commitParamSize := uint64(2 * 8) // DustLimit + CsvDelay + epochSize := uint64(8) + commitParamSize + + currentSize := 2 * commitParamSize + localHistorySize := 2 + uint64(len(c.historical.Local))*epochSize + remoteHistorySize := 2 + uint64(len(c.historical.Remote))*epochSize + + return currentSize + localHistorySize + remoteHistorySize +} + +// encode encodes a CommitChainEpochHistory to a writer. +func (c *CommitChainEpochHistory) encode(w io.Writer) error { + // Write the normalized current params, always writing local before + // remote and dust limit before csv delay. + err := WriteElements( + w, c.normalizedCurrent.Local.DustLimit, + c.normalizedCurrent.Local.CsvDelay, + c.normalizedCurrent.Remote.DustLimit, + c.normalizedCurrent.Remote.CsvDelay, + ) + if err != nil { + return err + } + + // Write the length so we can handle deserialization. + err = WriteElement(w, uint16(len(c.historical.Local))) + if err != nil { + return err + } + + // Write the local epochs. + for _, epoch := range c.historical.Local { + err = epoch.encode(w) + if err != nil { + return err + } + } + + // Write the length so we can handle deserialization. + err = WriteElement(w, uint16(len(c.historical.Remote))) + if err != nil { + return err + } + + // Write the remote epochs. + for _, epoch := range c.historical.Remote { + err = epoch.encode(w) + if err != nil { + return err + } + } + + return nil +} + +// ECommitChainEpochHistory defines a tlv encoder for CommitChainEpochHistory. +func ECommitChainEpochHistory(w io.Writer, val interface{}, + buf *[8]byte) error { + + if hist, ok := val.(*CommitChainEpochHistory); ok { + return hist.encode(w) + } + + return tlv.NewTypeForEncodingErr(val, "*CommitChainEpochHistory") +} + +// decodeCommitChainEpochHistory decodes a CommitChainEpochHistory from a +// reader. +func decodeCommitChainEpochHistory(r io.Reader) (CommitChainEpochHistory, + error) { + + var normalizedCurrent lntypes.Dual[CommitmentParams] + err := ReadElements(r, &normalizedCurrent.Local.DustLimit, + &normalizedCurrent.Local.CsvDelay, + &normalizedCurrent.Remote.DustLimit, + &normalizedCurrent.Remote.CsvDelay, + ) + if err != nil { + return CommitChainEpochHistory{}, err + } + + historical := lntypes.Dual[[]CommitChainEpoch]{} + + var localEpochsLen uint16 + err = ReadElement(r, &localEpochsLen) + if err != nil { + return CommitChainEpochHistory{}, err + } + + if localEpochsLen > 0 { + historical.Local = make([]CommitChainEpoch, localEpochsLen) + for i := range historical.Local { + historical.Local[i], err = decodeCommitChainEpoch(r) + if err != nil { + return CommitChainEpochHistory{}, err + } + } + } + + var remoteEpochsLen uint16 + err = ReadElement(r, &remoteEpochsLen) + if err != nil { + return CommitChainEpochHistory{}, err + } + + if remoteEpochsLen > 0 { + historical.Remote = make([]CommitChainEpoch, remoteEpochsLen) + for i := range historical.Remote { + historical.Remote[i], err = decodeCommitChainEpoch(r) + if err != nil { + return CommitChainEpochHistory{}, err + } + } + } + + return CommitChainEpochHistory{ + normalizedCurrent: normalizedCurrent, + historical: historical, + }, nil +} + +// DCommitChainEpochHistory defines a tlv decoder for CommitChainEpochHistory. +func DCommitChainEpochHistory(r io.Reader, val interface{}, + buf *[8]byte, l uint64) error { + + if hist, ok := val.(*CommitChainEpochHistory); ok { + decoded, err := decodeCommitChainEpochHistory(r) + if err != nil { + return err + } + + *hist = decoded + + return nil + } + + return tlv.NewTypeForDecodingErr(val, "*CommitChainEpochHistory", l, l) +} + +// BeginChainEpochHistory initializes a new CommitChainEpochHistory with the +// original CommitmentParams specified in each party's ChannelConfig. +// +// NOTE: This function is only intended to be used during the funding workflow. +func BeginChainEpochHistory( + origCfgParams lntypes.Dual[CommitmentParams]) CommitChainEpochHistory { + + return CommitChainEpochHistory{ + normalizedCurrent: origCfgParams, + historical: lntypes.Dual[[]CommitChainEpoch]{}, + } +} + +// Record returns a TLV record that can be used to encode/decode a +// CommitChainEpochHistory to/from a TLV stream. +// +// NOTE: This is a part of the RecordProducer interface. +func (c *CommitChainEpochHistory) Record() tlv.Record { + return tlv.MakeDynamicRecord( + 0, c, c.Size, ECommitChainEpochHistory, + DCommitChainEpochHistory, + ) +} + +// Push allows a ChannelParty to change the CommitmentParams for the channel and +// mark the last commit heights for each party that the old CommitmentParams +// applied to. To use this function correctly you must call it with the +// understanding that the party making changes to its ChannelConfig will pass +// in the CommitmentParams from that config change unaltered. Finally the +// current commitment heights of both commit chains are passed in to mark the +// last height for each chain that the current channel epoch applies to. +func (c *CommitChainEpochHistory) Push(whoSpecified lntypes.ChannelParty, + params CommitmentParams, currentHeights lntypes.Dual[uint64]) { + + // Here we define a function that marks a set of normalized commitment + // parameters with the current commit height when the epoch concluded + // to create the CommitChainEpoch structure. + closeEpoch := func(normalizedCurrent CommitmentParams, + currentHeight uint64) CommitChainEpoch { + + return CommitChainEpoch{ + LastHeight: currentHeight, + normalizedParams: normalizedCurrent, + } + } + + // Using the function we just defined we now apply it to both the local + // and remote components of the current epoch which will define the last + // height that this set of normalized commitment parameters held for. + closed := lntypes.ZipWithDual( + c.normalizedCurrent, currentHeights, closeEpoch, + ) + + // Since go is unprincipled, we can't treat append as an actual function + // so we make a wrapper for our use case. + push := func(as []CommitChainEpoch, + a CommitChainEpoch) []CommitChainEpoch { + + return append(as, a) + } + + // We now take the closed epoch we just created and add it to the end of + // our history of channel epochs. + c.historical = lntypes.ZipWithDual( + c.historical, closed, push, + ) + + // Now we begin the task of assembling the new normalized current + // commitment parameters for both parties. + newCurrent := lntypes.Dual[CommitmentParams]{} + + // The party issuing the commitment parameter change is referred to as + // "main" here. It could be either the local or remote party but the + // point is that the new Csv for the main party will always be the same + // as the Csv from the last epoch, since a change to the Csv at the + // config level is an imposition on the other party's commitment + // transaction. However, the dust limit will be the dust limit set in + // the argument. + mainCsv := closed.GetForParty(whoSpecified).normalizedParams.CsvDelay + mainParams := CommitmentParams{ + DustLimit: params.DustLimit, + CsvDelay: mainCsv, + } + newCurrent.SetForParty(whoSpecified, mainParams) + + // The other party is referred to as counter here and the key here is + // that for the counterparty, their dust limit will remain the same as + // it was before, while their Csv will update since it is imposed by the + // main party. + counterParty := whoSpecified.CounterParty() + counterDustLimit := closed.GetForParty(counterParty). + normalizedParams.DustLimit + + counterParams := CommitmentParams{ + DustLimit: counterDustLimit, + CsvDelay: params.CsvDelay, + } + newCurrent.SetForParty(counterParty, counterParams) + + // Now that we have set the values appropriately for the newCurrent + // we set the normalizedCurrent values to be the newly computed current + // commitment values. + c.normalizedCurrent = newCurrent +} + +// NormalizedParamsAt queries the CommitChainEpochHistory for the normalized +// commitment parameters for the ChannelParty's commitment transaction at a +// given height. The parameters are referred to as "normalized" because they +// indicate the parameters that apply to that party's commitment transaction +// irrespective of which party is responsible for setting those parameters. +func (c *CommitChainEpochHistory) NormalizedParamsAt( + whoseCommit lntypes.ChannelParty, + height uint64) CommitmentParams { + + // Try to find the epoch that applies to the height. + histEpoch := search(c.historical.GetForParty(whoseCommit), height) + + // Extract just the portion of the epoch that specifies the parameters. + histParams := fn.MapOption(func(e CommitChainEpoch) CommitmentParams { + return e.normalizedParams + })(histEpoch) + + // If we didn't find it, then we use the current parameters. + curr := c.normalizedCurrent.GetForParty(whoseCommit) + + return histParams.UnwrapOr(curr) +} + +// search is responsible for finding the epoch that encloses the specified +// height. This means that the LastHeight of that epoch must be greater than +// or equal to the query height AND the previous epoch's LastHeight must be +// less than the query height. +func search(epochs []CommitChainEpoch, h uint64) fn.Option[CommitChainEpoch] { + // We implement a simple binary search here. + half := len(epochs) / 2 + + switch { + // We have a couple of edge cases here. If we somehow end up with an + // empty epoch history we are querying we return None. + case len(epochs) == 0: + return fn.None[CommitChainEpoch]() + + // If we have a single epoch in the history then that epoch is the + // correct one iff its LastHeight is greater than or equal to the + // query height. + case len(epochs) == 1: + if h <= epochs[0].LastHeight { + return fn.Some(epochs[0]) + } else { + return fn.None[CommitChainEpoch]() + } + + // Otherwise we begin our dividing of the slice. If our height falls + // between the LastHeight of the last epoch in the former half of the + // history and the LastHeight of the first epoch in the latter half of + // the history, then the first epoch of the latter half is the correct + // epoch. + case epochs[half-1].LastHeight < h && + h <= epochs[half].LastHeight: + + return fn.Some(epochs[half]) + + // Now that we've excluded the between case, our query height is in + // either half. If it's less than the LastHeight of the last epoch in + // the former half, then we will compute the search again on that half. + case h <= epochs[half-1].LastHeight: + return search(epochs[:half], h) + + // Otherwise it's in the latter half and so we will compute the search + // on that half. + case h > epochs[half].LastHeight: + return search(epochs[half:], h) + + // We should have excausted all cases, so this indicates something + // severely wrong with the algorithm and we choose to hard-eject. + default: + panic("non-exhaustive cases in commit epochs search") + } +} diff --git a/lntypes/channel_party.go b/lntypes/channel_party.go index 5848becee68..ce9ee3126e4 100644 --- a/lntypes/channel_party.go +++ b/lntypes/channel_party.go @@ -109,6 +109,21 @@ func (d *Dual[A]) ModifyForParty(p ChannelParty, f func(A) A) A { } } +// RefForParty allows us to get a direct pointer access to the field that is +// associated with the given ChannelParty. +func (d *Dual[A]) RefForParty(p ChannelParty) *A { + switch p { + case Local: + return &d.Local + case Remote: + return &d.Remote + default: + panic(fmt.Sprintf( + "switch default triggered in ForParty: %v", p, + )) + } +} + // MapDual applies the function argument to both the Local and Remote fields of // the Dual[A] and returns a Dual[B] with that function applied. func MapDual[A, B any](d Dual[A], f func(A) B) Dual[B] { @@ -117,3 +132,12 @@ func MapDual[A, B any](d Dual[A], f func(A) B) Dual[B] { Remote: f(d.Remote), } } + +// ZipWithDual allows us to combine two Duals into a single Dual using the +// provided function. +func ZipWithDual[A, B, C any](a Dual[A], b Dual[B], f func(A, B) C) Dual[C] { + return Dual[C]{ + Local: f(a.Local, b.Local), + Remote: f(a.Remote, b.Remote), + } +} From 6f983ba1c51b0ff3395d88afd487db5545f1e78e Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 4 Sep 2024 14:09:24 -0700 Subject: [PATCH 21/22] channeldb+lnwallet: add epoch history to OpenChannel This commit takes the CommitChainEpochHistory defined in the last commit and adds it to the OpenChannel structure. As of this commit it is essentially redundant with the ChanCfgs but it will capture the history of the ChanCfgs when we add the ability to update them. --- channeldb/channel.go | 45 +++++++++++++++++++++++++++++++++++++++ channeldb/channel_test.go | 6 ++++++ lnwallet/channel.go | 1 + lnwallet/test_utils.go | 12 +++++++++++ lnwallet/wallet.go | 20 +++++++++++++++++ 5 files changed, 84 insertions(+) diff --git a/channeldb/channel.go b/channeldb/channel.go index b85a8a52ac9..f8dc623b0f0 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -260,6 +260,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] + + // commitChainEpochHistory is the optional TLV encoded blob of data + // representing the commit chain epoch history for the channel. + commitChainEpochHistory tlv.OptionalRecordT[tlv.TlvType8, CommitChainEpochHistory] //nolint:lll } // encode serializes the openChannelTlvData to the given io.Writer. @@ -281,6 +285,11 @@ func (c *openChannelTlvData) encode(w io.Writer) error { c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType7, tlv.Blob]) { tlvRecords = append(tlvRecords, blob.Record()) }) + c.commitChainEpochHistory.WhenSome( + func(hist tlv.RecordT[tlv.TlvType8, CommitChainEpochHistory]) { + tlvRecords = append(tlvRecords, hist.Record()) + }, + ) // Create the tlv stream. tlvStream, err := tlv.NewStream(tlvRecords...) @@ -296,6 +305,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error { memo := c.memo.Zero() tapscriptRoot := c.tapscriptRoot.Zero() blob := c.customBlob.Zero() + commitChainEpochHistory := c.commitChainEpochHistory.Zero() // Create the tlv stream. tlvStream, err := tlv.NewStream( @@ -306,6 +316,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error { memo.Record(), tapscriptRoot.Record(), blob.Record(), + commitChainEpochHistory.Record(), ) if err != nil { return err @@ -325,6 +336,10 @@ func (c *openChannelTlvData) decode(r io.Reader) error { if _, ok := tlvs[c.customBlob.TlvType()]; ok { c.customBlob = tlv.SomeRecordT(blob) } + if _, ok := tlvs[c.commitChainEpochHistory.TlvType()]; ok { + c.commitChainEpochHistory = + tlv.SomeRecordT(commitChainEpochHistory) + } return nil } @@ -947,6 +962,10 @@ type OpenChannel struct { // commitment. Commitments lntypes.Dual[ChannelCommitment] + // CommitChainEpochHistory is the history of the CommitmentParams for + // each side of the channel. + CommitChainEpochHistory CommitChainEpochHistory + // RemoteCurrentRevocation is the current revocation for their // commitment transaction. However, since this the derived public key, // we don't yet have the private key so we aren't yet able to verify @@ -1207,6 +1226,11 @@ func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) { auxData.customBlob.WhenSomeV(func(blob tlv.Blob) { c.CustomBlob = fn.Some(blob) }) + auxData.commitChainEpochHistory.WhenSomeV( + func(history CommitChainEpochHistory) { + c.CommitChainEpochHistory = history + }, + ) } // extractTlvData creates a new openChannelTlvData from the given channel. @@ -1224,6 +1248,11 @@ func (c *OpenChannel) extractTlvData() openChannelTlvData { realScid: tlv.NewRecordT[tlv.TlvType4]( c.confirmedScid, ), + commitChainEpochHistory: tlv.SomeRecordT( + tlv.NewRecordT[tlv.TlvType8]( + c.CommitChainEpochHistory, + ), + ), } if len(c.Memo) != 0 { @@ -4508,6 +4537,22 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error { // open channel. channel.amendTlvData(auxData) + // Now that we've extracted the aux data, we can initialize the + // CommitChainEpochHistory. If we don't find it in the aux data, + // then we initialize it with the original CommitmentParams from + // the ChannelConfig. + histVal := auxData.commitChainEpochHistory.ValOpt() + channel.CommitChainEpochHistory = histVal.UnwrapOr( + BeginChainEpochHistory( + lntypes.MapDual( + channel.ChanCfgs, + func(cfg ChannelConfig) CommitmentParams { + return cfg.CommitmentParams + }, + ), + ), + ) + channel.Packager = NewChannelPackager(channel.ShortChannelID) // Finally, read the optional shutdown scripts. diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index af17297902e..a5167e1ee46 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -343,6 +343,12 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { Local: localCfg, Remote: remoteCfg, }, + CommitChainEpochHistory: BeginChainEpochHistory( + lntypes.Dual[CommitmentParams]{ + Local: localRenderingParams, + Remote: remoteRenderingParams, + }, + ), TotalMSatSent: 8, TotalMSatReceived: 2, Commitments: lntypes.Dual[ChannelCommitment]{ diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3605ba42b74..db0933b5305 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -7321,6 +7321,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { return summary, nil } + // NewLocalForceCloseSummary generates a LocalForceCloseSummary from the given // channel state. The passed commitTx must be a fully signed commitment // transaction corresponding to localCommit. diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 1d93f755154..6d3ae1ed606 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -318,6 +318,12 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, Local: aliceCfg, Remote: bobCfg, }, + CommitChainEpochHistory: channeldb.BeginChainEpochHistory( + lntypes.Dual[channeldb.CommitmentParams]{ + Local: aliceCfg.CommitmentParams, + Remote: bobCfg.CommitmentParams, + }, + ), IdentityPub: aliceKeys[0].PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, @@ -340,6 +346,12 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, Local: bobCfg, Remote: aliceCfg, }, + CommitChainEpochHistory: channeldb.BeginChainEpochHistory( + lntypes.Dual[channeldb.CommitmentParams]{ + Local: bobCfg.CommitmentParams, + Remote: aliceCfg.CommitmentParams, + }, + ), IdentityPub: bobKeys[0].PubKey(), FundingOutpoint: *prevOut, ShortChannelID: shortChanID, diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 1812f887121..972330f1b23 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -2244,6 +2244,16 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // he stored within the database. res.partialState.ChanCfgs.Local = res.ourContribution.toChanConfig() res.partialState.ChanCfgs.Remote = res.theirContribution.toChanConfig() + res.partialState.CommitChainEpochHistory = + channeldb.BeginChainEpochHistory( + lntypes.MapDual( + res.partialState.ChanCfgs, + //nolint:lll + func(cfg channeldb.ChannelConfig) channeldb.CommitmentParams { + return cfg.CommitmentParams + }, + ), + ) // We'll also record the finalized funding txn, which will allow us to // rebroadcast on startup in case we fail. @@ -2425,6 +2435,16 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { pendingReservation.ourContribution.toChanConfig() chanState.ChanCfgs.Remote = pendingReservation.theirContribution.toChanConfig() + chanState.CommitChainEpochHistory = + channeldb.BeginChainEpochHistory( + lntypes.MapDual( + chanState.ChanCfgs, + //nolint:lll + func(cfg channeldb.ChannelConfig) channeldb.CommitmentParams { + return cfg.CommitmentParams + }, + ), + ) chanState.RevocationKeyLocator = pendingReservation.nextRevocationKeyLoc From b3cb1b7f14d2b8e0dc6d23a9355c16c46d8cba50 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 4 Sep 2024 16:16:53 -0700 Subject: [PATCH 22/22] lnwallet: use CommitChainEpochHistory to determine CsvDelay during Breach This commit changes the way we create breach retributions to use the CsvDelay we compute from the CommitChainEpochHistory so as to account for the possibility that the channel parameters have changed since opening. --- lnwallet/channel.go | 31 ++++++++++++++++++++----------- lnwallet/channel_test.go | 6 +++--- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index db0933b5305..22c226d39f7 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2061,7 +2061,12 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, return l.LocalAuxLeaf }, )(auxResult.AuxLeaves) - theirDelay := uint32(chanState.ChanCfgs.Remote.CsvDelay) + theirDelay := uint32( + chanState.CommitChainEpochHistory.NormalizedParamsAt( + lntypes.Remote, stateNum, + ).CsvDelay, + ) + theirScript, err := CommitScriptToSelf( chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey, keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf, @@ -2081,7 +2086,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // we need. if revokedLog != nil { br, ourAmt, theirAmt, err = createBreachRetribution( - revokedLog, spendTx, chanState, keyRing, + revokedLog, spendTx, chanState, stateNum, keyRing, commitmentSecret, leaseExpiry, auxResult.AuxLeaves, ) if err != nil { @@ -2096,8 +2101,8 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // data can still function. This branch can be deleted once we // are confident that no legacy format is in use. br, ourAmt, theirAmt, err = createBreachRetributionLegacy( - revokedLogLegacy, chanState, keyRing, commitmentSecret, - ourScript, theirScript, leaseExpiry, + revokedLogLegacy, chanState, stateNum, keyRing, + commitmentSecret, ourScript, theirScript, leaseExpiry, ) if err != nil { return nil, err @@ -2213,7 +2218,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // createHtlcRetribution is a helper function to construct an HtlcRetribution // based on the passed params. -func createHtlcRetribution(chanState *channeldb.OpenChannel, +func createHtlcRetribution(chanState *channeldb.OpenChannel, stateNum uint64, keyRing *CommitmentKeyRing, commitHash chainhash.Hash, commitmentSecret *btcec.PrivateKey, leaseExpiry uint32, htlc *channeldb.HTLCEntry, @@ -2221,7 +2226,11 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, var emptyRetribution HtlcRetribution - theirDelay := uint32(chanState.ChanCfgs.Remote.CsvDelay) + theirDelay := uint32( + chanState.CommitChainEpochHistory.NormalizedParamsAt( + lntypes.Remote, stateNum, + ).CsvDelay, + ) isRemoteInitiator := !chanState.IsInitiator // We'll generate the original second level witness script now, as @@ -2334,7 +2343,7 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, // see if these fields are present there. If they are not, then // ErrRevLogDataMissing is returned. func createBreachRetribution(revokedLog *channeldb.RevocationLog, - spendTx *wire.MsgTx, chanState *channeldb.OpenChannel, + spendTx *wire.MsgTx, chanState *channeldb.OpenChannel, stateNum uint64, keyRing *CommitmentKeyRing, commitmentSecret *btcec.PrivateKey, leaseExpiry uint32, auxLeaves fn.Option[CommitAuxLeaves]) (*BreachRetribution, int64, int64, @@ -2346,7 +2355,7 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, htlcRetributions := make([]HtlcRetribution, len(revokedLog.HTLCEntries)) for i, htlc := range revokedLog.HTLCEntries { hr, err := createHtlcRetribution( - chanState, keyRing, commitHash.Val, + chanState, stateNum, keyRing, commitHash.Val, commitmentSecret, leaseExpiry, htlc, auxLeaves, ) if err != nil { @@ -2450,8 +2459,8 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, // BreachRetribution using a ChannelCommitment. Returns the constructed // retribution, our amount, their amount, and a possible non-nil error. func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, - chanState *channeldb.OpenChannel, keyRing *CommitmentKeyRing, - commitmentSecret *btcec.PrivateKey, + chanState *channeldb.OpenChannel, stateNum uint64, + keyRing *CommitmentKeyRing, commitmentSecret *btcec.PrivateKey, ourScript, theirScript input.ScriptDescriptor, leaseExpiry uint32) (*BreachRetribution, int64, int64, error) { @@ -2497,7 +2506,7 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, } hr, err := createHtlcRetribution( - chanState, keyRing, commitHash, + chanState, stateNum, keyRing, commitHash, commitmentSecret, leaseExpiry, entry, fn.None[CommitAuxLeaves](), ) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index a71c6cec32f..776b652f633 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9673,7 +9673,7 @@ func TestCreateHtlcRetribution(t *testing.T) { // Create the htlc retribution. hr, err := createHtlcRetribution( - aliceChannel.channelState, keyRing, commitHash, + aliceChannel.channelState, 0,keyRing, commitHash, dummyPrivate, leaseExpiry, htlc, fn.None[CommitAuxLeaves](), ) // Expect no error. @@ -9878,7 +9878,7 @@ func TestCreateBreachRetribution(t *testing.T) { br, our, their, err := createBreachRetribution( tc.revocationLog, tx, - aliceChannel.channelState, keyRing, + aliceChannel.channelState, 0, keyRing, dummyPrivate, leaseExpiry, fn.None[CommitAuxLeaves](), ) @@ -9937,7 +9937,7 @@ func TestCreateBreachRetributionLegacy(t *testing.T) { // Create the breach retribution using the legacy format. br, ourAmt, theirAmt, err := createBreachRetributionLegacy( - &revokedLog, aliceChannel.channelState, keyRing, + &revokedLog, aliceChannel.channelState, 0, keyRing, dummyPrivate, ourScript, theirScript, leaseExpiry, ) require.NoError(t, err)