mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
197 lines
6.6 KiB
Go
197 lines
6.6 KiB
Go
package lnwallet
|
|
|
|
import (
|
|
"github.com/lightningnetwork/lnd/fn"
|
|
)
|
|
|
|
// updateLog is an append-only log that stores updates to a node's commitment
|
|
// chain. This structure can be seen as the "mempool" within Lightning where
|
|
// changes are stored before they're committed to the chain. Once an entry has
|
|
// been committed in both the local and remote commitment chain, then it can be
|
|
// removed from this log.
|
|
//
|
|
// TODO(roasbeef): create lightning package, move commitment and update to
|
|
// package?
|
|
// - also move state machine, separate from lnwallet package
|
|
// - possible embed updateLog within commitmentChain.
|
|
type updateLog struct {
|
|
// logIndex is a monotonically increasing integer that tracks the total
|
|
// number of update entries ever applied to the log. When sending new
|
|
// commitment states, we include all updates up to this index.
|
|
logIndex uint64
|
|
|
|
// htlcCounter is a monotonically increasing integer that tracks the
|
|
// total number of offered HTLC's by the owner of this update log,
|
|
// hence the `Add` update type. We use a distinct index for this
|
|
// purpose, as update's that remove entries from the log will be
|
|
// indexed using this counter.
|
|
htlcCounter uint64
|
|
|
|
// List is the updatelog itself, we embed this value so updateLog has
|
|
// access to all the method of a list.List.
|
|
*fn.List[*PaymentDescriptor]
|
|
|
|
// updateIndex maps a `logIndex` to a particular update entry. It
|
|
// deals with the four update types:
|
|
// `Fail|MalformedFail|Settle|FeeUpdate`
|
|
updateIndex map[uint64]*fn.Node[*PaymentDescriptor]
|
|
|
|
// htlcIndex maps a `htlcCounter` to an offered HTLC entry, hence the
|
|
// `Add` update.
|
|
htlcIndex map[uint64]*fn.Node[*PaymentDescriptor]
|
|
|
|
// modifiedHtlcs is a set that keeps track of all the current modified
|
|
// htlcs, hence update types `Fail|MalformedFail|Settle`. A modified
|
|
// HTLC is one that's present in the log, and has as a pending fail or
|
|
// settle that's attempting to consume it.
|
|
modifiedHtlcs fn.Set[uint64]
|
|
}
|
|
|
|
// newUpdateLog creates a new updateLog instance.
|
|
func newUpdateLog(logIndex, htlcCounter uint64) *updateLog {
|
|
return &updateLog{
|
|
List: fn.NewList[*PaymentDescriptor](),
|
|
updateIndex: make(map[uint64]*fn.Node[*PaymentDescriptor]),
|
|
htlcIndex: make(map[uint64]*fn.Node[*PaymentDescriptor]),
|
|
logIndex: logIndex,
|
|
htlcCounter: htlcCounter,
|
|
modifiedHtlcs: fn.NewSet[uint64](),
|
|
}
|
|
}
|
|
|
|
// restoreHtlc will "restore" a prior HTLC to the updateLog. We say restore as
|
|
// this method is intended to be used when re-covering a prior commitment
|
|
// state. This function differs from appendHtlc in that it won't increment
|
|
// either of log's counters. If the HTLC is already present, then it is
|
|
// ignored.
|
|
func (u *updateLog) restoreHtlc(pd *PaymentDescriptor) {
|
|
if _, ok := u.htlcIndex[pd.HtlcIndex]; ok {
|
|
return
|
|
}
|
|
|
|
u.htlcIndex[pd.HtlcIndex] = u.PushBack(pd)
|
|
}
|
|
|
|
// appendUpdate appends a new update to the tip of the updateLog. The entry is
|
|
// also added to index accordingly.
|
|
func (u *updateLog) appendUpdate(pd *PaymentDescriptor) {
|
|
u.updateIndex[u.logIndex] = u.PushBack(pd)
|
|
u.logIndex++
|
|
}
|
|
|
|
// restoreUpdate appends a new update to the tip of the updateLog. The entry is
|
|
// also added to index accordingly. This function differs from appendUpdate in
|
|
// that it won't increment the log index counter.
|
|
func (u *updateLog) restoreUpdate(pd *PaymentDescriptor) {
|
|
u.updateIndex[pd.LogIndex] = u.PushBack(pd)
|
|
}
|
|
|
|
// appendHtlc appends a new HTLC offer to the tip of the update log. The entry
|
|
// is also added to the offer index accordingly.
|
|
func (u *updateLog) appendHtlc(pd *PaymentDescriptor) {
|
|
u.htlcIndex[u.htlcCounter] = u.PushBack(pd)
|
|
u.htlcCounter++
|
|
|
|
u.logIndex++
|
|
}
|
|
|
|
// lookupHtlc attempts to look up an offered HTLC according to its offer
|
|
// index. If the entry isn't found, then a nil pointer is returned.
|
|
func (u *updateLog) lookupHtlc(i uint64) *PaymentDescriptor {
|
|
htlc, ok := u.htlcIndex[i]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return htlc.Value
|
|
}
|
|
|
|
// remove attempts to remove an entry from the update log. If the entry is
|
|
// found, then the entry will be removed from the update log and index.
|
|
func (u *updateLog) removeUpdate(i uint64) {
|
|
entry := u.updateIndex[i]
|
|
u.Remove(entry)
|
|
delete(u.updateIndex, i)
|
|
}
|
|
|
|
// removeHtlc attempts to remove an HTLC offer form the update log. If the
|
|
// entry is found, then the entry will be removed from both the main log and
|
|
// the offer index.
|
|
func (u *updateLog) removeHtlc(i uint64) {
|
|
entry := u.htlcIndex[i]
|
|
u.Remove(entry)
|
|
delete(u.htlcIndex, i)
|
|
|
|
u.modifiedHtlcs.Remove(i)
|
|
}
|
|
|
|
// htlcHasModification returns true if the HTLC identified by the passed index
|
|
// has a pending modification within the log.
|
|
func (u *updateLog) htlcHasModification(i uint64) bool {
|
|
return u.modifiedHtlcs.Contains(i)
|
|
}
|
|
|
|
// markHtlcModified marks an HTLC as modified based on its HTLC index. After a
|
|
// call to this method, htlcHasModification will return true until the HTLC is
|
|
// removed.
|
|
func (u *updateLog) markHtlcModified(i uint64) {
|
|
u.modifiedHtlcs.Add(i)
|
|
}
|
|
|
|
// compactLogs performs garbage collection within the log removing HTLCs which
|
|
// have been removed from the point-of-view of the tail of both chains. The
|
|
// entries which timeout/settle HTLCs are also removed.
|
|
func compactLogs(ourLog, theirLog *updateLog,
|
|
localChainTail, remoteChainTail uint64) {
|
|
|
|
compactLog := func(logA, logB *updateLog) {
|
|
var nextA *fn.Node[*PaymentDescriptor]
|
|
for e := logA.Front(); e != nil; e = nextA {
|
|
// Assign next iteration element at top of loop because
|
|
// we may remove the current element from the list,
|
|
// which can change the iterated sequence.
|
|
nextA = e.Next()
|
|
|
|
htlc := e.Value
|
|
|
|
// We skip Adds, as they will be removed along with the
|
|
// fail/settles below.
|
|
if htlc.EntryType == Add {
|
|
continue
|
|
}
|
|
|
|
// If the HTLC hasn't yet been removed from either
|
|
// chain, the skip it.
|
|
if htlc.removeCommitHeightRemote == 0 ||
|
|
htlc.removeCommitHeightLocal == 0 {
|
|
|
|
continue
|
|
}
|
|
|
|
// Otherwise if the height of the tail of both chains
|
|
// is at least the height in which the HTLC was
|
|
// removed, then evict the settle/timeout entry along
|
|
// with the original add entry.
|
|
if remoteChainTail >= htlc.removeCommitHeightRemote &&
|
|
localChainTail >= htlc.removeCommitHeightLocal {
|
|
|
|
// Fee updates have no parent htlcs, so we only
|
|
// remove the update itself.
|
|
if htlc.EntryType == FeeUpdate {
|
|
logA.removeUpdate(htlc.LogIndex)
|
|
continue
|
|
}
|
|
|
|
// The other types (fail/settle) do have a
|
|
// parent HTLC, so we'll remove that HTLC from
|
|
// the other log.
|
|
logA.removeUpdate(htlc.LogIndex)
|
|
logB.removeHtlc(htlc.ParentIndex)
|
|
}
|
|
}
|
|
}
|
|
|
|
compactLog(ourLog, theirLog)
|
|
compactLog(theirLog, ourLog)
|
|
}
|