mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-25 18:01:20 +02:00
lnwallet: move updateLog to its own file.
This commit is contained in:
@@ -823,198 +823,6 @@ func (s *commitmentChain) hasUnackedCommitment() bool {
|
||||
return s.commitments.Front() != s.commitments.Back()
|
||||
}
|
||||
|
||||
// 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.
|
||||
*list.List
|
||||
|
||||
// updateIndex maps a `logIndex` to a particular update entry. It
|
||||
// deals with the four update types:
|
||||
// `Fail|MalformedFail|Settle|FeeUpdate`
|
||||
updateIndex map[uint64]*list.Element
|
||||
|
||||
// htlcIndex maps a `htlcCounter` to an offered HTLC entry, hence the
|
||||
// `Add` update.
|
||||
htlcIndex map[uint64]*list.Element
|
||||
|
||||
// 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 map[uint64]struct{}
|
||||
}
|
||||
|
||||
// newUpdateLog creates a new updateLog instance.
|
||||
func newUpdateLog(logIndex, htlcCounter uint64) *updateLog {
|
||||
return &updateLog{
|
||||
List: list.New(),
|
||||
updateIndex: make(map[uint64]*list.Element),
|
||||
htlcIndex: make(map[uint64]*list.Element),
|
||||
logIndex: logIndex,
|
||||
htlcCounter: htlcCounter,
|
||||
modifiedHtlcs: make(map[uint64]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// 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.(*PaymentDescriptor)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
delete(u.modifiedHtlcs, 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 {
|
||||
_, o := u.modifiedHtlcs[i]
|
||||
return o
|
||||
}
|
||||
|
||||
// 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[i] = struct{}{}
|
||||
}
|
||||
|
||||
// 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 *list.Element
|
||||
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.(*PaymentDescriptor)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// LightningChannel implements the state machine which corresponds to the
|
||||
// current commitment protocol wire spec. The state machine implemented allows
|
||||
// for asynchronous fully desynchronized, batched+pipelined updates to
|
||||
|
196
lnwallet/update_log.go
Normal file
196
lnwallet/update_log.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package lnwallet
|
||||
|
||||
import "container/list"
|
||||
|
||||
// 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.
|
||||
*list.List
|
||||
|
||||
// updateIndex maps a `logIndex` to a particular update entry. It
|
||||
// deals with the four update types:
|
||||
// `Fail|MalformedFail|Settle|FeeUpdate`
|
||||
updateIndex map[uint64]*list.Element
|
||||
|
||||
// htlcIndex maps a `htlcCounter` to an offered HTLC entry, hence the
|
||||
// `Add` update.
|
||||
htlcIndex map[uint64]*list.Element
|
||||
|
||||
// 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 map[uint64]struct{}
|
||||
}
|
||||
|
||||
// newUpdateLog creates a new updateLog instance.
|
||||
func newUpdateLog(logIndex, htlcCounter uint64) *updateLog {
|
||||
return &updateLog{
|
||||
List: list.New(),
|
||||
updateIndex: make(map[uint64]*list.Element),
|
||||
htlcIndex: make(map[uint64]*list.Element),
|
||||
logIndex: logIndex,
|
||||
htlcCounter: htlcCounter,
|
||||
modifiedHtlcs: make(map[uint64]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// 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.(*PaymentDescriptor)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
delete(u.modifiedHtlcs, 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 {
|
||||
_, o := u.modifiedHtlcs[i]
|
||||
return o
|
||||
}
|
||||
|
||||
// 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[i] = struct{}{}
|
||||
}
|
||||
|
||||
// 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 *list.Element
|
||||
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()
|
||||
|
||||
//nolint:forcetypeassert
|
||||
htlc := e.Value.(*PaymentDescriptor)
|
||||
|
||||
// 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)
|
||||
}
|
Reference in New Issue
Block a user