mirror of
https://github.com/lightningnetwork/lnd.git
synced 2026-08-13 12:32:48 +02:00
Copy all HTLC fields when cloning channel commitment state. The old copy method only copied a subset of scalar fields and copied into nil slices for Signature and ExtraData. Allocate those slices and deep-copy custom record values so snapshots and channel copies retain complete HTLC metadata.
304 lines
11 KiB
Go
304 lines
11 KiB
Go
package chanstate
|
|
|
|
import (
|
|
"github.com/btcsuite/btcd/btcutil/v2"
|
|
"github.com/btcsuite/btcd/wire/v2"
|
|
"github.com/lightningnetwork/lnd/fn/v2"
|
|
"github.com/lightningnetwork/lnd/graph/db/models"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/tlv"
|
|
)
|
|
|
|
// ChannelCommitment is a snapshot of the commitment state at a particular
|
|
// point in the commitment chain. With each state transition, a snapshot of the
|
|
// current state along with all non-settled HTLCs are recorded. These snapshots
|
|
// detail the state of the _remote_ party's commitment at a particular state
|
|
// number. For ourselves (the local node) we ONLY store our most recent
|
|
// (unrevoked) state for safety purposes.
|
|
type ChannelCommitment struct {
|
|
// CommitHeight is the update number that this ChannelDelta represents
|
|
// the total number of commitment updates to this point. This can be
|
|
// viewed as sort of a "commitment height" as this number is
|
|
// monotonically increasing.
|
|
CommitHeight uint64
|
|
|
|
// LocalLogIndex is the cumulative log index of the local node at this
|
|
// point in the commitment chain. This value will be incremented for
|
|
// each _update_ added to the local update log.
|
|
LocalLogIndex uint64
|
|
|
|
// LocalHtlcIndex is the current local running HTLC index. This value
|
|
// will be incremented for each outgoing HTLC the local node offers.
|
|
LocalHtlcIndex uint64
|
|
|
|
// RemoteLogIndex is the cumulative log index of the remote node at
|
|
// this point in the commitment chain. This value will be incremented
|
|
// for each _update_ added to the remote update log.
|
|
RemoteLogIndex uint64
|
|
|
|
// RemoteHtlcIndex is the current remote running HTLC index. This value
|
|
// will be incremented for each outgoing HTLC the remote node offers.
|
|
RemoteHtlcIndex uint64
|
|
|
|
// LocalBalance is the current available settled balance within the
|
|
// channel directly spendable by us.
|
|
//
|
|
// NOTE: This is the balance *after* subtracting any commitment fee,
|
|
// AND anchor output values.
|
|
LocalBalance lnwire.MilliSatoshi
|
|
|
|
// RemoteBalance is the current available settled balance within the
|
|
// channel directly spendable by the remote node.
|
|
//
|
|
// NOTE: This is the balance *after* subtracting any commitment fee,
|
|
// AND anchor output values.
|
|
RemoteBalance lnwire.MilliSatoshi
|
|
|
|
// CommitFee is the amount calculated to be paid in fees for the
|
|
// current set of commitment transactions. The fee amount is persisted
|
|
// with the channel in order to allow the fee amount to be removed and
|
|
// recalculated with each channel state update, including updates that
|
|
// happen after a system restart.
|
|
CommitFee btcutil.Amount
|
|
|
|
// FeePerKw is the min satoshis/kilo-weight that should be paid within
|
|
// the commitment transaction for the entire duration of the channel's
|
|
// lifetime. This field may be updated during normal operation of the
|
|
// channel as on-chain conditions change.
|
|
//
|
|
// TODO(halseth): make this SatPerKWeight. Cannot be done atm because
|
|
// this will cause the import cycle lnwallet<->channeldb. Fee
|
|
// estimation stuff should be in its own package.
|
|
FeePerKw btcutil.Amount
|
|
|
|
// CommitTx is the latest version of the commitment state, broadcast
|
|
// able by us.
|
|
CommitTx *wire.MsgTx
|
|
|
|
// CustomBlob is an optional blob that can be used to store information
|
|
// specific to a custom channel type. This may track some custom
|
|
// specific state for this given commitment.
|
|
CustomBlob fn.Option[tlv.Blob]
|
|
|
|
// CommitSig is one half of the signature required to fully complete
|
|
// the script for the commitment transaction above. This is the
|
|
// signature signed by the remote party for our version of the
|
|
// commitment transactions.
|
|
CommitSig []byte
|
|
|
|
// Htlcs is the set of HTLC's that are pending at this particular
|
|
// commitment height.
|
|
Htlcs []HTLC
|
|
}
|
|
|
|
// Copy returns a deep copy of the channel commitment.
|
|
func (c *ChannelCommitment) Copy() ChannelCommitment {
|
|
c2 := *c
|
|
if c.CommitTx != nil {
|
|
c2.CommitTx = c.CommitTx.Copy()
|
|
}
|
|
if len(c.CommitSig) > 0 {
|
|
c2.CommitSig = make([]byte, len(c.CommitSig))
|
|
copy(c2.CommitSig, c.CommitSig)
|
|
}
|
|
|
|
c.CustomBlob.WhenSome(func(blob tlv.Blob) {
|
|
blobCopy := make([]byte, len(blob))
|
|
copy(blobCopy, blob)
|
|
c2.CustomBlob = fn.Some(blobCopy)
|
|
})
|
|
|
|
if len(c.Htlcs) > 0 {
|
|
c2.Htlcs = make([]HTLC, len(c.Htlcs))
|
|
for i, h := range c.Htlcs {
|
|
c2.Htlcs[i] = h.Copy()
|
|
}
|
|
}
|
|
|
|
return c2
|
|
}
|
|
|
|
// HTLC is the on-disk representation of a hash time-locked contract. HTLCs are
|
|
// contained within ChannelDeltas which encode the current state of the
|
|
// commitment between state updates.
|
|
//
|
|
// TODO(roasbeef): save space by using smaller ints at tail end?
|
|
type HTLC struct {
|
|
// TODO(yy): can embed an HTLCEntry here.
|
|
|
|
// Signature is the signature for the second level covenant transaction
|
|
// for this HTLC. The second level transaction is a timeout tx in the
|
|
// case that this is an outgoing HTLC, and a success tx in the case
|
|
// that this is an incoming HTLC.
|
|
//
|
|
// TODO(roasbeef): make [64]byte instead?
|
|
Signature []byte
|
|
|
|
// RHash is the payment hash of the HTLC.
|
|
RHash [32]byte
|
|
|
|
// Amt is the amount of milli-satoshis this HTLC escrows.
|
|
Amt lnwire.MilliSatoshi
|
|
|
|
// RefundTimeout is the absolute timeout on the HTLC that the sender
|
|
// must wait before reclaiming the funds in limbo.
|
|
RefundTimeout uint32
|
|
|
|
// OutputIndex is the output index for this particular HTLC output
|
|
// within the commitment transaction.
|
|
OutputIndex int32
|
|
|
|
// Incoming denotes whether we're the receiver or the sender of this
|
|
// HTLC.
|
|
Incoming bool
|
|
|
|
// OnionBlob is an opaque blob which is used to complete multi-hop
|
|
// routing.
|
|
OnionBlob [lnwire.OnionPacketSize]byte
|
|
|
|
// HtlcIndex is the HTLC counter index of this active, outstanding
|
|
// HTLC. This differs from the LogIndex, as the HtlcIndex is only
|
|
// incremented for each offered HTLC, while they LogIndex is
|
|
// incremented for each update (includes settle+fail).
|
|
HtlcIndex uint64
|
|
|
|
// LogIndex is the cumulative log index of this HTLC. This differs
|
|
// from the HtlcIndex as this will be incremented for each new log
|
|
// update added.
|
|
LogIndex uint64
|
|
|
|
// ExtraData contains any additional information that was transmitted
|
|
// with the HTLC via TLVs. This data *must* already be encoded as a
|
|
// TLV stream, and may be empty. The length of this data is naturally
|
|
// limited by the space available to TLVs in update_add_htlc:
|
|
// = 65535 bytes (bolt 8 maximum message size):
|
|
// - 2 bytes (bolt 1 message_type)
|
|
// - 32 bytes (channel_id)
|
|
// - 8 bytes (id)
|
|
// - 8 bytes (amount_msat)
|
|
// - 32 bytes (payment_hash)
|
|
// - 4 bytes (cltv_expiry)
|
|
// - 1366 bytes (onion_routing_packet)
|
|
// = 64083 bytes maximum possible TLV stream
|
|
//
|
|
// Note that this extra data is stored inline with the OnionBlob for
|
|
// legacy reasons, see serialization/deserialization functions for
|
|
// detail.
|
|
ExtraData lnwire.ExtraOpaqueData
|
|
|
|
// BlindingPoint is an optional blinding point included with the HTLC.
|
|
//
|
|
// Note: this field is not a part of on-disk representation of the
|
|
// HTLC. It is stored in the ExtraData field, which is used to store
|
|
// a TLV stream of additional information associated with the HTLC.
|
|
BlindingPoint lnwire.BlindingPointRecord
|
|
|
|
// CustomRecords is a set of custom TLV records that are associated with
|
|
// this HTLC. These records are used to store additional information
|
|
// about the HTLC that is not part of the standard HTLC fields. This
|
|
// field is encoded within the ExtraData field.
|
|
CustomRecords lnwire.CustomRecords
|
|
}
|
|
|
|
// Copy returns a full copy of the target HTLC.
|
|
func (h *HTLC) Copy() HTLC {
|
|
clone := HTLC{
|
|
Incoming: h.Incoming,
|
|
Amt: h.Amt,
|
|
RefundTimeout: h.RefundTimeout,
|
|
OutputIndex: h.OutputIndex,
|
|
RHash: h.RHash,
|
|
OnionBlob: h.OnionBlob,
|
|
HtlcIndex: h.HtlcIndex,
|
|
LogIndex: h.LogIndex,
|
|
}
|
|
if len(h.Signature) > 0 {
|
|
clone.Signature = make([]byte, len(h.Signature))
|
|
copy(clone.Signature, h.Signature)
|
|
}
|
|
if len(h.ExtraData) > 0 {
|
|
clone.ExtraData = make(lnwire.ExtraOpaqueData, len(h.ExtraData))
|
|
copy(clone.ExtraData, h.ExtraData)
|
|
}
|
|
clone.BlindingPoint = h.BlindingPoint
|
|
if h.CustomRecords != nil {
|
|
clone.CustomRecords = make(
|
|
lnwire.CustomRecords, len(h.CustomRecords),
|
|
)
|
|
for k, v := range h.CustomRecords {
|
|
clone.CustomRecords[k] = make([]byte, len(v))
|
|
copy(clone.CustomRecords[k], v)
|
|
}
|
|
}
|
|
|
|
return clone
|
|
}
|
|
|
|
// LogUpdate represents a pending update to the remote commitment chain. The
|
|
// log update may be an add, fail, or settle entry. We maintain this data in
|
|
// order to be able to properly retransmit our proposed state if necessary.
|
|
type LogUpdate struct {
|
|
// LogIndex is the log index of this proposed commitment update entry.
|
|
LogIndex uint64
|
|
|
|
// UpdateMsg is the update message that was included within our
|
|
// local update log. The LogIndex value denotes the log index of this
|
|
// update which will be used when restoring our local update log if
|
|
// we're left with a dangling update on restart.
|
|
UpdateMsg lnwire.Message
|
|
}
|
|
|
|
// CommitDiff represents the delta needed to apply the state transition between
|
|
// two subsequent commitment states. Given state N and state N+1, one is able
|
|
// to apply the set of messages contained within the CommitDiff to N to arrive
|
|
// at state N+1. Each time a new commitment is extended, we'll write a new
|
|
// commitment (along with the full commitment state) to disk so we can
|
|
// re-transmit the state in the case of a connection loss or message drop.
|
|
type CommitDiff struct {
|
|
// ChannelCommitment is the full commitment state that one would arrive
|
|
// at by applying the set of messages contained in the UpdateDiff to
|
|
// the prior accepted commitment.
|
|
Commitment ChannelCommitment
|
|
|
|
// LogUpdates is the set of messages sent prior to the commitment state
|
|
// transition in question. Upon reconnection, if we detect that they
|
|
// don't have the commitment, then we re-send this along with the
|
|
// proper signature.
|
|
LogUpdates []LogUpdate
|
|
|
|
// CommitSig is the exact CommitSig message that should be sent after
|
|
// the set of LogUpdates above has been retransmitted. The signatures
|
|
// within this message should properly cover the new commitment state
|
|
// and also the HTLC's within the new commitment state.
|
|
CommitSig *lnwire.CommitSig
|
|
|
|
// OpenedCircuitKeys is a set of unique identifiers for any downstream
|
|
// Add packets included in this commitment txn. After a restart, this
|
|
// set of htlcs is acked from the link's incoming mailbox to ensure
|
|
// there isn't an attempt to re-add them to this commitment txn.
|
|
OpenedCircuitKeys []models.CircuitKey
|
|
|
|
// ClosedCircuitKeys records the unique identifiers for any settle/fail
|
|
// packets that were resolved by this commitment txn. After a restart,
|
|
// this is used to ensure those circuits are removed from the circuit
|
|
// map, and the downstream packets in the link's mailbox are removed.
|
|
ClosedCircuitKeys []models.CircuitKey
|
|
|
|
// AddAcks specifies the locations (commit height, pkg index) of any
|
|
// Adds that were failed/settled in this commit diff. This will ack
|
|
// entries in *this* channel's forwarding packages.
|
|
//
|
|
// NOTE: This value is not serialized, it is used to atomically mark the
|
|
// resolution of adds, such that they will not be reprocessed after a
|
|
// restart.
|
|
AddAcks []AddRef
|
|
|
|
// SettleFailAcks specifies the locations (chan id, commit height, pkg
|
|
// index) of any Settles or Fails that were locked into this commit
|
|
// diff, and originate from *another* channel, i.e. the outgoing link.
|
|
//
|
|
// NOTE: This value is not serialized, it is used to atomically acks
|
|
// settles and fails from the forwarding packages of other channels,
|
|
// such that they will not be reforwarded internally after a restart.
|
|
SettleFailAcks []SettleFailRef
|
|
}
|