mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
Merge pull request #8950 from ProofOfKeags/refactor/lnwallet-channel-move-only
[NANO]: Move definitions into their own file
This commit is contained in:
commit
72a36da9c6
4 changed files with 481 additions and 462 deletions
|
@ -2,7 +2,6 @@ package lnwallet
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -168,220 +167,6 @@ func (e *ErrCommitSyncLocalDataLoss) Error() string {
|
|||
// payments requested by the wallet/daemon.
|
||||
type PaymentHash [32]byte
|
||||
|
||||
// updateType is the exact type of an entry within the shared HTLC log.
|
||||
type updateType uint8
|
||||
|
||||
const (
|
||||
// Add is an update type that adds a new HTLC entry into the log.
|
||||
// Either side can add a new pending HTLC by adding a new Add entry
|
||||
// into their update log.
|
||||
Add updateType = iota
|
||||
|
||||
// Fail is an update type which removes a prior HTLC entry from the
|
||||
// log. Adding a Fail entry to one's log will modify the _remote_
|
||||
// party's update log once a new commitment view has been evaluated
|
||||
// which contains the Fail entry.
|
||||
Fail
|
||||
|
||||
// MalformedFail is an update type which removes a prior HTLC entry
|
||||
// from the log. Adding a MalformedFail entry to one's log will modify
|
||||
// the _remote_ party's update log once a new commitment view has been
|
||||
// evaluated which contains the MalformedFail entry. The difference
|
||||
// from Fail type lie in the different data we have to store.
|
||||
MalformedFail
|
||||
|
||||
// Settle is an update type which settles a prior HTLC crediting the
|
||||
// balance of the receiving node. Adding a Settle entry to a log will
|
||||
// result in the settle entry being removed on the log as well as the
|
||||
// original add entry from the remote party's log after the next state
|
||||
// transition.
|
||||
Settle
|
||||
|
||||
// FeeUpdate is an update type sent by the channel initiator that
|
||||
// updates the fee rate used when signing the commitment transaction.
|
||||
FeeUpdate
|
||||
)
|
||||
|
||||
// String returns a human readable string that uniquely identifies the target
|
||||
// update type.
|
||||
func (u updateType) String() string {
|
||||
switch u {
|
||||
case Add:
|
||||
return "Add"
|
||||
case Fail:
|
||||
return "Fail"
|
||||
case MalformedFail:
|
||||
return "MalformedFail"
|
||||
case Settle:
|
||||
return "Settle"
|
||||
case FeeUpdate:
|
||||
return "FeeUpdate"
|
||||
default:
|
||||
return "<unknown type>"
|
||||
}
|
||||
}
|
||||
|
||||
// PaymentDescriptor represents a commitment state update which either adds,
|
||||
// settles, or removes an HTLC. PaymentDescriptors encapsulate all necessary
|
||||
// metadata w.r.t to an HTLC, and additional data pairing a settle message to
|
||||
// the original added HTLC.
|
||||
//
|
||||
// TODO(roasbeef): LogEntry interface??
|
||||
// - need to separate attrs for cancel/add/settle/feeupdate
|
||||
type PaymentDescriptor struct {
|
||||
// RHash is the payment hash for this HTLC. The HTLC can be settled iff
|
||||
// the preimage to this hash is presented.
|
||||
RHash PaymentHash
|
||||
|
||||
// RPreimage is the preimage that settles the HTLC pointed to within the
|
||||
// log by the ParentIndex.
|
||||
RPreimage PaymentHash
|
||||
|
||||
// Timeout is the absolute timeout in blocks, after which this HTLC
|
||||
// expires.
|
||||
Timeout uint32
|
||||
|
||||
// Amount is the HTLC amount in milli-satoshis.
|
||||
Amount lnwire.MilliSatoshi
|
||||
|
||||
// LogIndex is the log entry number that his HTLC update has within the
|
||||
// log. Depending on if IsIncoming is true, this is either an entry the
|
||||
// remote party added, or one that we added locally.
|
||||
LogIndex uint64
|
||||
|
||||
// HtlcIndex is the index within the main update log for this HTLC.
|
||||
// Entries within the log of type Add will have this field populated,
|
||||
// as other entries will point to the entry via this counter.
|
||||
//
|
||||
// NOTE: This field will only be populate if EntryType is Add.
|
||||
HtlcIndex uint64
|
||||
|
||||
// ParentIndex is the HTLC index of the entry that this update settles or
|
||||
// times out.
|
||||
//
|
||||
// NOTE: This field will only be populate if EntryType is Fail or
|
||||
// Settle.
|
||||
ParentIndex uint64
|
||||
|
||||
// SourceRef points to an Add update in a forwarding package owned by
|
||||
// this channel.
|
||||
//
|
||||
// NOTE: This field will only be populated if EntryType is Fail or
|
||||
// Settle.
|
||||
SourceRef *channeldb.AddRef
|
||||
|
||||
// DestRef points to a Fail/Settle update in another link's forwarding
|
||||
// package.
|
||||
//
|
||||
// NOTE: This field will only be populated if EntryType is Fail or
|
||||
// Settle, and the forwarded Add successfully included in an outgoing
|
||||
// link's commitment txn.
|
||||
DestRef *channeldb.SettleFailRef
|
||||
|
||||
// OpenCircuitKey references the incoming Chan/HTLC ID of an Add HTLC
|
||||
// packet delivered by the switch.
|
||||
//
|
||||
// NOTE: This field is only populated for payment descriptors in the
|
||||
// *local* update log, and if the Add packet was delivered by the
|
||||
// switch.
|
||||
OpenCircuitKey *models.CircuitKey
|
||||
|
||||
// ClosedCircuitKey references the incoming Chan/HTLC ID of the Add HTLC
|
||||
// that opened the circuit.
|
||||
//
|
||||
// NOTE: This field is only populated for payment descriptors in the
|
||||
// *local* update log, and if settle/fails have a committed circuit in
|
||||
// the circuit map.
|
||||
ClosedCircuitKey *models.CircuitKey
|
||||
|
||||
// localOutputIndex is the output index of this HTLc output in the
|
||||
// commitment transaction of the local node.
|
||||
//
|
||||
// NOTE: If the output is dust from the PoV of the local commitment
|
||||
// chain, then this value will be -1.
|
||||
localOutputIndex int32
|
||||
|
||||
// remoteOutputIndex is the output index of this HTLC output in the
|
||||
// commitment transaction of the remote node.
|
||||
//
|
||||
// NOTE: If the output is dust from the PoV of the remote commitment
|
||||
// chain, then this value will be -1.
|
||||
remoteOutputIndex int32
|
||||
|
||||
// sig is the signature for the second-level HTLC transaction that
|
||||
// spends the version of this HTLC on the commitment transaction of the
|
||||
// local node. This signature is generated by the remote node and
|
||||
// stored by the local node in the case that local node needs to
|
||||
// broadcast their commitment transaction.
|
||||
sig input.Signature
|
||||
|
||||
// addCommitHeight[Remote|Local] encodes the height of the commitment
|
||||
// which included this HTLC on either the remote or local commitment
|
||||
// chain. This value is used to determine when an HTLC is fully
|
||||
// "locked-in".
|
||||
addCommitHeightRemote uint64
|
||||
addCommitHeightLocal uint64
|
||||
|
||||
// removeCommitHeight[Remote|Local] encodes the height of the
|
||||
// commitment which removed the parent pointer of this
|
||||
// PaymentDescriptor either due to a timeout or a settle. Once both
|
||||
// these heights are below the tail of both chains, the log entries can
|
||||
// safely be removed.
|
||||
removeCommitHeightRemote uint64
|
||||
removeCommitHeightLocal uint64
|
||||
|
||||
// OnionBlob is an opaque blob which is used to complete multi-hop
|
||||
// routing.
|
||||
//
|
||||
// NOTE: Populated only on add payment descriptor entry types.
|
||||
OnionBlob []byte
|
||||
|
||||
// ShaOnionBlob is a sha of the onion blob.
|
||||
//
|
||||
// NOTE: Populated only in payment descriptor with MalformedFail type.
|
||||
ShaOnionBlob [sha256.Size]byte
|
||||
|
||||
// FailReason stores the reason why a particular payment was canceled.
|
||||
//
|
||||
// NOTE: Populate only in fail payment descriptor entry types.
|
||||
FailReason []byte
|
||||
|
||||
// FailCode stores the code why a particular payment was canceled.
|
||||
//
|
||||
// NOTE: Populated only in payment descriptor with MalformedFail type.
|
||||
FailCode lnwire.FailCode
|
||||
|
||||
// [our|their|]PkScript are the raw public key scripts that encodes the
|
||||
// redemption rules for this particular HTLC. These fields will only be
|
||||
// populated iff the EntryType of this PaymentDescriptor is Add.
|
||||
// ourPkScript is the ourPkScript from the context of our local
|
||||
// commitment chain. theirPkScript is the latest pkScript from the
|
||||
// context of the remote commitment chain.
|
||||
//
|
||||
// NOTE: These values may change within the logs themselves, however,
|
||||
// they'll stay consistent within the commitment chain entries
|
||||
// themselves.
|
||||
ourPkScript []byte
|
||||
ourWitnessScript []byte
|
||||
theirPkScript []byte
|
||||
theirWitnessScript []byte
|
||||
|
||||
// EntryType denotes the exact type of the PaymentDescriptor. In the
|
||||
// case of a Timeout, or Settle type, then the Parent field will point
|
||||
// into the log to the HTLC being modified.
|
||||
EntryType updateType
|
||||
|
||||
// isForwarded denotes if an incoming HTLC has been forwarded to any
|
||||
// possible upstream peers in the route.
|
||||
isForwarded bool
|
||||
|
||||
// BlindingPoint is an optional ephemeral key used in route blinding.
|
||||
// This value is set for nodes that are relaying payments inside of a
|
||||
// blinded route (ie, not the introduction node) from update_add_htlc's
|
||||
// TLVs.
|
||||
BlindingPoint lnwire.BlindingPointRecord
|
||||
}
|
||||
|
||||
// PayDescsFromRemoteLogUpdates converts a slice of LogUpdates received from the
|
||||
// remote peer into PaymentDescriptors to inform a link's forwarding decisions.
|
||||
//
|
||||
|
@ -983,253 +768,6 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
|
|||
return commit, nil
|
||||
}
|
||||
|
||||
// commitmentChain represents a chain of unrevoked commitments. The tail of the
|
||||
// chain is the latest fully signed, yet unrevoked commitment. Two chains are
|
||||
// tracked, one for the local node, and another for the remote node. New
|
||||
// commitments we create locally extend the remote node's chain, and vice
|
||||
// versa. Commitment chains are allowed to grow to a bounded length, after
|
||||
// which the tail needs to be "dropped" before new commitments can be received.
|
||||
// The tail is "dropped" when the owner of the chain sends a revocation for the
|
||||
// previous tail.
|
||||
type commitmentChain struct {
|
||||
// commitments is a linked list of commitments to new states. New
|
||||
// commitments are added to the end of the chain with increase height.
|
||||
// Once a commitment transaction is revoked, the tail is incremented,
|
||||
// freeing up the revocation window for new commitments.
|
||||
commitments *list.List
|
||||
}
|
||||
|
||||
// newCommitmentChain creates a new commitment chain.
|
||||
func newCommitmentChain() *commitmentChain {
|
||||
return &commitmentChain{
|
||||
commitments: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// addCommitment extends the commitment chain by a single commitment. This
|
||||
// added commitment represents a state update proposed by either party. Once
|
||||
// the commitment prior to this commitment is revoked, the commitment becomes
|
||||
// the new defacto state within the channel.
|
||||
func (s *commitmentChain) addCommitment(c *commitment) {
|
||||
s.commitments.PushBack(c)
|
||||
}
|
||||
|
||||
// advanceTail reduces the length of the commitment chain by one. The tail of
|
||||
// the chain should be advanced once a revocation for the lowest unrevoked
|
||||
// commitment in the chain is received.
|
||||
func (s *commitmentChain) advanceTail() {
|
||||
s.commitments.Remove(s.commitments.Front())
|
||||
}
|
||||
|
||||
// tip returns the latest commitment added to the chain.
|
||||
func (s *commitmentChain) tip() *commitment {
|
||||
return s.commitments.Back().Value.(*commitment)
|
||||
}
|
||||
|
||||
// tail returns the lowest unrevoked commitment transaction in the chain.
|
||||
func (s *commitmentChain) tail() *commitment {
|
||||
return s.commitments.Front().Value.(*commitment)
|
||||
}
|
||||
|
||||
// hasUnackedCommitment returns true if the commitment chain has more than one
|
||||
// entry. The tail of the commitment chain has been ACKed by revoking all prior
|
||||
// commitments, but any subsequent commitments have not yet been ACKed.
|
||||
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
|
||||
|
|
60
lnwallet/commitment_chain.go
Normal file
60
lnwallet/commitment_chain.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package lnwallet
|
||||
|
||||
import "container/list"
|
||||
|
||||
// commitmentChain represents a chain of unrevoked commitments. The tail of the
|
||||
// chain is the latest fully signed, yet unrevoked commitment. Two chains are
|
||||
// tracked, one for the local node, and another for the remote node. New
|
||||
// commitments we create locally extend the remote node's chain, and vice
|
||||
// versa. Commitment chains are allowed to grow to a bounded length, after
|
||||
// which the tail needs to be "dropped" before new commitments can be received.
|
||||
// The tail is "dropped" when the owner of the chain sends a revocation for the
|
||||
// previous tail.
|
||||
type commitmentChain struct {
|
||||
// commitments is a linked list of commitments to new states. New
|
||||
// commitments are added to the end of the chain with increase height.
|
||||
// Once a commitment transaction is revoked, the tail is incremented,
|
||||
// freeing up the revocation window for new commitments.
|
||||
commitments *list.List
|
||||
}
|
||||
|
||||
// newCommitmentChain creates a new commitment chain.
|
||||
func newCommitmentChain() *commitmentChain {
|
||||
return &commitmentChain{
|
||||
commitments: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// addCommitment extends the commitment chain by a single commitment. This
|
||||
// added commitment represents a state update proposed by either party. Once
|
||||
// the commitment prior to this commitment is revoked, the commitment becomes
|
||||
// the new defacto state within the channel.
|
||||
func (s *commitmentChain) addCommitment(c *commitment) {
|
||||
s.commitments.PushBack(c)
|
||||
}
|
||||
|
||||
// advanceTail reduces the length of the commitment chain by one. The tail of
|
||||
// the chain should be advanced once a revocation for the lowest unrevoked
|
||||
// commitment in the chain is received.
|
||||
func (s *commitmentChain) advanceTail() {
|
||||
s.commitments.Remove(s.commitments.Front())
|
||||
}
|
||||
|
||||
// tip returns the latest commitment added to the chain.
|
||||
func (s *commitmentChain) tip() *commitment {
|
||||
//nolint:forcetypeassert
|
||||
return s.commitments.Back().Value.(*commitment)
|
||||
}
|
||||
|
||||
// tail returns the lowest unrevoked commitment transaction in the chain.
|
||||
func (s *commitmentChain) tail() *commitment {
|
||||
//nolint:forcetypeassert
|
||||
return s.commitments.Front().Value.(*commitment)
|
||||
}
|
||||
|
||||
// hasUnackedCommitment returns true if the commitment chain has more than one
|
||||
// entry. The tail of the commitment chain has been ACKed by revoking all prior
|
||||
// commitments, but any subsequent commitments have not yet been ACKed.
|
||||
func (s *commitmentChain) hasUnackedCommitment() bool {
|
||||
return s.commitments.Front() != s.commitments.Back()
|
||||
}
|
224
lnwallet/payment_descriptor.go
Normal file
224
lnwallet/payment_descriptor.go
Normal file
|
@ -0,0 +1,224 @@
|
|||
package lnwallet
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
// updateType is the exact type of an entry within the shared HTLC log.
|
||||
type updateType uint8
|
||||
|
||||
const (
|
||||
// Add is an update type that adds a new HTLC entry into the log.
|
||||
// Either side can add a new pending HTLC by adding a new Add entry
|
||||
// into their update log.
|
||||
Add updateType = iota
|
||||
|
||||
// Fail is an update type which removes a prior HTLC entry from the
|
||||
// log. Adding a Fail entry to one's log will modify the _remote_
|
||||
// party's update log once a new commitment view has been evaluated
|
||||
// which contains the Fail entry.
|
||||
Fail
|
||||
|
||||
// MalformedFail is an update type which removes a prior HTLC entry
|
||||
// from the log. Adding a MalformedFail entry to one's log will modify
|
||||
// the _remote_ party's update log once a new commitment view has been
|
||||
// evaluated which contains the MalformedFail entry. The difference
|
||||
// from Fail type lie in the different data we have to store.
|
||||
MalformedFail
|
||||
|
||||
// Settle is an update type which settles a prior HTLC crediting the
|
||||
// balance of the receiving node. Adding a Settle entry to a log will
|
||||
// result in the settle entry being removed on the log as well as the
|
||||
// original add entry from the remote party's log after the next state
|
||||
// transition.
|
||||
Settle
|
||||
|
||||
// FeeUpdate is an update type sent by the channel initiator that
|
||||
// updates the fee rate used when signing the commitment transaction.
|
||||
FeeUpdate
|
||||
)
|
||||
|
||||
// String returns a human readable string that uniquely identifies the target
|
||||
// update type.
|
||||
func (u updateType) String() string {
|
||||
switch u {
|
||||
case Add:
|
||||
return "Add"
|
||||
case Fail:
|
||||
return "Fail"
|
||||
case MalformedFail:
|
||||
return "MalformedFail"
|
||||
case Settle:
|
||||
return "Settle"
|
||||
case FeeUpdate:
|
||||
return "FeeUpdate"
|
||||
default:
|
||||
return "<unknown type>"
|
||||
}
|
||||
}
|
||||
|
||||
// PaymentDescriptor represents a commitment state update which either adds,
|
||||
// settles, or removes an HTLC. PaymentDescriptors encapsulate all necessary
|
||||
// metadata w.r.t to an HTLC, and additional data pairing a settle message to
|
||||
// the original added HTLC.
|
||||
//
|
||||
// TODO(roasbeef): LogEntry interface??
|
||||
// - need to separate attrs for cancel/add/settle/feeupdate
|
||||
type PaymentDescriptor struct {
|
||||
// RHash is the payment hash for this HTLC. The HTLC can be settled iff
|
||||
// the preimage to this hash is presented.
|
||||
RHash PaymentHash
|
||||
|
||||
// RPreimage is the preimage that settles the HTLC pointed to within the
|
||||
// log by the ParentIndex.
|
||||
RPreimage PaymentHash
|
||||
|
||||
// Timeout is the absolute timeout in blocks, after which this HTLC
|
||||
// expires.
|
||||
Timeout uint32
|
||||
|
||||
// Amount is the HTLC amount in milli-satoshis.
|
||||
Amount lnwire.MilliSatoshi
|
||||
|
||||
// LogIndex is the log entry number that his HTLC update has within the
|
||||
// log. Depending on if IsIncoming is true, this is either an entry the
|
||||
// remote party added, or one that we added locally.
|
||||
LogIndex uint64
|
||||
|
||||
// HtlcIndex is the index within the main update log for this HTLC.
|
||||
// Entries within the log of type Add will have this field populated,
|
||||
// as other entries will point to the entry via this counter.
|
||||
//
|
||||
// NOTE: This field will only be populate if EntryType is Add.
|
||||
HtlcIndex uint64
|
||||
|
||||
// ParentIndex is the HTLC index of the entry that this update settles
|
||||
// or times out.
|
||||
//
|
||||
// NOTE: This field will only be populate if EntryType is Fail or
|
||||
// Settle.
|
||||
ParentIndex uint64
|
||||
|
||||
// SourceRef points to an Add update in a forwarding package owned by
|
||||
// this channel.
|
||||
//
|
||||
// NOTE: This field will only be populated if EntryType is Fail or
|
||||
// Settle.
|
||||
SourceRef *channeldb.AddRef
|
||||
|
||||
// DestRef points to a Fail/Settle update in another link's forwarding
|
||||
// package.
|
||||
//
|
||||
// NOTE: This field will only be populated if EntryType is Fail or
|
||||
// Settle, and the forwarded Add successfully included in an outgoing
|
||||
// link's commitment txn.
|
||||
DestRef *channeldb.SettleFailRef
|
||||
|
||||
// OpenCircuitKey references the incoming Chan/HTLC ID of an Add HTLC
|
||||
// packet delivered by the switch.
|
||||
//
|
||||
// NOTE: This field is only populated for payment descriptors in the
|
||||
// *local* update log, and if the Add packet was delivered by the
|
||||
// switch.
|
||||
OpenCircuitKey *models.CircuitKey
|
||||
|
||||
// ClosedCircuitKey references the incoming Chan/HTLC ID of the Add HTLC
|
||||
// that opened the circuit.
|
||||
//
|
||||
// NOTE: This field is only populated for payment descriptors in the
|
||||
// *local* update log, and if settle/fails have a committed circuit in
|
||||
// the circuit map.
|
||||
ClosedCircuitKey *models.CircuitKey
|
||||
|
||||
// localOutputIndex is the output index of this HTLc output in the
|
||||
// commitment transaction of the local node.
|
||||
//
|
||||
// NOTE: If the output is dust from the PoV of the local commitment
|
||||
// chain, then this value will be -1.
|
||||
localOutputIndex int32
|
||||
|
||||
// remoteOutputIndex is the output index of this HTLC output in the
|
||||
// commitment transaction of the remote node.
|
||||
//
|
||||
// NOTE: If the output is dust from the PoV of the remote commitment
|
||||
// chain, then this value will be -1.
|
||||
remoteOutputIndex int32
|
||||
|
||||
// sig is the signature for the second-level HTLC transaction that
|
||||
// spends the version of this HTLC on the commitment transaction of the
|
||||
// local node. This signature is generated by the remote node and
|
||||
// stored by the local node in the case that local node needs to
|
||||
// broadcast their commitment transaction.
|
||||
sig input.Signature
|
||||
|
||||
// addCommitHeight[Remote|Local] encodes the height of the commitment
|
||||
// which included this HTLC on either the remote or local commitment
|
||||
// chain. This value is used to determine when an HTLC is fully
|
||||
// "locked-in".
|
||||
addCommitHeightRemote uint64
|
||||
addCommitHeightLocal uint64
|
||||
|
||||
// removeCommitHeight[Remote|Local] encodes the height of the
|
||||
// commitment which removed the parent pointer of this
|
||||
// PaymentDescriptor either due to a timeout or a settle. Once both
|
||||
// these heights are below the tail of both chains, the log entries can
|
||||
// safely be removed.
|
||||
removeCommitHeightRemote uint64
|
||||
removeCommitHeightLocal uint64
|
||||
|
||||
// OnionBlob is an opaque blob which is used to complete multi-hop
|
||||
// routing.
|
||||
//
|
||||
// NOTE: Populated only on add payment descriptor entry types.
|
||||
OnionBlob []byte
|
||||
|
||||
// ShaOnionBlob is a sha of the onion blob.
|
||||
//
|
||||
// NOTE: Populated only in payment descriptor with MalformedFail type.
|
||||
ShaOnionBlob [sha256.Size]byte
|
||||
|
||||
// FailReason stores the reason why a particular payment was canceled.
|
||||
//
|
||||
// NOTE: Populate only in fail payment descriptor entry types.
|
||||
FailReason []byte
|
||||
|
||||
// FailCode stores the code why a particular payment was canceled.
|
||||
//
|
||||
// NOTE: Populated only in payment descriptor with MalformedFail type.
|
||||
FailCode lnwire.FailCode
|
||||
|
||||
// [our|their|]PkScript are the raw public key scripts that encodes the
|
||||
// redemption rules for this particular HTLC. These fields will only be
|
||||
// populated iff the EntryType of this PaymentDescriptor is Add.
|
||||
// ourPkScript is the ourPkScript from the context of our local
|
||||
// commitment chain. theirPkScript is the latest pkScript from the
|
||||
// context of the remote commitment chain.
|
||||
//
|
||||
// NOTE: These values may change within the logs themselves, however,
|
||||
// they'll stay consistent within the commitment chain entries
|
||||
// themselves.
|
||||
ourPkScript []byte
|
||||
ourWitnessScript []byte
|
||||
theirPkScript []byte
|
||||
theirWitnessScript []byte
|
||||
|
||||
// EntryType denotes the exact type of the PaymentDescriptor. In the
|
||||
// case of a Timeout, or Settle type, then the Parent field will point
|
||||
// into the log to the HTLC being modified.
|
||||
EntryType updateType
|
||||
|
||||
// isForwarded denotes if an incoming HTLC has been forwarded to any
|
||||
// possible upstream peers in the route.
|
||||
isForwarded bool
|
||||
|
||||
// BlindingPoint is an optional ephemeral key used in route blinding.
|
||||
// This value is set for nodes that are relaying payments inside of a
|
||||
// blinded route (ie, not the introduction node) from update_add_htlc's
|
||||
// TLVs.
|
||||
BlindingPoint lnwire.BlindingPointRecord
|
||||
}
|
197
lnwallet/update_log.go
Normal file
197
lnwallet/update_log.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
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
|
||||
}
|
||||
|
||||
//nolint:forcetypeassert
|
||||
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)
|
||||
}
|
Loading…
Add table
Reference in a new issue