From a0515a16db28d945c38ac77c5508183671773ae5 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 5 Apr 2024 16:48:27 -0700 Subject: [PATCH 1/5] htlcswitch: extract error handling for syncChanStates --- htlcswitch/link.go | 161 ++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index ff140352c..c459ef997 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1086,6 +1086,83 @@ func (l *channelLink) loadAndRemove() error { return l.channel.RemoveFwdPkgs(removeHeights...) } +// handleChanSyncErr performs the error handling logic in the case where we +// could not successfully syncChanStates with our channel peer. +func (l *channelLink) handleChanSyncErr(err error) { + l.log.Warnf("error when syncing channel states: %v", err) + + var errDataLoss *lnwallet.ErrCommitSyncLocalDataLoss + + switch { + case errors.Is(err, ErrLinkShuttingDown): + l.log.Debugf("unable to sync channel states, link is " + + "shutting down") + return + + // We failed syncing the commit chains, probably because the remote has + // lost state. We should force close the channel. + case errors.Is(err, lnwallet.ErrCommitSyncRemoteDataLoss): + fallthrough + + // The remote sent us an invalid last commit secret, we should force + // close the channel. + // TODO(halseth): and permanently ban the peer? + case errors.Is(err, lnwallet.ErrInvalidLastCommitSecret): + fallthrough + + // The remote sent us a commit point different from what they sent us + // before. + // TODO(halseth): ban peer? + case errors.Is(err, lnwallet.ErrInvalidLocalUnrevokedCommitPoint): + // We'll fail the link and tell the peer to force close the + // channel. Note that the database state is not updated here, + // but will be updated when the close transaction is ready to + // avoid that we go down before storing the transaction in the + // db. + l.failf( + LinkFailureError{ + code: ErrSyncError, + FailureAction: LinkFailureForceClose, + }, + "unable to synchronize channel states: %v", err, + ) + + // We have lost state and cannot safely force close the channel. Fail + // the channel and wait for the remote to hopefully force close it. The + // remote has sent us its latest unrevoked commitment point, and we'll + // store it in the database, such that we can attempt to recover the + // funds if the remote force closes the channel. + case errors.As(err, &errDataLoss): + err := l.channel.MarkDataLoss( + errDataLoss.CommitPoint, + ) + if err != nil { + l.log.Errorf("unable to mark channel data loss: %v", + err) + } + + // We determined the commit chains were not possible to sync. We + // cautiously fail the channel, but don't force close. + // TODO(halseth): can we safely force close in any cases where this + // error is returned? + case errors.Is(err, lnwallet.ErrCannotSyncCommitChains): + if err := l.channel.MarkBorked(); err != nil { + l.log.Errorf("unable to mark channel borked: %v", err) + } + + // Other, unspecified error. + default: + } + + l.failf( + LinkFailureError{ + code: ErrRecoveryError, + FailureAction: LinkFailureForceNone, + }, + "unable to synchronize channel states: %v", err, + ) +} + // htlcManager is the primary goroutine which drives a channel's commitment // update state-machine in response to messages received via several channels. // This goroutine reads messages from the upstream (remote) peer, and also from @@ -1121,89 +1198,7 @@ func (l *channelLink) htlcManager() { if l.cfg.SyncStates { err := l.syncChanStates() if err != nil { - l.log.Warnf("error when syncing channel states: %v", err) - - errDataLoss, localDataLoss := - err.(*lnwallet.ErrCommitSyncLocalDataLoss) - - switch { - case err == ErrLinkShuttingDown: - l.log.Debugf("unable to sync channel states, " + - "link is shutting down") - return - - // We failed syncing the commit chains, probably - // because the remote has lost state. We should force - // close the channel. - case err == lnwallet.ErrCommitSyncRemoteDataLoss: - fallthrough - - // The remote sent us an invalid last commit secret, we - // should force close the channel. - // TODO(halseth): and permanently ban the peer? - case err == lnwallet.ErrInvalidLastCommitSecret: - fallthrough - - // The remote sent us a commit point different from - // what they sent us before. - // TODO(halseth): ban peer? - case err == lnwallet.ErrInvalidLocalUnrevokedCommitPoint: - // We'll fail the link and tell the peer to - // force close the channel. Note that the - // database state is not updated here, but will - // be updated when the close transaction is - // ready to avoid that we go down before - // storing the transaction in the db. - l.failf( - //nolint:lll - LinkFailureError{ - code: ErrSyncError, - FailureAction: LinkFailureForceClose, - }, - "unable to synchronize channel "+ - "states: %v", err, - ) - return - - // We have lost state and cannot safely force close the - // channel. Fail the channel and wait for the remote to - // hopefully force close it. The remote has sent us its - // latest unrevoked commitment point, and we'll store - // it in the database, such that we can attempt to - // recover the funds if the remote force closes the - // channel. - case localDataLoss: - err := l.channel.MarkDataLoss( - errDataLoss.CommitPoint, - ) - if err != nil { - l.log.Errorf("unable to mark channel "+ - "data loss: %v", err) - } - - // We determined the commit chains were not possible to - // sync. We cautiously fail the channel, but don't - // force close. - // TODO(halseth): can we safely force close in any - // cases where this error is returned? - case err == lnwallet.ErrCannotSyncCommitChains: - if err := l.channel.MarkBorked(); err != nil { - l.log.Errorf("unable to mark channel "+ - "borked: %v", err) - } - - // Other, unspecified error. - default: - } - - l.failf( - LinkFailureError{ - code: ErrRecoveryError, - FailureAction: LinkFailureForceNone, - }, - "unable to synchronize channel "+ - "states: %v", err, - ) + l.handleChanSyncErr(err) return } } From 8077862225501a338e274ecfae4c9d3a10470bdd Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 9 Aug 2024 12:47:58 -0700 Subject: [PATCH 2/5] lnwallet: pack commit chains into Dual This commit packs the LightningChannel's localCommitmentChain and remoteCommitmentChain into a Dual structure for better symmetric access. This will be leveraged by an upcoming commit where we want to more concisely express how we compute the number of pending updates. --- lnwallet/channel.go | 122 ++++++++++++++++++++------------------- lnwallet/channel_test.go | 56 ++++++++++-------- 2 files changed, 93 insertions(+), 85 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index e5cbe6b6a..e55cc8ad2 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -858,15 +858,13 @@ type LightningChannel struct { // accepted. currentHeight uint64 - // remoteCommitChain is the remote node's commitment chain. Any new - // commitments we initiate are added to the tip of this chain. - remoteCommitChain *commitmentChain - - // localCommitChain is our local commitment chain. Any new commitments - // received are added to the tip of this chain. The tail (or lowest - // height) in this chain is our current accepted state, which we are - // able to broadcast safely. - localCommitChain *commitmentChain + // commitChains is a Dual of the local and remote node's commitment + // chains. Any new commitments we initiate are added to Remote chain's + // tip. The Local portion of this field is our local commitment chain. + // Any new commitments received are added to the tip of this chain. + // The tail (or lowest height) in this chain is our current accepted + // state, which we are able to broadcast safely. + commitChains lntypes.Dual[*commitmentChain] channelState *channeldb.OpenChannel @@ -992,14 +990,18 @@ func NewLightningChannel(signer input.Signer, return nil, fmt.Errorf("unable to derive shachain: %w", err) } + commitChains := lntypes.Dual[*commitmentChain]{ + Local: newCommitmentChain(), + Remote: newCommitmentChain(), + } + lc := &LightningChannel{ - Signer: signer, - leafStore: opts.leafStore, - sigPool: sigPool, - currentHeight: localCommit.CommitHeight, - remoteCommitChain: newCommitmentChain(), - localCommitChain: newCommitmentChain(), - channelState: state, + Signer: signer, + leafStore: opts.leafStore, + sigPool: sigPool, + currentHeight: localCommit.CommitHeight, + commitChains: commitChains, + channelState: state, commitBuilder: NewCommitmentBuilder( state, opts.leafStore, ), @@ -1463,10 +1465,10 @@ func (lc *LightningChannel) restoreCommitState( if err != nil { return err } - lc.localCommitChain.addCommitment(localCommit) + lc.commitChains.Local.addCommitment(localCommit) lc.log.Tracef("starting local commitment: %v", - lnutils.SpewLogClosure(lc.localCommitChain.tail())) + lnutils.SpewLogClosure(lc.commitChains.Local.tail())) // We'll also do the same for the remote commitment chain. remoteCommit, err := lc.diskCommitToMemCommit( @@ -1476,10 +1478,10 @@ func (lc *LightningChannel) restoreCommitState( if err != nil { return err } - lc.remoteCommitChain.addCommitment(remoteCommit) + lc.commitChains.Remote.addCommitment(remoteCommit) lc.log.Tracef("starting remote commitment: %v", - lnutils.SpewLogClosure(lc.remoteCommitChain.tail())) + lnutils.SpewLogClosure(lc.commitChains.Remote.tail())) var ( pendingRemoteCommit *commitment @@ -1508,10 +1510,10 @@ func (lc *LightningChannel) restoreCommitState( if err != nil { return err } - lc.remoteCommitChain.addCommitment(pendingRemoteCommit) + lc.commitChains.Remote.addCommitment(pendingRemoteCommit) lc.log.Debugf("pending remote commitment: %v", - lnutils.SpewLogClosure(lc.remoteCommitChain.tip())) + lnutils.SpewLogClosure(lc.commitChains.Remote.tip())) // We'll also re-create the set of commitment keys needed to // fully re-derive the state. @@ -2655,10 +2657,10 @@ func (lc *LightningChannel) fetchCommitmentView( ourLogIndex, ourHtlcIndex, theirLogIndex, theirHtlcIndex uint64, keyRing *CommitmentKeyRing) (*commitment, error) { - commitChain := lc.localCommitChain + commitChain := lc.commitChains.Local dustLimit := lc.channelState.LocalChanCfg.DustLimit if whoseCommitChain.IsRemote() { - commitChain = lc.remoteCommitChain + commitChain = lc.commitChains.Remote dustLimit = lc.channelState.RemoteChanCfg.DustLimit } @@ -3480,10 +3482,10 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) // Fetch the last remote update that we have signed for. - lastRemoteCommitted := lc.remoteCommitChain.tail().theirMessageIndex + lastRemoteCommitted := lc.commitChains.Remote.tail().theirMessageIndex // Fetch the last remote update that we have acked. - lastLocalCommitted := lc.localCommitChain.tail().theirMessageIndex + lastLocalCommitted := lc.commitChains.Local.tail().theirMessageIndex // We'll now run through the remote update log to locate the items that // we haven't signed for yet. This will be the set of items we need to @@ -3709,9 +3711,9 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, ) error { // First fetch the initial balance before applying any updates. - commitChain := lc.localCommitChain + commitChain := lc.commitChains.Local if whoseCommitChain.IsRemote() { - commitChain = lc.remoteCommitChain + commitChain = lc.commitChains.Remote } ourInitialBalance := commitChain.tip().ourBalance theirInitialBalance := commitChain.tip().theirBalance @@ -3961,7 +3963,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // party, then we're unable to create new states. Each time we create a // new state, we consume a prior revocation point. commitPoint := lc.channelState.RemoteNextRevocation - unacked := lc.remoteCommitChain.hasUnackedCommitment() + unacked := lc.commitChains.Remote.hasUnackedCommitment() if unacked || commitPoint == nil { lc.log.Tracef("waiting for remote ack=%v, nil "+ "RemoteNextRevocation: %v", unacked, commitPoint == nil) @@ -3969,8 +3971,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } // Determine the last update on the remote log that has been locked in. - remoteACKedIndex := lc.localCommitChain.tail().theirMessageIndex - remoteHtlcIndex := lc.localCommitChain.tail().theirHtlcIndex + remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex + remoteHtlcIndex := lc.commitChains.Local.tail().theirHtlcIndex // Before we extend this new commitment to the remote commitment chain, // ensure that we aren't violating any of the constraints the remote @@ -4117,7 +4119,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // Extend the remote commitment chain by one with the addition of our // latest commitment update. - lc.remoteCommitChain.addCommitment(newCommitView) + lc.commitChains.Remote.addCommitment(newCommitView) return &NewCommitState{ CommitSigs: &CommitSigs{ @@ -4233,9 +4235,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // Take note of our current commit chain heights before we begin adding // more to them. var ( - localTailHeight = lc.localCommitChain.tail().height - remoteTailHeight = lc.remoteCommitChain.tail().height - remoteTipHeight = lc.remoteCommitChain.tip().height + localTailHeight = lc.commitChains.Local.tail().height + remoteTailHeight = lc.commitChains.Remote.tail().height + remoteTipHeight = lc.commitChains.Remote.tip().height ) // We'll now check that their view of our local chain is up-to-date. @@ -4503,10 +4505,10 @@ func (lc *LightningChannel) computeView(view *HtlcView, dryRunFee fn.Option[chainfee.SatPerKWeight]) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, lntypes.WeightUnit, *HtlcView, error) { - commitChain := lc.localCommitChain + commitChain := lc.commitChains.Local dustLimit := lc.channelState.LocalChanCfg.DustLimit if whoseCommitChain.IsRemote() { - commitChain = lc.remoteCommitChain + commitChain = lc.commitChains.Remote dustLimit = lc.channelState.RemoteChanCfg.DustLimit } @@ -4969,8 +4971,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } // Determine the last update on the local log that has been locked in. - localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex - localHtlcIndex := lc.remoteCommitChain.tail().ourHtlcIndex + localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex + localHtlcIndex := lc.commitChains.Remote.tail().ourHtlcIndex // Ensure that this new local update from the remote node respects all // the constraints we specified during initial channel setup. If not, @@ -5207,7 +5209,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { localCommitmentView.sig = commitSigs.CommitSig.ToSignatureBytes() //nolint:lll } - lc.localCommitChain.addCommitment(localCommitmentView) + lc.commitChains.Local.addCommitment(localCommitmentView) return nil } @@ -5225,13 +5227,13 @@ func (lc *LightningChannel) IsChannelClean() bool { defer lc.RUnlock() // Check whether we have a pending commitment for our local state. - if lc.localCommitChain.hasUnackedCommitment() { + if lc.commitChains.Local.hasUnackedCommitment() { return false } // Check whether our counterparty has a pending commitment for their // state. - if lc.remoteCommitChain.hasUnackedCommitment() { + if lc.commitChains.Remote.hasUnackedCommitment() { return false } @@ -5284,8 +5286,8 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { var ( remoteUpdatesPending, localUpdatesPending bool - lastLocalCommit = lc.localCommitChain.tip() - lastRemoteCommit = lc.remoteCommitChain.tip() + lastLocalCommit = lc.commitChains.Local.tip() + lastRemoteCommit = lc.commitChains.Remote.tip() perspective string ) @@ -5337,7 +5339,7 @@ func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { lc.RLock() defer lc.RUnlock() - lastRemoteCommit := lc.remoteCommitChain.tip() + lastRemoteCommit := lc.commitChains.Remote.tip() return lc.localUpdateLog.logIndex - lastRemoteCommit.ourMessageIndex } @@ -5362,16 +5364,16 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, } lc.log.Tracef("revoking height=%v, now at height=%v", - lc.localCommitChain.tail().height, + lc.commitChains.Local.tail().height, lc.currentHeight+1) // Advance our tail, as we've revoked our previous state. - lc.localCommitChain.advanceTail() + lc.commitChains.Local.advanceTail() lc.currentHeight++ // Additionally, generate a channel delta for this state transition for // persistent storage. - chainTail := lc.localCommitChain.tail() + chainTail := lc.commitChains.Local.tail() newCommitment := chainTail.toDiskCommit(lntypes.Local) // Get the unsigned acked remotes updates that are currently in memory. @@ -5451,13 +5453,13 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( lc.log.Tracef("remote party accepted state transition, revoked height "+ "%v, now at %v", - lc.remoteCommitChain.tail().height, - lc.remoteCommitChain.tail().height+1) + lc.commitChains.Remote.tail().height, + lc.commitChains.Remote.tail().height+1) // Add one to the remote tail since this will be height *after* we write // the revocation to disk, the local height will remain unchanged. - remoteChainTail := lc.remoteCommitChain.tail().height + 1 - localChainTail := lc.localCommitChain.tail().height + remoteChainTail := lc.commitChains.Remote.tail().height + 1 + localChainTail := lc.commitChains.Local.tail().height source := lc.ShortChanID() chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) @@ -5598,8 +5600,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // We use the remote commitment chain's tip as it will soon become the tail // once advanceTail is called. - remoteMessageIndex := lc.remoteCommitChain.tip().ourMessageIndex - localMessageIndex := lc.localCommitChain.tail().ourMessageIndex + remoteMessageIndex := lc.commitChains.Remote.tip().ourMessageIndex + localMessageIndex := lc.commitChains.Local.tail().ourMessageIndex localPeerUpdates := lc.unsignedLocalUpdates( remoteMessageIndex, localMessageIndex, chanID, @@ -5660,7 +5662,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // Since they revoked the current lowest height in their commitment // chain, we can advance their chain by a single commitment. - lc.remoteCommitChain.advanceTail() + lc.commitChains.Remote.advanceTail() // As we've just completed a new state transition, attempt to see if we // can remove any entries from the update log which have been removed @@ -5921,7 +5923,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, buffer BufferType) error { // Make sure adding this HTLC won't violate any of the constraints we // must keep on the commitment transactions. - remoteACKedIndex := lc.localCommitChain.tail().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex // First we'll check whether this HTLC can be added to the remote // commitment transaction without violation any of the constraints. @@ -5977,7 +5979,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // PR). } - localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex + localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex // Clamp down on the number of HTLC's we can receive by checking the // commitment sanity. @@ -8123,7 +8125,7 @@ func (lc *LightningChannel) availableBalance( // We'll grab the current set of log updates that the remote has // ACKed. - remoteACKedIndex := lc.localCommitChain.tip().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.localUpdateLog.logIndex) @@ -8308,7 +8310,7 @@ func (lc *LightningChannel) validateFeeRate(feePerKw chainfee.SatPerKWeight) err availableBalance, txWeight := lc.availableBalance(AdditionalHtlc) oldFee := lnwire.NewMSatFromSatoshis( - lc.localCommitChain.tip().feePerKw.FeeForWeight(txWeight), + lc.commitChains.Local.tip().feePerKw.FeeForWeight(txWeight), ) // Our base balance is the total amount of satoshis we can commit @@ -8641,7 +8643,7 @@ func (lc *LightningChannel) MaxFeeRate( // exactly why it was introduced to react for sharp fee changes. availableBalance, weight := lc.availableBalance(AdditionalHtlc) - currentFee := lc.localCommitChain.tip().feePerKw.FeeForWeight(weight) + currentFee := lc.commitChains.Local.tip().feePerKw.FeeForWeight(weight) // baseBalance is the maximum amount available for us to spend on fees. baseBalance := availableBalance.ToSatoshis() + currentFee diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index de6bb219c..3b6ee77de 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -1435,12 +1435,12 @@ func TestHTLCDustLimit(t *testing.T) { // while Bob's should not, because the value falls beneath his dust // limit. The amount of the HTLC should be applied to fees in Bob's // commitment transaction. - aliceCommitment := aliceChannel.localCommitChain.tip() + aliceCommitment := aliceChannel.commitChains.Local.tip() if len(aliceCommitment.txn.TxOut) != 3 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 3, len(aliceCommitment.txn.TxOut)) } - bobCommitment := bobChannel.localCommitChain.tip() + bobCommitment := bobChannel.commitChains.Local.tip() if len(bobCommitment.txn.TxOut) != 2 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 2, len(bobCommitment.txn.TxOut)) @@ -1465,7 +1465,7 @@ func TestHTLCDustLimit(t *testing.T) { // At this point, for Alice's commitment chains, the value of the HTLC // should have been added to Alice's balance and TotalSatoshisSent. - commitment := aliceChannel.localCommitChain.tip() + commitment := aliceChannel.commitChains.Local.tip() if len(commitment.txn.TxOut) != 2 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 2, len(commitment.txn.TxOut)) @@ -1698,7 +1698,7 @@ func TestChannelBalanceDustLimit(t *testing.T) { // output for Alice's balance should have been removed as dust, leaving // only a single output that will send the remaining funds in the // channel to Bob. - commitment := bobChannel.localCommitChain.tip() + commitment := bobChannel.commitChains.Local.tip() if len(commitment.txn.TxOut) != 1 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 1, len(commitment.txn.TxOut)) @@ -1816,25 +1816,25 @@ func TestStateUpdatePersistence(t *testing.T) { // After the state transition the fee update is fully locked in, and // should've been removed from both channels' update logs. - if aliceChannel.localCommitChain.tail().feePerKw != fee { + if aliceChannel.commitChains.Local.tail().feePerKw != fee { t.Fatalf("fee not locked in") } - if bobChannel.localCommitChain.tail().feePerKw != fee { + if bobChannel.commitChains.Local.tail().feePerKw != fee { t.Fatalf("fee not locked in") } assertNumLogUpdates(3, 1) // The latest commitment from both sides should have all the HTLCs. - numAliceOutgoing := aliceChannel.localCommitChain.tail().outgoingHTLCs - numAliceIncoming := aliceChannel.localCommitChain.tail().incomingHTLCs + numAliceOutgoing := aliceChannel.commitChains.Local.tail().outgoingHTLCs + numAliceIncoming := aliceChannel.commitChains.Local.tail().incomingHTLCs if len(numAliceOutgoing) != 3 { t.Fatalf("expected %v htlcs, instead got %v", 3, numAliceOutgoing) } if len(numAliceIncoming) != 1 { t.Fatalf("expected %v htlcs, instead got %v", 1, numAliceIncoming) } - numBobOutgoing := bobChannel.localCommitChain.tail().outgoingHTLCs - numBobIncoming := bobChannel.localCommitChain.tail().incomingHTLCs + numBobOutgoing := bobChannel.commitChains.Local.tail().outgoingHTLCs + numBobIncoming := bobChannel.commitChains.Local.tail().incomingHTLCs if len(numBobOutgoing) != 1 { t.Fatalf("expected %v htlcs, instead got %v", 1, numBobOutgoing) } @@ -2090,20 +2090,24 @@ func TestCancelHTLC(t *testing.T) { // Now HTLCs should be present on the commitment transaction for either // side. - if len(aliceChannel.localCommitChain.tip().outgoingHTLCs) != 0 || - len(aliceChannel.remoteCommitChain.tip().outgoingHTLCs) != 0 { + if len(aliceChannel.commitChains.Local.tip().outgoingHTLCs) != 0 || + len(aliceChannel.commitChains.Remote.tip().outgoingHTLCs) != 0 { + t.Fatalf("htlc's still active from alice's POV") } - if len(aliceChannel.localCommitChain.tip().incomingHTLCs) != 0 || - len(aliceChannel.remoteCommitChain.tip().incomingHTLCs) != 0 { + if len(aliceChannel.commitChains.Local.tip().incomingHTLCs) != 0 || + len(aliceChannel.commitChains.Remote.tip().incomingHTLCs) != 0 { + t.Fatalf("htlc's still active from alice's POV") } - if len(bobChannel.localCommitChain.tip().outgoingHTLCs) != 0 || - len(bobChannel.remoteCommitChain.tip().outgoingHTLCs) != 0 { + if len(bobChannel.commitChains.Local.tip().outgoingHTLCs) != 0 || + len(bobChannel.commitChains.Remote.tip().outgoingHTLCs) != 0 { + t.Fatalf("htlc's still active from bob's POV") } - if len(bobChannel.localCommitChain.tip().incomingHTLCs) != 0 || - len(bobChannel.remoteCommitChain.tip().incomingHTLCs) != 0 { + if len(bobChannel.commitChains.Local.tip().incomingHTLCs) != 0 || + len(bobChannel.commitChains.Remote.tip().incomingHTLCs) != 0 { + t.Fatalf("htlc's still active from bob's POV") } @@ -5207,7 +5211,9 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // When sending htlcs we enforce the feebuffer on the commitment // transaction. remoteCommitWeight := func(lc *LightningChannel) lntypes.WeightUnit { - remoteACKedIndex := lc.localCommitChain.tip().theirMessageIndex + remoteACKedIndex := + lc.commitChains.Local.tip().theirMessageIndex + htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.localUpdateLog.logIndex) @@ -5830,7 +5836,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // At this point, Alice's commitment chain should have a new pending // commit for Bob. We'll extract it so we can simulate Bob broadcasting // the commitment due to an issue. - bobCommit := aliceChannel.remoteCommitChain.tip().txn + bobCommit := aliceChannel.commitChains.Remote.tip().txn bobTxHash := bobCommit.TxHash() spendDetail := &chainntnfs.SpendDetail{ SpenderTxHash: &bobTxHash, @@ -7523,7 +7529,7 @@ func TestForceCloseBorkedState(t *testing.T) { // We manually advance the commitment tail here since the above // ReceiveRevocation call will fail before it's actually advanced. - aliceChannel.remoteCommitChain.advanceTail() + aliceChannel.commitChains.Remote.advanceTail() _, err = aliceChannel.SignNextCommitment() if err != channeldb.ErrChanBorked { t.Fatalf("sign commitment should have failed: %v", err) @@ -7739,7 +7745,7 @@ func TestIdealCommitFeeRate(t *testing.T) { maxFeeAlloc float64) chainfee.SatPerKWeight { balance, weight := c.availableBalance(AdditionalHtlc) - feeRate := c.localCommitChain.tip().feePerKw + feeRate := c.commitChains.Local.tip().feePerKw currentFee := feeRate.FeeForWeight(weight) maxBalance := balance.ToSatoshis() + currentFee @@ -7756,7 +7762,7 @@ func TestIdealCommitFeeRate(t *testing.T) { // currentFeeRate calculates the current fee rate of the channel. The // ideal fee rate is floored at the current fee rate of the channel. currentFeeRate := func(c *LightningChannel) chainfee.SatPerKWeight { - return c.localCommitChain.tip().feePerKw + return c.commitChains.Local.tip().feePerKw } // testCase definies the test cases when calculating the ideal fee rate @@ -11099,7 +11105,7 @@ func TestBlindingPointPersistence(t *testing.T) { require.NoError(t, err, "unable to restart alice") // Assert that the blinding point is restored from disk. - remoteCommit := aliceChannel.remoteCommitChain.tip() + remoteCommit := aliceChannel.commitChains.Remote.tip() require.Len(t, remoteCommit.outgoingHTLCs, 1) require.Equal(t, blinding, remoteCommit.outgoingHTLCs[0].BlindingPoint.UnwrapOrFailV(t)) @@ -11116,7 +11122,7 @@ func TestBlindingPointPersistence(t *testing.T) { require.NoError(t, err, "unable to restart bob's channel") // Assert that Bob is able to recover the blinding point from disk. - bobCommit := bobChannel.localCommitChain.tip() + bobCommit := bobChannel.commitChains.Local.tip() require.Len(t, bobCommit.incomingHTLCs, 1) require.Equal(t, blinding, bobCommit.incomingHTLCs[0].BlindingPoint.UnwrapOrFailV(t)) From 2e7fbc446f41804c1ed2f33a8f6a6c784e95fda9 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 9 Aug 2024 13:00:59 -0700 Subject: [PATCH 3/5] lnwallet: pack update logs into Dual This commit, like the last one packs the update logs into a symmetric Dual structure. This will allow us to index into them more concisely in higher order logic. --- lnwallet/channel.go | 195 +++++++++++++++++--------------- lnwallet/channel_test.go | 233 ++++++++++++++++++++++----------------- 2 files changed, 235 insertions(+), 193 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index e55cc8ad2..daac2b72c 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -874,8 +874,7 @@ type LightningChannel struct { // updates to this channel. The log is walked backwards as HTLC updates // are applied in order to re-construct a commitment transaction from a // commitment. The log is compacted once a revocation is received. - localUpdateLog *updateLog - remoteUpdateLog *updateLog + updateLogs lntypes.Dual[*updateLog] // log is a channel-specific logging instance. log btclog.Logger @@ -980,6 +979,10 @@ func NewLightningChannel(signer input.Signer, remoteUpdateLog := newUpdateLog( localCommit.RemoteLogIndex, localCommit.RemoteHtlcIndex, ) + updateLogs := lntypes.Dual[*updateLog]{ + Local: localUpdateLog, + Remote: remoteUpdateLog, + } logPrefix := fmt.Sprintf("ChannelPoint(%v):", state.FundingOutpoint) @@ -1005,8 +1008,7 @@ func NewLightningChannel(signer input.Signer, commitBuilder: NewCommitmentBuilder( state, opts.leafStore, ), - localUpdateLog: localUpdateLog, - remoteUpdateLog: remoteUpdateLog, + updateLogs: updateLogs, Capacity: state.Capacity, taprootNonceProducer: taprootNonceProducer, log: build.NewPrefixLog(logPrefix, walletLog), @@ -1662,7 +1664,7 @@ func (lc *LightningChannel) restoreStateLogs( htlc.addCommitHeightRemote = incomingRemoteAddHeights[htlc.HtlcIndex] // Restore the htlc back to the remote log. - lc.remoteUpdateLog.restoreHtlc(&htlc) + lc.updateLogs.Remote.restoreHtlc(&htlc) } // Similarly, we'll do the same for the outgoing HTLCs within the @@ -1677,7 +1679,7 @@ func (lc *LightningChannel) restoreStateLogs( htlc.addCommitHeightLocal = outgoingLocalAddHeights[htlc.HtlcIndex] // Restore the htlc back to the local log. - lc.localUpdateLog.restoreHtlc(&htlc) + lc.updateLogs.Local.restoreHtlc(&htlc) } // If we have a dangling (un-acked) commit for the remote party, then we @@ -1722,7 +1724,7 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( logUpdate := logUpdate payDesc, err := lc.remoteLogUpdateToPayDesc( - &logUpdate, lc.localUpdateLog, localCommitmentHeight, + &logUpdate, lc.updateLogs.Local, localCommitmentHeight, ) if err != nil { return err @@ -1732,7 +1734,7 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( // Sanity check that we are not restoring a remote log update // that we haven't received a sig for. - if logIdx >= lc.remoteUpdateLog.logIndex { + if logIdx >= lc.updateLogs.Remote.logIndex { return fmt.Errorf("attempted to restore an "+ "unsigned remote update: log_index=%v", logIdx) @@ -1773,15 +1775,17 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( payDesc.removeCommitHeightRemote = height } - lc.remoteUpdateLog.restoreUpdate(payDesc) + lc.updateLogs.Remote.restoreUpdate(payDesc) default: if heightSet { payDesc.removeCommitHeightRemote = height } - lc.remoteUpdateLog.restoreUpdate(payDesc) - lc.localUpdateLog.markHtlcModified(payDesc.ParentIndex) + lc.updateLogs.Remote.restoreUpdate(payDesc) + lc.updateLogs.Local.markHtlcModified( + payDesc.ParentIndex, + ) } } @@ -1800,20 +1804,23 @@ func (lc *LightningChannel) restorePeerLocalUpdates(updates []channeldb.LogUpdat logUpdate := logUpdate payDesc, err := lc.localLogUpdateToPayDesc( - &logUpdate, lc.remoteUpdateLog, remoteCommitmentHeight, + &logUpdate, lc.updateLogs.Remote, + remoteCommitmentHeight, ) if err != nil { return err } - lc.localUpdateLog.restoreUpdate(payDesc) + lc.updateLogs.Local.restoreUpdate(payDesc) // Since Add updates are not stored and FeeUpdates don't have a // corresponding entry in the remote update log, we only need to // mark the htlc as modified if the update was Settle, Fail, or // MalformedFail. if payDesc.EntryType != FeeUpdate { - lc.remoteUpdateLog.markHtlcModified(payDesc.ParentIndex) + lc.updateLogs.Remote.markHtlcModified( + payDesc.ParentIndex, + ) } } @@ -1848,7 +1855,7 @@ func (lc *LightningChannel) restorePendingLocalUpdates( logUpdate := logUpdate payDesc, err := lc.logUpdateToPayDesc( - &logUpdate, lc.remoteUpdateLog, pendingHeight, + &logUpdate, lc.updateLogs.Remote, pendingHeight, chainfee.SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys, lc.channelState.RemoteChanCfg.DustLimit, @@ -1862,9 +1869,9 @@ func (lc *LightningChannel) restorePendingLocalUpdates( // updates, so they will be unset. To account for this we set // them to to current update log index. if payDesc.EntryType == FeeUpdate && payDesc.LogIndex == 0 && - lc.localUpdateLog.logIndex > 0 { + lc.updateLogs.Local.logIndex > 0 { - payDesc.LogIndex = lc.localUpdateLog.logIndex + payDesc.LogIndex = lc.updateLogs.Local.logIndex lc.log.Debugf("Found FeeUpdate on "+ "pendingRemoteCommitDiff without logIndex, "+ "using %v", payDesc.LogIndex) @@ -1872,10 +1879,10 @@ func (lc *LightningChannel) restorePendingLocalUpdates( // At this point the restored update's logIndex must be equal // to the update log, otherwise something is horribly wrong. - if payDesc.LogIndex != lc.localUpdateLog.logIndex { + if payDesc.LogIndex != lc.updateLogs.Local.logIndex { panic(fmt.Sprintf("log index mismatch: "+ "%v vs %v", payDesc.LogIndex, - lc.localUpdateLog.logIndex)) + lc.updateLogs.Local.logIndex)) } switch payDesc.EntryType { @@ -1885,21 +1892,25 @@ func (lc *LightningChannel) restorePendingLocalUpdates( // panic to catch this. // TODO(halseth): remove when cause of htlc entry bug // is found. - if payDesc.HtlcIndex != lc.localUpdateLog.htlcCounter { + if payDesc.HtlcIndex != + lc.updateLogs.Local.htlcCounter { + panic(fmt.Sprintf("htlc index mismatch: "+ "%v vs %v", payDesc.HtlcIndex, - lc.localUpdateLog.htlcCounter)) + lc.updateLogs.Local.htlcCounter)) } - lc.localUpdateLog.appendHtlc(payDesc) + lc.updateLogs.Local.appendHtlc(payDesc) case FeeUpdate: - lc.localUpdateLog.appendUpdate(payDesc) + lc.updateLogs.Local.appendUpdate(payDesc) default: - lc.localUpdateLog.appendUpdate(payDesc) + lc.updateLogs.Local.appendUpdate(payDesc) - lc.remoteUpdateLog.markHtlcModified(payDesc.ParentIndex) + lc.updateLogs.Remote.markHtlcModified( + payDesc.ParentIndex, + ) } } @@ -2617,7 +2628,7 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, ourLogIndex uint64) *HtlcView { var ourHTLCs []*PaymentDescriptor - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { htlc := e.Value // This HTLC is active from this point-of-view iff the log @@ -2629,7 +2640,7 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, } var theirHTLCs []*PaymentDescriptor - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { htlc := e.Value // If this is an incoming HTLC, then it is only active from @@ -2953,10 +2964,10 @@ func (lc *LightningChannel) fetchParent(entry *PaymentDescriptor, ) if whoseUpdateLog.IsRemote() { - updateLog = lc.remoteUpdateLog + updateLog = lc.updateLogs.Remote logName = "remote" } else { - updateLog = lc.localUpdateLog + updateLog = lc.updateLogs.Local logName = "local" } @@ -3354,7 +3365,7 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, // were only just committed within this pending state. This will be the // set of items we need to retransmit if we reconnect and find that // they didn't process this new state fully. - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { pd := e.Value // If this entry wasn't committed at the exact height of this @@ -3492,7 +3503,7 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { // restore if we reconnect in order to produce the signature that the // remote party expects. var logUpdates []channeldb.LogUpdate - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { pd := e.Value // Skip all remote updates that we have already included in our @@ -3982,7 +3993,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // point all updates will have to get locked-in so we enforce the // minimum requirement. err := lc.validateCommitmentSanity( - remoteACKedIndex, lc.localUpdateLog.logIndex, lntypes.Remote, + remoteACKedIndex, lc.updateLogs.Local.logIndex, lntypes.Remote, NoBuffer, nil, nil, ) if err != nil { @@ -4005,8 +4016,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // _all_ of our changes (pending or committed) but only the remote // node's changes up to the last change we've ACK'd. newCommitView, err := lc.fetchCommitmentView( - lntypes.Remote, lc.localUpdateLog.logIndex, - lc.localUpdateLog.htlcCounter, remoteACKedIndex, + lntypes.Remote, lc.updateLogs.Local.logIndex, + lc.updateLogs.Local.htlcCounter, remoteACKedIndex, remoteHtlcIndex, keyRing, ) if err != nil { @@ -4016,7 +4027,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { lc.log.Tracef("extending remote chain to height %v, "+ "local_log=%v, remote_log=%v", newCommitView.height, - lc.localUpdateLog.logIndex, remoteACKedIndex) + lc.updateLogs.Local.logIndex, remoteACKedIndex) lc.log.Tracef("remote chain: our_balance=%v, "+ "their_balance=%v, commit_tx: %v", @@ -4983,7 +4994,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { // the UpdateAddHTLC msg from our peer prior to receiving the // commit-sig). err := lc.validateCommitmentSanity( - lc.remoteUpdateLog.logIndex, localACKedIndex, lntypes.Local, + lc.updateLogs.Remote.logIndex, localACKedIndex, lntypes.Local, NoBuffer, nil, nil, ) if err != nil { @@ -5011,7 +5022,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { // up to the last change the remote node has ACK'd. localCommitmentView, err := lc.fetchCommitmentView( lntypes.Local, localACKedIndex, localHtlcIndex, - lc.remoteUpdateLog.logIndex, lc.remoteUpdateLog.htlcCounter, + lc.updateLogs.Remote.logIndex, lc.updateLogs.Remote.htlcCounter, keyRing, ) if err != nil { @@ -5021,7 +5032,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { lc.log.Tracef("extending local chain to height %v, "+ "local_log=%v, remote_log=%v", localCommitmentView.height, - localACKedIndex, lc.remoteUpdateLog.logIndex) + localACKedIndex, lc.updateLogs.Remote.logIndex) lc.log.Tracef("local chain: our_balance=%v, "+ "their_balance=%v, commit_tx: %v", @@ -5175,7 +5186,11 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } var txBytes bytes.Buffer - localCommitTx.Serialize(&txBytes) + err = localCommitTx.Serialize(&txBytes) + if err != nil { + return err + } + return &InvalidHtlcSigError{ commitHeight: nextHeight, htlcSig: sig.ToSignatureBytes(), @@ -5297,7 +5312,7 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // There are local updates pending if our local update log is // not in sync with our remote commitment tx. - localUpdatesPending = lc.localUpdateLog.logIndex != + localUpdatesPending = lc.updateLogs.Local.logIndex != lastRemoteCommit.ourMessageIndex // There are remote updates pending if their remote commitment @@ -5312,7 +5327,7 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // perspective of the remote party) if the remote party has // updates to their remote tx pending for which they haven't // signed yet. - localUpdatesPending = lc.remoteUpdateLog.logIndex != + localUpdatesPending = lc.updateLogs.Remote.logIndex != lastLocalCommit.theirMessageIndex // There are remote updates pending (remote updates from the @@ -5341,7 +5356,7 @@ func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { lastRemoteCommit := lc.commitChains.Remote.tip() - return lc.localUpdateLog.logIndex - lastRemoteCommit.ourMessageIndex + return lc.updateLogs.Local.logIndex - lastRemoteCommit.ourMessageIndex } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment @@ -5477,7 +5492,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( ) var addIndex, settleFailIndex uint16 - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { pd := e.Value // Fee updates are local to this particular channel, and should @@ -5668,7 +5683,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // can remove any entries from the update log which have been removed // from the PoV of both commitment chains. compactLogs( - lc.localUpdateLog, lc.remoteUpdateLog, localChainTail, + lc.updateLogs.Local, lc.updateLogs.Remote, localChainTail, remoteChainTail, ) @@ -5774,7 +5789,7 @@ func (lc *LightningChannel) addHTLC(htlc *lnwire.UpdateAddHTLC, return 0, err } - lc.localUpdateLog.appendHtlc(pd) + lc.updateLogs.Local.appendHtlc(pd) return pd.HtlcIndex, nil } @@ -5807,7 +5822,7 @@ func (lc *LightningChannel) GetDustSum(whoseCommit lntypes.ChannelParty, feeRate = dryRunFee.UnwrapOr(feeRate) // Grab all of our HTLCs and evaluate against the dust limit. - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { pd := e.Value if pd.EntryType != Add { continue @@ -5826,7 +5841,7 @@ func (lc *LightningChannel) GetDustSum(whoseCommit lntypes.ChannelParty, } // Grab all of their HTLCs and evaluate against the dust limit. - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { pd := e.Value if pd.EntryType != Add { continue @@ -5906,8 +5921,8 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, RHash: PaymentHash(htlc.PaymentHash), Timeout: htlc.Expiry, Amount: htlc.Amount, - LogIndex: lc.localUpdateLog.logIndex, - HtlcIndex: lc.localUpdateLog.htlcCounter, + LogIndex: lc.updateLogs.Local.logIndex, + HtlcIndex: lc.updateLogs.Local.htlcCounter, OnionBlob: htlc.OnionBlob[:], OpenCircuitKey: openKey, BlindingPoint: htlc.BlindingPoint, @@ -5928,7 +5943,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, // First we'll check whether this HTLC can be added to the remote // commitment transaction without violation any of the constraints. err := lc.validateCommitmentSanity( - remoteACKedIndex, lc.localUpdateLog.logIndex, lntypes.Remote, + remoteACKedIndex, lc.updateLogs.Local.logIndex, lntypes.Remote, buffer, pd, nil, ) if err != nil { @@ -5941,7 +5956,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, // concurrently, but if we fail this check there is for sure not // possible for us to add the HTLC. err = lc.validateCommitmentSanity( - lc.remoteUpdateLog.logIndex, lc.localUpdateLog.logIndex, + lc.updateLogs.Remote.logIndex, lc.updateLogs.Local.logIndex, lntypes.Local, buffer, pd, nil, ) if err != nil { @@ -5960,9 +5975,9 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, lc.Lock() defer lc.Unlock() - if htlc.ID != lc.remoteUpdateLog.htlcCounter { + if htlc.ID != lc.updateLogs.Remote.htlcCounter { return 0, fmt.Errorf("ID %d on HTLC add does not match expected next "+ - "ID %d", htlc.ID, lc.remoteUpdateLog.htlcCounter) + "ID %d", htlc.ID, lc.updateLogs.Remote.htlcCounter) } pd := &PaymentDescriptor{ @@ -5970,8 +5985,8 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, RHash: PaymentHash(htlc.PaymentHash), Timeout: htlc.Expiry, Amount: htlc.Amount, - LogIndex: lc.remoteUpdateLog.logIndex, - HtlcIndex: lc.remoteUpdateLog.htlcCounter, + LogIndex: lc.updateLogs.Remote.logIndex, + HtlcIndex: lc.updateLogs.Remote.htlcCounter, OnionBlob: htlc.OnionBlob[:], BlindingPoint: htlc.BlindingPoint, // TODO(guggero): Add custom records from HTLC here once we have @@ -5988,14 +6003,14 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // we use it here. The current lightning protocol does not allow to // reject ADDs already sent by the peer. err := lc.validateCommitmentSanity( - lc.remoteUpdateLog.logIndex, localACKedIndex, lntypes.Local, + lc.updateLogs.Remote.logIndex, localACKedIndex, lntypes.Local, NoBuffer, nil, pd, ) if err != nil { return 0, err } - lc.remoteUpdateLog.appendHtlc(pd) + lc.updateLogs.Remote.appendHtlc(pd) return pd.HtlcIndex, nil } @@ -6031,7 +6046,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, lc.Lock() defer lc.Unlock() - htlc := lc.remoteUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Remote.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } @@ -6039,7 +6054,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, // Now that we know the HTLC exists, before checking to see if the // preimage matches, we'll ensure that we haven't already attempted to // modify the HTLC. - if lc.remoteUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Remote.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadySettled(htlcIndex) } @@ -6050,7 +6065,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, pd := &PaymentDescriptor{ Amount: htlc.Amount, RPreimage: preimage, - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, ParentIndex: htlcIndex, EntryType: Settle, SourceRef: sourceRef, @@ -6058,12 +6073,12 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, ClosedCircuitKey: closeKey, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) // With the settle added to our local log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate settle. - lc.remoteUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Remote.markHtlcModified(htlcIndex) return nil } @@ -6076,7 +6091,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 lc.Lock() defer lc.Unlock() - htlc := lc.localUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Local.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } @@ -6084,7 +6099,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 // Now that we know the HTLC exists, before checking to see if the // preimage matches, we'll ensure that they haven't already attempted // to modify the HTLC. - if lc.localUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Local.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadySettled(htlcIndex) } @@ -6097,16 +6112,16 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 RPreimage: preimage, ParentIndex: htlc.HtlcIndex, RHash: htlc.RHash, - LogIndex: lc.remoteUpdateLog.logIndex, + LogIndex: lc.updateLogs.Remote.logIndex, EntryType: Settle, } - lc.remoteUpdateLog.appendUpdate(pd) + lc.updateLogs.Remote.appendUpdate(pd) // With the settle added to the remote log, we'll now mark the HTLC as // modified to prevent the remote party from accidentally attempting a // duplicate settle. - lc.localUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Local.markHtlcModified(htlcIndex) return nil } @@ -6142,14 +6157,14 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, lc.Lock() defer lc.Unlock() - htlc := lc.remoteUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Remote.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } // Now that we know the HTLC exists, we'll ensure that we haven't // already attempted to fail the HTLC. - if lc.remoteUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Remote.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadyFailed(htlcIndex) } @@ -6157,7 +6172,7 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlcIndex, - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, EntryType: Fail, FailReason: reason, SourceRef: sourceRef, @@ -6165,12 +6180,12 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, ClosedCircuitKey: closeKey, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) // With the fail added to the remote log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate fail. - lc.remoteUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Remote.markHtlcModified(htlcIndex) return nil } @@ -6192,14 +6207,14 @@ func (lc *LightningChannel) MalformedFailHTLC(htlcIndex uint64, lc.Lock() defer lc.Unlock() - htlc := lc.remoteUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Remote.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } // Now that we know the HTLC exists, we'll ensure that we haven't // already attempted to fail the HTLC. - if lc.remoteUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Remote.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadyFailed(htlcIndex) } @@ -6207,19 +6222,19 @@ func (lc *LightningChannel) MalformedFailHTLC(htlcIndex uint64, Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlcIndex, - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, EntryType: MalformedFail, FailCode: failCode, ShaOnionBlob: shaOnionBlob, SourceRef: sourceRef, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) // With the fail added to the remote log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate fail. - lc.remoteUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Remote.markHtlcModified(htlcIndex) return nil } @@ -6234,14 +6249,14 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64, reason []byte, lc.Lock() defer lc.Unlock() - htlc := lc.localUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Local.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } // Now that we know the HTLC exists, we'll ensure that they haven't // already attempted to fail the HTLC. - if lc.localUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Local.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadyFailed(htlcIndex) } @@ -6249,17 +6264,17 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64, reason []byte, Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlc.HtlcIndex, - LogIndex: lc.remoteUpdateLog.logIndex, + LogIndex: lc.updateLogs.Remote.logIndex, EntryType: Fail, FailReason: reason, } - lc.remoteUpdateLog.appendUpdate(pd) + lc.updateLogs.Remote.appendUpdate(pd) // With the fail added to the remote log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate fail. - lc.localUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Local.markHtlcModified(htlcIndex) return nil } @@ -8127,7 +8142,7 @@ func (lc *LightningChannel) availableBalance( // ACKed. remoteACKedIndex := lc.commitChains.Local.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, - lc.localUpdateLog.logIndex) + lc.updateLogs.Local.logIndex) // Calculate our available balance from our local commitment. // TODO(halseth): could reuse parts validateCommitmentSanity to do this @@ -8359,12 +8374,12 @@ func (lc *LightningChannel) UpdateFee(feePerKw chainfee.SatPerKWeight) error { } pd := &PaymentDescriptor{ - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), EntryType: FeeUpdate, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) return nil } @@ -8383,8 +8398,8 @@ func (lc *LightningChannel) CommitFeeTotalAt( // We want to grab every update in both update logs to calculate the // commitment fees in the worst-case with this fee-rate. - localIdx := lc.localUpdateLog.logIndex - remoteIdx := lc.remoteUpdateLog.logIndex + localIdx := lc.updateLogs.Local.logIndex + remoteIdx := lc.updateLogs.Remote.logIndex localHtlcView := lc.fetchHTLCView(remoteIdx, localIdx) @@ -8431,12 +8446,12 @@ func (lc *LightningChannel) ReceiveUpdateFee(feePerKw chainfee.SatPerKWeight) er // TODO(roasbeef): or just modify to use the other balance? pd := &PaymentDescriptor{ - LogIndex: lc.remoteUpdateLog.logIndex, + LogIndex: lc.updateLogs.Remote.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), EntryType: FeeUpdate, } - lc.remoteUpdateLog.appendUpdate(pd) + lc.updateLogs.Remote.appendUpdate(pd) return nil } @@ -8905,7 +8920,7 @@ func (lc *LightningChannel) unsignedLocalUpdates(remoteMessageIndex, localMessageIndex uint64, chanID lnwire.ChannelID) []channeldb.LogUpdate { var localPeerUpdates []channeldb.LogUpdate - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { pd := e.Value // We don't save add updates as they are restored from the diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 3b6ee77de..9a016e0f0 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -335,21 +335,23 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // The logs of both sides should now be cleared since the entry adding // the HTLC should have been removed once both sides receive the // revocation. - if aliceChannel.localUpdateLog.Len() != 0 { + if aliceChannel.updateLogs.Local.Len() != 0 { t.Fatalf("alice's local not updated, should be empty, has %v "+ - "entries instead", aliceChannel.localUpdateLog.Len()) + "entries instead", aliceChannel.updateLogs.Local.Len()) } - if aliceChannel.remoteUpdateLog.Len() != 0 { + if aliceChannel.updateLogs.Remote.Len() != 0 { t.Fatalf("alice's remote not updated, should be empty, has %v "+ - "entries instead", aliceChannel.remoteUpdateLog.Len()) + "entries instead", aliceChannel.updateLogs.Remote.Len()) } - if len(aliceChannel.localUpdateLog.updateIndex) != 0 { - t.Fatalf("alice's local log index not cleared, should be empty but "+ - "has %v entries", len(aliceChannel.localUpdateLog.updateIndex)) + if len(aliceChannel.updateLogs.Local.updateIndex) != 0 { + t.Fatalf("alice's local log index not cleared, should be "+ + "empty but has %v entries", + len(aliceChannel.updateLogs.Local.updateIndex)) } - if len(aliceChannel.remoteUpdateLog.updateIndex) != 0 { - t.Fatalf("alice's remote log index not cleared, should be empty but "+ - "has %v entries", len(aliceChannel.remoteUpdateLog.updateIndex)) + if len(aliceChannel.updateLogs.Remote.updateIndex) != 0 { + t.Fatalf("alice's remote log index not cleared, should be "+ + "empty but has %v entries", + len(aliceChannel.updateLogs.Remote.updateIndex)) } } @@ -1773,26 +1775,26 @@ func TestStateUpdatePersistence(t *testing.T) { // Helper method that asserts the expected number of updates are found // in the update logs. assertNumLogUpdates := func(numAliceUpdates, numBobUpdates int) { - if aliceChannel.localUpdateLog.Len() != numAliceUpdates { + if aliceChannel.updateLogs.Local.Len() != numAliceUpdates { t.Fatalf("expected %d local updates, found %d", numAliceUpdates, - aliceChannel.localUpdateLog.Len()) + aliceChannel.updateLogs.Local.Len()) } - if aliceChannel.remoteUpdateLog.Len() != numBobUpdates { + if aliceChannel.updateLogs.Remote.Len() != numBobUpdates { t.Fatalf("expected %d remote updates, found %d", numBobUpdates, - aliceChannel.remoteUpdateLog.Len()) + aliceChannel.updateLogs.Remote.Len()) } - if bobChannel.localUpdateLog.Len() != numBobUpdates { + if bobChannel.updateLogs.Local.Len() != numBobUpdates { t.Fatalf("expected %d local updates, found %d", numBobUpdates, - bobChannel.localUpdateLog.Len()) + bobChannel.updateLogs.Local.Len()) } - if bobChannel.remoteUpdateLog.Len() != numAliceUpdates { + if bobChannel.updateLogs.Remote.Len() != numAliceUpdates { t.Fatalf("expected %d remote updates, found %d", numAliceUpdates, - bobChannel.remoteUpdateLog.Len()) + bobChannel.updateLogs.Remote.Len()) } } @@ -1868,62 +1870,72 @@ func TestStateUpdatePersistence(t *testing.T) { // The state update logs of the new channels and the old channels // should now be identical other than the height the HTLCs were added. - if aliceChannel.localUpdateLog.logIndex != - aliceChannelNew.localUpdateLog.logIndex { + if aliceChannel.updateLogs.Local.logIndex != + aliceChannelNew.updateLogs.Local.logIndex { + t.Fatalf("alice log counter: expected %v, got %v", - aliceChannel.localUpdateLog.logIndex, - aliceChannelNew.localUpdateLog.logIndex) + aliceChannel.updateLogs.Local.logIndex, + aliceChannelNew.updateLogs.Local.logIndex) } - if aliceChannel.remoteUpdateLog.logIndex != - aliceChannelNew.remoteUpdateLog.logIndex { + if aliceChannel.updateLogs.Remote.logIndex != + aliceChannelNew.updateLogs.Remote.logIndex { + t.Fatalf("alice log counter: expected %v, got %v", - aliceChannel.remoteUpdateLog.logIndex, - aliceChannelNew.remoteUpdateLog.logIndex) + aliceChannel.updateLogs.Remote.logIndex, + aliceChannelNew.updateLogs.Remote.logIndex) } - if aliceChannel.localUpdateLog.Len() != - aliceChannelNew.localUpdateLog.Len() { + if aliceChannel.updateLogs.Local.Len() != + aliceChannelNew.updateLogs.Local.Len() { + t.Fatalf("alice log len: expected %v, got %v", - aliceChannel.localUpdateLog.Len(), - aliceChannelNew.localUpdateLog.Len()) + aliceChannel.updateLogs.Local.Len(), + aliceChannelNew.updateLogs.Local.Len()) } - if aliceChannel.remoteUpdateLog.Len() != - aliceChannelNew.remoteUpdateLog.Len() { + if aliceChannel.updateLogs.Remote.Len() != + aliceChannelNew.updateLogs.Remote.Len() { + t.Fatalf("alice log len: expected %v, got %v", - aliceChannel.remoteUpdateLog.Len(), - aliceChannelNew.remoteUpdateLog.Len()) + aliceChannel.updateLogs.Remote.Len(), + aliceChannelNew.updateLogs.Remote.Len()) } - if bobChannel.localUpdateLog.logIndex != - bobChannelNew.localUpdateLog.logIndex { + if bobChannel.updateLogs.Local.logIndex != + bobChannelNew.updateLogs.Local.logIndex { + t.Fatalf("bob log counter: expected %v, got %v", - bobChannel.localUpdateLog.logIndex, - bobChannelNew.localUpdateLog.logIndex) + bobChannel.updateLogs.Local.logIndex, + bobChannelNew.updateLogs.Local.logIndex) } - if bobChannel.remoteUpdateLog.logIndex != - bobChannelNew.remoteUpdateLog.logIndex { + if bobChannel.updateLogs.Remote.logIndex != + bobChannelNew.updateLogs.Remote.logIndex { + t.Fatalf("bob log counter: expected %v, got %v", - bobChannel.remoteUpdateLog.logIndex, - bobChannelNew.remoteUpdateLog.logIndex) + bobChannel.updateLogs.Remote.logIndex, + bobChannelNew.updateLogs.Remote.logIndex) } - if bobChannel.localUpdateLog.Len() != - bobChannelNew.localUpdateLog.Len() { + if bobChannel.updateLogs.Local.Len() != + bobChannelNew.updateLogs.Local.Len() { + t.Fatalf("bob log len: expected %v, got %v", - bobChannel.localUpdateLog.Len(), - bobChannelNew.localUpdateLog.Len()) + bobChannel.updateLogs.Local.Len(), + bobChannelNew.updateLogs.Local.Len()) } - if bobChannel.remoteUpdateLog.Len() != - bobChannelNew.remoteUpdateLog.Len() { + if bobChannel.updateLogs.Remote.Len() != + bobChannelNew.updateLogs.Remote.Len() { + t.Fatalf("bob log len: expected %v, got %v", - bobChannel.remoteUpdateLog.Len(), - bobChannelNew.remoteUpdateLog.Len()) + bobChannel.updateLogs.Remote.Len(), + bobChannelNew.updateLogs.Remote.Len()) } // TODO(roasbeef): expand test to also ensure state revocation log has // proper pk scripts // Newly generated pkScripts for HTLCs should be the same as in the old channel. - for _, entry := range aliceChannel.localUpdateLog.htlcIndex { + for _, entry := range aliceChannel.updateLogs.Local.htlcIndex { htlc := entry.Value - restoredHtlc := aliceChannelNew.localUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := aliceChannelNew.updateLogs.Local.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("alice ourPkScript in ourLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -1933,9 +1945,11 @@ func TestStateUpdatePersistence(t *testing.T) { htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5]) } } - for _, entry := range aliceChannel.remoteUpdateLog.htlcIndex { + for _, entry := range aliceChannel.updateLogs.Remote.htlcIndex { htlc := entry.Value - restoredHtlc := aliceChannelNew.remoteUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := aliceChannelNew.updateLogs.Remote.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("alice ourPkScript in theirLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -1945,9 +1959,11 @@ func TestStateUpdatePersistence(t *testing.T) { htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5]) } } - for _, entry := range bobChannel.localUpdateLog.htlcIndex { + for _, entry := range bobChannel.updateLogs.Local.htlcIndex { htlc := entry.Value - restoredHtlc := bobChannelNew.localUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := bobChannelNew.updateLogs.Local.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("bob ourPkScript in ourLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -1957,9 +1973,11 @@ func TestStateUpdatePersistence(t *testing.T) { htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5]) } } - for _, entry := range bobChannel.remoteUpdateLog.htlcIndex { + for _, entry := range bobChannel.updateLogs.Remote.htlcIndex { htlc := entry.Value - restoredHtlc := bobChannelNew.remoteUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := bobChannelNew.updateLogs.Remote.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("bob ourPkScript in theirLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -3245,39 +3263,39 @@ func TestChanSyncOweCommitment(t *testing.T) { // Alice's local log counter should be 4 and her HTLC index 3. She // should detect Bob's remote log counter as being 3 and his HTLC index // 3 as well. - if aliceChannel.localUpdateLog.logIndex != 4 { + if aliceChannel.updateLogs.Local.logIndex != 4 { t.Fatalf("incorrect log index: expected %v, got %v", 4, - aliceChannel.localUpdateLog.logIndex) + aliceChannel.updateLogs.Local.logIndex) } - if aliceChannel.localUpdateLog.htlcCounter != 1 { + if aliceChannel.updateLogs.Local.htlcCounter != 1 { t.Fatalf("incorrect htlc index: expected %v, got %v", 1, - aliceChannel.localUpdateLog.htlcCounter) + aliceChannel.updateLogs.Local.htlcCounter) } - if aliceChannel.remoteUpdateLog.logIndex != 3 { + if aliceChannel.updateLogs.Remote.logIndex != 3 { t.Fatalf("incorrect log index: expected %v, got %v", 3, - aliceChannel.localUpdateLog.logIndex) + aliceChannel.updateLogs.Local.logIndex) } - if aliceChannel.remoteUpdateLog.htlcCounter != 3 { + if aliceChannel.updateLogs.Remote.htlcCounter != 3 { t.Fatalf("incorrect htlc index: expected %v, got %v", 3, - aliceChannel.localUpdateLog.htlcCounter) + aliceChannel.updateLogs.Local.htlcCounter) } // Bob should also have the same state, but mirrored. - if bobChannel.localUpdateLog.logIndex != 3 { + if bobChannel.updateLogs.Local.logIndex != 3 { t.Fatalf("incorrect log index: expected %v, got %v", 3, - bobChannel.localUpdateLog.logIndex) + bobChannel.updateLogs.Local.logIndex) } - if bobChannel.localUpdateLog.htlcCounter != 3 { + if bobChannel.updateLogs.Local.htlcCounter != 3 { t.Fatalf("incorrect htlc index: expected %v, got %v", 3, - bobChannel.localUpdateLog.htlcCounter) + bobChannel.updateLogs.Local.htlcCounter) } - if bobChannel.remoteUpdateLog.logIndex != 4 { + if bobChannel.updateLogs.Remote.logIndex != 4 { t.Fatalf("incorrect log index: expected %v, got %v", 4, - bobChannel.localUpdateLog.logIndex) + bobChannel.updateLogs.Local.logIndex) } - if bobChannel.remoteUpdateLog.htlcCounter != 1 { + if bobChannel.updateLogs.Remote.htlcCounter != 1 { t.Fatalf("incorrect htlc index: expected %v, got %v", 1, - bobChannel.localUpdateLog.htlcCounter) + bobChannel.updateLogs.Local.htlcCounter) } // We'll conclude the test by having Bob settle Alice's HTLC, then @@ -4507,7 +4525,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { t.Helper() expUpd := expFee + expAdd - upd, fees := countLog(aliceChannel.localUpdateLog) + upd, fees := countLog(aliceChannel.updateLogs.Local) if upd != expUpd { t.Fatalf("expected %d updates, found %d in Alice's "+ "log", expUpd, upd) @@ -4516,7 +4534,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { t.Fatalf("expected %d fee updates, found %d in "+ "Alice's log", expFee, fees) } - upd, fees = countLog(bobChannel.remoteUpdateLog) + upd, fees = countLog(bobChannel.updateLogs.Remote) if upd != expUpd { t.Fatalf("expected %d updates, found %d in Bob's log", expUpd, upd) @@ -5215,7 +5233,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { lc.commitChains.Local.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, - lc.localUpdateLog.logIndex) + lc.updateLogs.Local.logIndex) _, w := lc.availableCommitmentBalance( htlcView, lntypes.Remote, FeeBuffer, @@ -6922,20 +6940,20 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // compare all the logs between the old and new channels, to make sure // they all got restored properly. - err = compareLogs(aliceChannel.localUpdateLog, - newAliceChannel.localUpdateLog) + err = compareLogs(aliceChannel.updateLogs.Local, + newAliceChannel.updateLogs.Local) require.NoError(t, err, "alice local log not restored") - err = compareLogs(aliceChannel.remoteUpdateLog, - newAliceChannel.remoteUpdateLog) + err = compareLogs(aliceChannel.updateLogs.Remote, + newAliceChannel.updateLogs.Remote) require.NoError(t, err, "alice remote log not restored") - err = compareLogs(bobChannel.localUpdateLog, - newBobChannel.localUpdateLog) + err = compareLogs(bobChannel.updateLogs.Local, + newBobChannel.updateLogs.Local) require.NoError(t, err, "bob local log not restored") - err = compareLogs(bobChannel.remoteUpdateLog, - newBobChannel.remoteUpdateLog) + err = compareLogs(bobChannel.updateLogs.Remote, + newBobChannel.updateLogs.Remote) require.NoError(t, err, "bob remote log not restored") } @@ -6968,8 +6986,9 @@ func assertInLog(t *testing.T, log *updateLog, numAdds, numFails int) { // the local and remote update log of the given channel. func assertInLogs(t *testing.T, channel *LightningChannel, numAddsLocal, numFailsLocal, numAddsRemote, numFailsRemote int) { - assertInLog(t, channel.localUpdateLog, numAddsLocal, numFailsLocal) - assertInLog(t, channel.remoteUpdateLog, numAddsRemote, numFailsRemote) + + assertInLog(t, channel.updateLogs.Local, numAddsLocal, numFailsLocal) + assertInLog(t, channel.updateLogs.Remote, numAddsRemote, numFailsRemote) } // restoreAndAssert creates a new LightningChannel from the given channel's @@ -6984,8 +7003,10 @@ func restoreAndAssert(t *testing.T, channel *LightningChannel, numAddsLocal, ) require.NoError(t, err, "unable to create new channel") - assertInLog(t, newChannel.localUpdateLog, numAddsLocal, numFailsLocal) - assertInLog(t, newChannel.remoteUpdateLog, numAddsRemote, numFailsRemote) + assertInLog(t, newChannel.updateLogs.Local, numAddsLocal, numFailsLocal) + assertInLog( + t, newChannel.updateLogs.Remote, numAddsRemote, numFailsRemote, + ) } // TestChannelRestoreUpdateLogsFailedHTLC runs through a scenario where an @@ -7249,16 +7270,18 @@ func TestChannelRestoreCommitHeight(t *testing.T) { var pd *PaymentDescriptor if remoteLog { - if newChannel.localUpdateLog.lookupHtlc(htlcIndex) != nil { + h := newChannel.updateLogs.Local.lookupHtlc(htlcIndex) + if h != nil { t.Fatalf("htlc found in wrong log") } - pd = newChannel.remoteUpdateLog.lookupHtlc(htlcIndex) + pd = newChannel.updateLogs.Remote.lookupHtlc(htlcIndex) } else { - if newChannel.remoteUpdateLog.lookupHtlc(htlcIndex) != nil { + h := newChannel.updateLogs.Remote.lookupHtlc(htlcIndex) + if h != nil { t.Fatalf("htlc found in wrong log") } - pd = newChannel.localUpdateLog.lookupHtlc(htlcIndex) + pd = newChannel.updateLogs.Local.lookupHtlc(htlcIndex) } if pd == nil { t.Fatalf("htlc not found in log") @@ -8196,16 +8219,18 @@ func TestFetchParent(t *testing.T) { // Create a lightning channel with newly initialized // local and remote logs. lc := LightningChannel{ - localUpdateLog: newUpdateLog(0, 0), - remoteUpdateLog: newUpdateLog(0, 0), + updateLogs: lntypes.Dual[*updateLog]{ + Local: newUpdateLog(0, 0), + Remote: newUpdateLog(0, 0), + }, } // Add the local and remote entries to update logs. for _, entry := range test.localEntries { - lc.localUpdateLog.appendHtlc(entry) + lc.updateLogs.Local.appendHtlc(entry) } for _, entry := range test.remoteEntries { - lc.remoteUpdateLog.appendHtlc(entry) + lc.updateLogs.Remote.appendHtlc(entry) } parent, err := lc.fetchParent( @@ -8532,23 +8557,25 @@ func TestEvaluateView(t *testing.T) { }, // Create update logs for local and remote. - localUpdateLog: newUpdateLog(0, 0), - remoteUpdateLog: newUpdateLog(0, 0), + updateLogs: lntypes.Dual[*updateLog]{ + Local: newUpdateLog(0, 0), + Remote: newUpdateLog(0, 0), + }, } for _, htlc := range test.ourHtlcs { if htlc.EntryType == Add { - lc.localUpdateLog.appendHtlc(htlc) + lc.updateLogs.Local.appendHtlc(htlc) } else { - lc.localUpdateLog.appendUpdate(htlc) + lc.updateLogs.Local.appendUpdate(htlc) } } for _, htlc := range test.theirHtlcs { if htlc.EntryType == Add { - lc.remoteUpdateLog.appendHtlc(htlc) + lc.updateLogs.Remote.appendHtlc(htlc) } else { - lc.remoteUpdateLog.appendUpdate(htlc) + lc.updateLogs.Remote.appendUpdate(htlc) } } From ce7fcd30f8816ef69ea18532c97c280acace0f96 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 9 Aug 2024 14:52:21 -0700 Subject: [PATCH 4/5] lnwallet: pack commitment message indices into Dual This is yet another commit that packs a symmetric structure into a Dual. This is the last one needed for the time being to consolidate Num{X}UpdatesPendingOn{Y} functions into a single one. --- lnwallet/channel.go | 108 +++++++++++++++++++++------------------ lnwallet/channel_test.go | 2 +- 2 files changed, 60 insertions(+), 50 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index daac2b72c..ada7ec8fd 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -282,8 +282,7 @@ type commitment struct { // new commitment sent to the remote party includes an index in the // shared log which details which of their updates we're including in // this new commitment. - ourMessageIndex uint64 - theirMessageIndex uint64 + messageIndices lntypes.Dual[uint64] // [our|their]HtlcIndex are the current running counters for the HTLCs // offered by either party. This value is incremented each time a party @@ -500,9 +499,9 @@ func (c *commitment) toDiskCommit( commit := &channeldb.ChannelCommitment{ CommitHeight: c.height, - LocalLogIndex: c.ourMessageIndex, + LocalLogIndex: c.messageIndices.Local, LocalHtlcIndex: c.ourHtlcIndex, - RemoteLogIndex: c.theirMessageIndex, + RemoteLogIndex: c.messageIndices.Remote, RemoteHtlcIndex: c.theirHtlcIndex, LocalBalance: c.ourBalance, RemoteBalance: c.theirBalance, @@ -772,24 +771,28 @@ func (lc *LightningChannel) diskCommitToMemCommit( return nil, err } + messageIndices := lntypes.Dual[uint64]{ + Local: diskCommit.LocalLogIndex, + Remote: diskCommit.RemoteLogIndex, + } + // With the necessary items generated, we'll now re-construct the // commitment state as it was originally present in memory. commit := &commitment{ - height: diskCommit.CommitHeight, - whoseCommit: whoseCommit, - ourBalance: diskCommit.LocalBalance, - theirBalance: diskCommit.RemoteBalance, - ourMessageIndex: diskCommit.LocalLogIndex, - ourHtlcIndex: diskCommit.LocalHtlcIndex, - theirMessageIndex: diskCommit.RemoteLogIndex, - theirHtlcIndex: diskCommit.RemoteHtlcIndex, - txn: diskCommit.CommitTx, - sig: diskCommit.CommitSig, - fee: diskCommit.CommitFee, - feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw), - incomingHTLCs: incomingHtlcs, - outgoingHTLCs: outgoingHtlcs, - customBlob: diskCommit.CustomBlob, + height: diskCommit.CommitHeight, + whoseCommit: whoseCommit, + ourBalance: diskCommit.LocalBalance, + theirBalance: diskCommit.RemoteBalance, + messageIndices: messageIndices, + ourHtlcIndex: diskCommit.LocalHtlcIndex, + theirHtlcIndex: diskCommit.RemoteHtlcIndex, + txn: diskCommit.CommitTx, + sig: diskCommit.CommitSig, + fee: diskCommit.CommitFee, + feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw), + incomingHTLCs: incomingHtlcs, + outgoingHTLCs: outgoingHtlcs, + customBlob: diskCommit.CustomBlob, } if whoseCommit.IsLocal() { commit.dustLimit = lc.channelState.LocalChanCfg.DustLimit @@ -1758,7 +1761,7 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( // height as this commitment will include these updates for // their new remote commitment. if pendingRemoteCommit != nil { - if logIdx < pendingRemoteCommit.theirMessageIndex { + if logIdx < pendingRemoteCommit.messageIndices.Remote { height = pendingRemoteCommit.height heightSet = true } @@ -2751,22 +2754,26 @@ func (lc *LightningChannel) fetchCommitmentView( return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) } + messageIndices := lntypes.Dual[uint64]{ + Local: ourLogIndex, + Remote: theirLogIndex, + } + // With the commitment view created, store the resulting balances and // transaction with the other parameters for this height. c := &commitment{ - ourBalance: commitTx.ourBalance, - theirBalance: commitTx.theirBalance, - txn: commitTx.txn, - fee: commitTx.fee, - ourMessageIndex: ourLogIndex, - ourHtlcIndex: ourHtlcIndex, - theirMessageIndex: theirLogIndex, - theirHtlcIndex: theirHtlcIndex, - height: nextHeight, - feePerKw: feePerKw, - dustLimit: dustLimit, - whoseCommit: whoseCommitChain, - customBlob: newCommitBlob, + ourBalance: commitTx.ourBalance, + theirBalance: commitTx.theirBalance, + txn: commitTx.txn, + fee: commitTx.fee, + messageIndices: messageIndices, + ourHtlcIndex: ourHtlcIndex, + theirHtlcIndex: theirHtlcIndex, + height: nextHeight, + feePerKw: feePerKw, + dustLimit: dustLimit, + whoseCommit: whoseCommitChain, + customBlob: newCommitBlob, } // In order to ensure _none_ of the HTLC's associated with this new @@ -3493,10 +3500,12 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) // Fetch the last remote update that we have signed for. - lastRemoteCommitted := lc.commitChains.Remote.tail().theirMessageIndex + lastRemoteCommitted := + lc.commitChains.Remote.tail().messageIndices.Remote // Fetch the last remote update that we have acked. - lastLocalCommitted := lc.commitChains.Local.tail().theirMessageIndex + lastLocalCommitted := + lc.commitChains.Local.tail().messageIndices.Remote // We'll now run through the remote update log to locate the items that // we haven't signed for yet. This will be the set of items we need to @@ -3982,7 +3991,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } // Determine the last update on the remote log that has been locked in. - remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote remoteHtlcIndex := lc.commitChains.Local.tail().theirHtlcIndex // Before we extend this new commitment to the remote commitment chain, @@ -4982,7 +4991,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } // Determine the last update on the local log that has been locked in. - localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex + localACKedIndex := lc.commitChains.Remote.tail().messageIndices.Local localHtlcIndex := lc.commitChains.Remote.tail().ourHtlcIndex // Ensure that this new local update from the remote node respects all @@ -5313,13 +5322,13 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // There are local updates pending if our local update log is // not in sync with our remote commitment tx. localUpdatesPending = lc.updateLogs.Local.logIndex != - lastRemoteCommit.ourMessageIndex + lastRemoteCommit.messageIndices.Local // There are remote updates pending if their remote commitment // tx (our local commitment tx) contains updates that we don't // have added to our remote commitment tx yet. - remoteUpdatesPending = lastLocalCommit.theirMessageIndex != - lastRemoteCommit.theirMessageIndex + remoteUpdatesPending = lastLocalCommit.messageIndices.Remote != + lastRemoteCommit.messageIndices.Remote } else { perspective = "remote" @@ -5328,13 +5337,13 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // updates to their remote tx pending for which they haven't // signed yet. localUpdatesPending = lc.updateLogs.Remote.logIndex != - lastLocalCommit.theirMessageIndex + lastLocalCommit.messageIndices.Remote // There are remote updates pending (remote updates from the // perspective of the remote party) if we have updates on our // remote commitment tx that they haven't added to theirs yet. - remoteUpdatesPending = lastRemoteCommit.ourMessageIndex != - lastLocalCommit.ourMessageIndex + remoteUpdatesPending = lastRemoteCommit.messageIndices.Local != + lastLocalCommit.messageIndices.Local } // If any of the conditions above is true, we owe a commitment @@ -5356,7 +5365,8 @@ func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { lastRemoteCommit := lc.commitChains.Remote.tip() - return lc.updateLogs.Local.logIndex - lastRemoteCommit.ourMessageIndex + return lc.updateLogs.Local.logIndex - + lastRemoteCommit.messageIndices.Local } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment @@ -5615,8 +5625,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // We use the remote commitment chain's tip as it will soon become the tail // once advanceTail is called. - remoteMessageIndex := lc.commitChains.Remote.tip().ourMessageIndex - localMessageIndex := lc.commitChains.Local.tail().ourMessageIndex + remoteMessageIndex := lc.commitChains.Remote.tip().messageIndices.Local + localMessageIndex := lc.commitChains.Local.tail().messageIndices.Local localPeerUpdates := lc.unsignedLocalUpdates( remoteMessageIndex, localMessageIndex, chanID, @@ -5938,7 +5948,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, buffer BufferType) error { // Make sure adding this HTLC won't violate any of the constraints we // must keep on the commitment transactions. - remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote // First we'll check whether this HTLC can be added to the remote // commitment transaction without violation any of the constraints. @@ -5994,7 +6004,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // PR). } - localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex + localACKedIndex := lc.commitChains.Remote.tail().messageIndices.Local // Clamp down on the number of HTLC's we can receive by checking the // commitment sanity. @@ -8140,7 +8150,7 @@ func (lc *LightningChannel) availableBalance( // We'll grab the current set of log updates that the remote has // ACKed. - remoteACKedIndex := lc.commitChains.Local.tip().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tip().messageIndices.Remote htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.updateLogs.Local.logIndex) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 9a016e0f0..f39dd2d43 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -5230,7 +5230,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // transaction. remoteCommitWeight := func(lc *LightningChannel) lntypes.WeightUnit { remoteACKedIndex := - lc.commitChains.Local.tip().theirMessageIndex + lc.commitChains.Local.tip().messageIndices.Remote htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.updateLogs.Local.logIndex) From 1422729f80695b81ac8c3684a7e3f0e6d180830f Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Mon, 11 Dec 2023 19:18:57 -0800 Subject: [PATCH 5/5] lnwallet+htlcswitch: define expanded NumPendingUpdates This commit squashes the below operations for a net result where we have an expanded capability of assessing pending updates. This is made possible by packing the components into Duals in the prior commits. We squash the operations to simplify review. htlcswitch+lnwallet: rename PendingLocalUpdateCount lnwallet: complete pending update queries API for LightningChannel lnwallet+htlcswitch: consolidate NumPendingUpdates using ChannelParty This commit makes the observation that we can cleanly define the NumPendingUpdates function using a single expression by taking advantage of the relevant fields being properly packed into Duals. --- htlcswitch/link.go | 24 ++++++++++++++---------- lnwallet/channel.go | 14 ++++++++------ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index c459ef997..81c82cfa2 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -958,7 +958,7 @@ func (l *channelLink) resolveFwdPkgs() error { // If any of our reprocessing steps require an update to the commitment // txn, we initiate a state transition to capture all relevant changes. - if l.channel.PendingLocalUpdateCount() > 0 { + if l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) > 0 { return l.updateCommitTx() } @@ -1286,15 +1286,19 @@ func (l *channelLink) htlcManager() { // the batch ticker so that it can be cleared. Otherwise pause // the ticker to prevent waking up the htlcManager while the // batch is empty. - if l.channel.PendingLocalUpdateCount() > 0 { + numUpdates := l.channel.NumPendingUpdates( + lntypes.Local, lntypes.Remote, + ) + if numUpdates > 0 { l.cfg.BatchTicker.Resume() l.log.Tracef("BatchTicker resumed, "+ - "PendingLocalUpdateCount=%d", - l.channel.PendingLocalUpdateCount()) + "NumPendingUpdates(Local, Remote)=%d", + numUpdates, + ) } else { l.cfg.BatchTicker.Pause() l.log.Trace("BatchTicker paused due to zero " + - "PendingLocalUpdateCount") + "NumPendingUpdates(Local, Remote)") } select { @@ -1652,7 +1656,7 @@ func (l *channelLink) handleDownstreamUpdateAdd(pkt *htlcPacket) error { l.log.Tracef("received downstream htlc: payment_hash=%x, "+ "local_log_index=%v, pend_updates=%v", htlc.PaymentHash[:], index, - l.channel.PendingLocalUpdateCount()) + l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote)) pkt.outgoingChanID = l.ShortChanID() pkt.outgoingHTLCID = index @@ -1858,7 +1862,8 @@ func (l *channelLink) handleDownstreamPkt(pkt *htlcPacket) { // tryBatchUpdateCommitTx updates the commitment transaction if the batch is // full. func (l *channelLink) tryBatchUpdateCommitTx() { - if l.channel.PendingLocalUpdateCount() < uint64(l.cfg.BatchSize) { + pending := l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) + if pending < uint64(l.cfg.BatchSize) { return } @@ -1934,7 +1939,6 @@ func (l *channelLink) cleanupSpuriousResponse(pkt *htlcPacket) { // direct channel with, updating our respective commitment chains. func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { switch msg := msg.(type) { - case *lnwire.UpdateAddHTLC: if l.IsFlushing(Incoming) { // This is forbidden by the protocol specification. @@ -2592,9 +2596,9 @@ func (l *channelLink) updateCommitTx() error { l.cfg.PendingCommitTicker.Resume() l.log.Trace("PendingCommitTicker resumed") + n := l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) l.log.Tracef("revocation window exhausted, unable to send: "+ - "%v, pend_updates=%v, dangling_closes%v", - l.channel.PendingLocalUpdateCount(), + "%v, pend_updates=%v, dangling_closes%v", n, lnutils.SpewLogClosure(l.openedCircuits), lnutils.SpewLogClosure(l.closedCircuits)) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index ada7ec8fd..81930aad5 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -5357,16 +5357,18 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { return oweCommitment } -// PendingLocalUpdateCount returns the number of local updates that still need -// to be applied to the remote commitment tx. -func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { +// NumPendingUpdates returns the number of updates originated by whoseUpdates +// that have not been committed to the *tip* of whoseCommit's commitment chain. +func (lc *LightningChannel) NumPendingUpdates(whoseUpdates lntypes.ChannelParty, + whoseCommit lntypes.ChannelParty) uint64 { + lc.RLock() defer lc.RUnlock() - lastRemoteCommit := lc.commitChains.Remote.tip() + lastCommit := lc.commitChains.GetForParty(whoseCommit).tip() + updateIndex := lc.updateLogs.GetForParty(whoseUpdates).logIndex - return lc.updateLogs.Local.logIndex - - lastRemoteCommit.messageIndices.Local + return updateIndex - lastCommit.messageIndices.GetForParty(whoseUpdates) } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment