diff --git a/htlcswitch/link.go b/htlcswitch/link.go index ff140352c08..81c82cfa258 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() } @@ -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 } } @@ -1291,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 { @@ -1657,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 @@ -1863,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 } @@ -1939,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. @@ -2597,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 e5cbe6b6aac..81930aad593 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 @@ -858,15 +861,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 @@ -876,8 +877,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 @@ -982,6 +982,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) @@ -992,19 +996,22 @@ 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, ), - localUpdateLog: localUpdateLog, - remoteUpdateLog: remoteUpdateLog, + updateLogs: updateLogs, Capacity: state.Capacity, taprootNonceProducer: taprootNonceProducer, log: build.NewPrefixLog(logPrefix, walletLog), @@ -1463,10 +1470,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 +1483,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 +1515,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. @@ -1660,7 +1667,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 @@ -1675,7 +1682,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 @@ -1720,7 +1727,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 @@ -1730,7 +1737,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) @@ -1754,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 } @@ -1771,15 +1778,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, + ) } } @@ -1798,20 +1807,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, + ) } } @@ -1846,7 +1858,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, @@ -1860,9 +1872,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) @@ -1870,10 +1882,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 { @@ -1883,21 +1895,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, + ) } } @@ -2615,7 +2631,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 @@ -2627,7 +2643,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 @@ -2655,10 +2671,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 } @@ -2738,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 @@ -2951,10 +2971,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" } @@ -3352,7 +3372,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 @@ -3480,17 +3500,19 @@ 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().messageIndices.Remote // Fetch the last remote update that we have acked. - lastLocalCommitted := lc.localCommitChain.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 // 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 @@ -3709,9 +3731,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 +3983,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 +3991,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().messageIndices.Remote + 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 @@ -3980,7 +4002,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 { @@ -4003,8 +4025,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 { @@ -4014,7 +4036,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", @@ -4117,7 +4139,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 +4255,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 +4525,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 +4991,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().messageIndices.Local + 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, @@ -4981,7 +5003,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 { @@ -5009,7 +5031,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 { @@ -5019,7 +5041,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", @@ -5173,7 +5195,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(), @@ -5207,7 +5233,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 +5251,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 +5310,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 ) @@ -5295,14 +5321,14 @@ 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 != - lastRemoteCommit.ourMessageIndex + localUpdatesPending = lc.updateLogs.Local.logIndex != + 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" @@ -5310,14 +5336,14 @@ 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 != - lastLocalCommit.theirMessageIndex + localUpdatesPending = lc.updateLogs.Remote.logIndex != + 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 @@ -5331,15 +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.remoteCommitChain.tip() + lastCommit := lc.commitChains.GetForParty(whoseCommit).tip() + updateIndex := lc.updateLogs.GetForParty(whoseUpdates).logIndex - return lc.localUpdateLog.logIndex - lastRemoteCommit.ourMessageIndex + return updateIndex - lastCommit.messageIndices.GetForParty(whoseUpdates) } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment @@ -5362,16 +5391,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 +5480,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) @@ -5475,7 +5504,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 @@ -5598,8 +5627,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().messageIndices.Local + localMessageIndex := lc.commitChains.Local.tail().messageIndices.Local localPeerUpdates := lc.unsignedLocalUpdates( remoteMessageIndex, localMessageIndex, chanID, @@ -5660,13 +5689,13 @@ 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 // from the PoV of both commitment chains. compactLogs( - lc.localUpdateLog, lc.remoteUpdateLog, localChainTail, + lc.updateLogs.Local, lc.updateLogs.Remote, localChainTail, remoteChainTail, ) @@ -5772,7 +5801,7 @@ func (lc *LightningChannel) addHTLC(htlc *lnwire.UpdateAddHTLC, return 0, err } - lc.localUpdateLog.appendHtlc(pd) + lc.updateLogs.Local.appendHtlc(pd) return pd.HtlcIndex, nil } @@ -5805,7 +5834,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 @@ -5824,7 +5853,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 @@ -5904,8 +5933,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, @@ -5921,12 +5950,12 @@ 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().messageIndices.Remote // 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 { @@ -5939,7 +5968,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 { @@ -5958,9 +5987,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{ @@ -5968,8 +5997,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 @@ -5977,7 +6006,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // PR). } - localACKedIndex := lc.remoteCommitChain.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. @@ -5986,14 +6015,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 } @@ -6029,7 +6058,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} } @@ -6037,7 +6066,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) } @@ -6048,7 +6077,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, @@ -6056,12 +6085,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 } @@ -6074,7 +6103,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} } @@ -6082,7 +6111,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) } @@ -6095,16 +6124,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 } @@ -6140,14 +6169,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) } @@ -6155,7 +6184,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, @@ -6163,12 +6192,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 } @@ -6190,14 +6219,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) } @@ -6205,19 +6234,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 } @@ -6232,14 +6261,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) } @@ -6247,17 +6276,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 } @@ -8123,9 +8152,9 @@ 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().messageIndices.Remote 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 @@ -8308,7 +8337,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 @@ -8357,12 +8386,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 } @@ -8381,8 +8410,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) @@ -8429,12 +8458,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 } @@ -8641,7 +8670,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 @@ -8903,7 +8932,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 de6bb219cd8..f39dd2d433d 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)) } } @@ -1435,12 +1437,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 +1467,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 +1700,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)) @@ -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()) } } @@ -1816,25 +1818,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) } @@ -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]) @@ -2090,20 +2108,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") } @@ -3241,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 @@ -4503,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) @@ -4512,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) @@ -5207,9 +5229,11 @@ 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().messageIndices.Remote + htlcView := lc.fetchHTLCView(remoteACKedIndex, - lc.localUpdateLog.logIndex) + lc.updateLogs.Local.logIndex) _, w := lc.availableCommitmentBalance( htlcView, lntypes.Remote, FeeBuffer, @@ -5830,7 +5854,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, @@ -6916,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") } @@ -6962,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 @@ -6978,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 @@ -7243,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") @@ -7523,7 +7552,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 +7768,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 +7785,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 @@ -8190,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( @@ -8526,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) } } @@ -11099,7 +11132,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 +11149,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))