What changed, and why it matters
This commit is a pure internal code reorganization (refactor). It moves the OpenChannel data type and its methods from the channeldb package into a new chanstate package, while leaving a compatibility alias in channeldb so existing code keeps working. There are no user-facing behavior changes, no protocol changes, and no security fixes or vulnerabilities introduced in the visible diff.
No security action required. Treat as routine refactoring; standard code-review and CI validation are sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates the OpenChannel struct and its backend-neutral receiver methods from channeldb to chanstate. channeldb keeps a type alias (OpenChannel = cstate.OpenChannel) and retains the KV store implementation and serialization helpers. Tests are updated to use new store-facing accessors (SetChannelStatusForStore, ChannelStatusForStore) instead of directly touching the now-private chanStatus field. ChannelShell is also simplified from a generic type to a concrete type holding *OpenChannel. No logic changes are visible in the moved methods; they are byte-for-byte equivalent to the originals.
Changed components
channeldb/channel.gochanneldb/channel_test.gochanneldb/db.gochanneldb/db_test.gochanstate/channel.gochanstate/interface.gochanstate/open_channel.goInspect captured patch +1302 / −1271
diff --git a/channeldb/channel.go b/channeldb/channel.go
index 729e313..135565c 100644
--- a/channeldb/channel.go
+++ b/channeldb/channel.go
@@ -2,16 +2,13 @@ package channeldb
import (
"bytes"
- "crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
- "sync"
"github.com/btcsuite/btcd/btcec/v2"
- "github.com/btcsuite/btcd/btcutil/v2"
"github.com/btcsuite/btcd/chainhash/v2"
"github.com/btcsuite/btcd/wire/v2"
"github.com/btcsuite/btcwallet/walletdb"
@@ -19,8 +16,6 @@ import (
"github.com/lightningnetwork/lnd/fn/v2"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
- "github.com/lightningnetwork/lnd/htlcswitch/hop"
- "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lntypes"
@@ -228,6 +223,10 @@ const (
)
type (
+ // OpenChannel encapsulates the persistent and dynamic state of an open
+ // channel with a remote node.
+ OpenChannel = cstate.OpenChannel
+
// ChannelCommitment is a snapshot of the commitment state at a
// particular point in the commitment chain.
ChannelCommitment = cstate.ChannelCommitment
@@ -606,410 +605,6 @@ const (
FinalHtlcOffchainBit FinalHtlcByte = 1 << 1
)
-// OpenChannel encapsulates the persistent and dynamic state of an open channel
-// with a remote node. An open channel supports several options for on-disk
-// serialization depending on the exact context. Full (upon channel creation)
-// state commitments, and partial (due to a commitment update) writes are
-// supported. Each partial write due to a state update appends the new update
-// to an on-disk log, which can then subsequently be queried in order to
-// "time-travel" to a prior state.
-type OpenChannel struct {
- // ChanType denotes which type of channel this is.
- ChanType ChannelType
-
- // ChainHash is a hash which represents the blockchain that this
- // channel will be opened within. This value is typically the genesis
- // hash. In the case that the original chain went through a contentious
- // hard-fork, then this value will be tweaked using the unique fork
- // point on each branch.
- ChainHash chainhash.Hash
-
- // FundingOutpoint is the outpoint of the final funding transaction.
- // This value uniquely and globally identifies the channel within the
- // target blockchain as specified by the chain hash parameter.
- FundingOutpoint wire.OutPoint
-
- // ShortChannelID encodes the exact location in the chain in which the
- // channel was initially confirmed. This includes: the block height,
- // transaction index, and the output within the target transaction.
- //
- // If IsZeroConf(), then this will the "base" (very first) ALIAS scid
- // and the confirmed SCID will be stored in ConfirmedScid.
- ShortChannelID lnwire.ShortChannelID
-
- // IsPending indicates whether a channel's funding transaction has been
- // confirmed.
- IsPending bool
-
- // IsInitiator is a bool which indicates if we were the original
- // initiator for the channel. This value may affect how higher levels
- // negotiate fees, or close the channel.
- IsInitiator bool
-
- // chanStatus is the current status of this channel. If it is not in
- // the state Default, it should not be used for forwarding payments.
- chanStatus ChannelStatus
-
- // FundingBroadcastHeight is the height in which the funding
- // transaction was broadcast. This value can be used by higher level
- // sub-systems to determine if a channel is stale and/or should have
- // been confirmed before a certain height.
- FundingBroadcastHeight uint32
-
- // ConfirmationHeight records the block height at which the funding
- // transaction was first confirmed.
- ConfirmationHeight uint32
-
- // CloseConfirmationHeight records the block height at which the closing
- // transaction was first confirmed. This is used to track remaining
- // confirmations until the channel is considered fully closed. It is
- // None if the closing transaction has not yet been confirmed, or if
- // this data was not available (e.g. channels closed before this
- // field was introduced).
- CloseConfirmationHeight fn.Option[uint32]
-
- // NumConfsRequired is the number of confirmations a channel's funding
- // transaction must have received in order to be considered available
- // for normal transactional use.
- NumConfsRequired uint16
-
- // ChannelFlags holds the flags that were sent as part of the
- // open_channel message.
- ChannelFlags lnwire.FundingFlag
-
- // IdentityPub is the identity public key of the remote node this
- // channel has been established with.
- IdentityPub *btcec.PublicKey
-
- // Capacity is the total capacity of this channel.
- Capacity btcutil.Amount
-
- // TotalMSatSent is the total number of milli-satoshis we've sent
- // within this channel.
- TotalMSatSent lnwire.MilliSatoshi
-
- // TotalMSatReceived is the total number of milli-satoshis we've
- // received within this channel.
- TotalMSatReceived lnwire.MilliSatoshi
-
- // InitialLocalBalance is the balance we have during the channel
- // opening. When we are not the initiator, this value represents the
- // push amount.
- InitialLocalBalance lnwire.MilliSatoshi
-
- // InitialRemoteBalance is the balance they have during the channel
- // opening.
- InitialRemoteBalance lnwire.MilliSatoshi
-
- // LocalChanCfg is the channel configuration for the local node.
- LocalChanCfg ChannelConfig
-
- // RemoteChanCfg is the channel configuration for the remote node.
- RemoteChanCfg ChannelConfig
-
- // LocalCommitment is the current local commitment state for the local
- // party. This is stored distinct from the state of the remote party
- // as there are certain asymmetric parameters which affect the
- // structure of each commitment.
- LocalCommitment ChannelCommitment
-
- // RemoteCommitment is the current remote commitment state for the
- // remote party. This is stored distinct from the state of the local
- // party as there are certain asymmetric parameters which affect the
- // structure of each commitment.
- RemoteCommitment ChannelCommitment
-
- // RemoteCurrentRevocation is the current revocation for their
- // commitment transaction. However, since this the derived public key,
- // we don't yet have the private key so we aren't yet able to verify
- // that it's actually in the hash chain.
- RemoteCurrentRevocation *btcec.PublicKey
-
- // RemoteNextRevocation is the revocation key to be used for the *next*
- // commitment transaction we create for the local node. Within the
- // specification, this value is referred to as the
- // per-commitment-point.
- RemoteNextRevocation *btcec.PublicKey
-
- // RevocationProducer is used to generate the revocation in such a way
- // that remote side might store it efficiently and have the ability to
- // restore the revocation by index if needed. Current implementation of
- // secret producer is shachain producer.
- RevocationProducer shachain.Producer
-
- // RevocationStore is used to efficiently store the revocations for
- // previous channels states sent to us by remote side. Current
- // implementation of secret store is shachain store.
- RevocationStore shachain.Store
-
- // FundingTxn is the transaction containing this channel's funding
- // outpoint. Upon restarts, this txn will be rebroadcast if the channel
- // is found to be pending.
- //
- // NOTE: This value will only be populated for single-funder channels
- // for which we are the initiator, and that we also have the funding
- // transaction for. One can check this by using the HasFundingTx()
- // method on the ChanType field.
- FundingTxn *wire.MsgTx
-
- // LocalShutdownScript is set to a pre-set script if the channel was opened
- // by the local node with option_upfront_shutdown_script set. If the option
- // was not set, the field is empty.
- LocalShutdownScript lnwire.DeliveryAddress
-
- // RemoteShutdownScript is set to a pre-set script if the channel was opened
- // by the remote node with option_upfront_shutdown_script set. If the option
- // was not set, the field is empty.
- RemoteShutdownScript lnwire.DeliveryAddress
-
- // ThawHeight is the height when a frozen channel once again becomes a
- // normal channel. If this is zero, then there're no restrictions on
- // this channel. If the value is lower than 500,000, then it's
- // interpreted as a relative height, or an absolute height otherwise.
- ThawHeight uint32
-
- // LastWasRevoke is a boolean that determines if the last update we sent
- // was a revocation (true) or a commitment signature (false).
- LastWasRevoke bool
-
- // RevocationKeyLocator stores the KeyLocator information that we will
- // need to derive the shachain root for this channel. This allows us to
- // have private key isolation from lnd.
- RevocationKeyLocator keychain.KeyLocator
-
- // confirmedScid is the confirmed ShortChannelID for a zero-conf
- // channel. If the channel is unconfirmed, then this will be the
- // default ShortChannelID. This is only set for zero-conf channels.
- confirmedScid lnwire.ShortChannelID
-
- // Memo is any arbitrary information we wish to store locally about the
- // channel that will be useful to our future selves.
- Memo []byte
-
- // TapscriptRoot is an optional tapscript root used to derive the MuSig2
- // funding output.
- TapscriptRoot fn.Option[chainhash.Hash]
-
- // CustomBlob is an optional blob that can be used to store information
- // specific to a custom channel type. This information is only created
- // at channel funding time, and after wards is to be considered
- // immutable.
- CustomBlob fn.Option[tlv.Blob]
-
- // Db persists channel state through the chanstate Store contract. This
- // field intentionally keeps the existing name while the code moves from
- // channeldb toward chanstate so call sites can become backend
- // independent before the OpenChannel type itself is moved.
- Db cstate.Store[*OpenChannel]
-
- // TODO(roasbeef): just need to store local and remote HTLC's?
-
- sync.RWMutex
-}
-
-// String returns a string representation of the channel.
-func (c *OpenChannel) String() string {
- indexStr := "height=%v, local_htlc_index=%v, local_log_index=%v, " +
- "remote_htlc_index=%v, remote_log_index=%v"
-
- commit := c.LocalCommitment
- local := fmt.Sprintf(indexStr, commit.CommitHeight,
- commit.LocalHtlcIndex, commit.LocalLogIndex,
- commit.RemoteHtlcIndex, commit.RemoteLogIndex,
- )
-
- commit = c.RemoteCommitment
- remote := fmt.Sprintf(indexStr, commit.CommitHeight,
- commit.LocalHtlcIndex, commit.LocalLogIndex,
- commit.RemoteHtlcIndex, commit.RemoteLogIndex,
- )
-
- return fmt.Sprintf("SCID=%v, status=%v, initiator=%v, pending=%v, "+
- "local commitment has %s, remote commitment has %s",
- c.ShortChannelID, c.chanStatus, c.IsInitiator, c.IsPending,
- local, remote,
- )
-}
-
-// Initiator returns the ChannelParty that originally opened this channel.
-func (c *OpenChannel) Initiator() lntypes.ChannelParty {
- c.RLock()
- defer c.RUnlock()
-
- if c.IsInitiator {
- return lntypes.Local
- }
-
- return lntypes.Remote
-}
-
-// ShortChanID returns the current ShortChannelID of this channel.
-func (c *OpenChannel) ShortChanID() lnwire.ShortChannelID {
- c.RLock()
- defer c.RUnlock()
-
- return c.ShortChannelID
-}
-
-// ZeroConfRealScid returns the zero-conf channel's confirmed scid. This should
-// only be called if IsZeroConf returns true.
-func (c *OpenChannel) ZeroConfRealScid() lnwire.ShortChannelID {
- c.RLock()
- defer c.RUnlock()
-
- return c.confirmedScid
-}
-
-// ZeroConfConfirmed returns whether the zero-conf channel has confirmed. This
-// should only be called if IsZeroConf returns true.
-func (c *OpenChannel) ZeroConfConfirmed() bool {
- c.RLock()
- defer c.RUnlock()
-
- return c.confirmedScid != hop.Source
-}
-
-// IsZeroConf returns whether the option_zeroconf channel type was negotiated.
-func (c *OpenChannel) IsZeroConf() bool {
- c.RLock()
- defer c.RUnlock()
-
- return c.ChanType.HasZeroConf()
-}
-
-// IsOptionScidAlias returns whether the option_scid_alias channel type was
-// negotiated.
-func (c *OpenChannel) IsOptionScidAlias() bool {
- c.RLock()
- defer c.RUnlock()
-
- return c.ChanType.HasScidAliasChan()
-}
-
-// NegotiatedAliasFeature returns whether the option-scid-alias feature bit was
-// negotiated.
-func (c *OpenChannel) NegotiatedAliasFeature() bool {
- c.RLock()
- defer c.RUnlock()
-
- return c.ChanType.HasScidAliasFeature()
-}
-
-// ChanStatus returns the current ChannelStatus of this channel.
-func (c *OpenChannel) ChanStatus() ChannelStatus {
- c.RLock()
- defer c.RUnlock()
-
- return c.chanStatus
-}
-
-// ChannelStatusForStore returns the in-memory channel status without taking
-// the channel mutex.
-//
-// NOTE: This is a preliminary migration hook for KV-backed store code that
-// still lives in channeldb while OpenChannel moves toward chanstate. Callers
-// are responsible for synchronization. Normal callers should use ChanStatus.
-func (c *OpenChannel) ChannelStatusForStore() ChannelStatus {
- return c.chanStatus
-}
-
-// SetChannelStatusForStore updates the in-memory channel status without taking
-// the channel mutex.
-//
-// NOTE: This is a preliminary migration hook for KV-backed store code that
-// still lives in channeldb while OpenChannel moves toward chanstate. Callers
-// are responsible for synchronization. Normal callers should use
-// ApplyChanStatus or ClearChanStatus when the status change must be persisted.
-func (c *OpenChannel) SetChannelStatusForStore(status ChannelStatus) {
- c.chanStatus = status
-}
-
-// ApplyChanStatus allows the caller to modify the internal channel state in a
-// thead-safe manner.
-func (c *OpenChannel) ApplyChanStatus(status ChannelStatus) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.ApplyChannelStatus(c, status)
-}
-
-// ClearChanStatus allows the caller to clear a particular channel status from
-// the primary channel status bit field. After this method returns, a call to
-// HasChanStatus(status) should return false.
-func (c *OpenChannel) ClearChanStatus(status ChannelStatus) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.ClearChannelStatus(c, status)
-}
-
-// HasChanStatus returns true if the internal bitfield channel status of the
-// target channel has the specified status bit set.
-func (c *OpenChannel) HasChanStatus(status ChannelStatus) bool {
- c.RLock()
- defer c.RUnlock()
-
- return c.hasChanStatus(status)
-}
-
-func (c *OpenChannel) hasChanStatus(status ChannelStatus) bool {
- // Special case ChanStatusDefualt since it isn't actually flag, but a
- // particular combination (or lack-there-of) of flags.
- if status == ChanStatusDefault {
- return c.chanStatus == ChanStatusDefault
- }
-
- return c.chanStatus&status == status
-}
-
-// HasChanStatusForStore returns true if the internal bitfield channel status
-// has the specified status bit set, without taking the channel mutex.
-//
-// NOTE: This is a preliminary migration hook for KV-backed store code that
-// still lives in channeldb while OpenChannel moves toward chanstate. Callers
-// are responsible for synchronization. Normal callers should use
-// HasChanStatus.
-func (c *OpenChannel) HasChanStatusForStore(status ChannelStatus) bool {
- return c.hasChanStatus(status)
-}
-
-// ConfirmedScidForStore returns the in-memory confirmed SCID without taking
-// the channel mutex.
-//
-// NOTE: This is a preliminary migration hook for KV-backed store code that
-// still lives in channeldb while OpenChannel moves toward chanstate. Callers
-// are responsible for synchronization. Normal callers should use
-// ZeroConfRealScid.
-func (c *OpenChannel) ConfirmedScidForStore() lnwire.ShortChannelID {
- return c.confirmedScid
-}
-
-// SetConfirmedScidForStore updates the in-memory confirmed SCID without taking
-// the channel mutex.
-//
-// NOTE: This is a preliminary migration hook for KV-backed store code that
-// still lives in channeldb while OpenChannel moves toward chanstate. Callers
-// are responsible for synchronization.
-func (c *OpenChannel) SetConfirmedScidForStore(scid lnwire.ShortChannelID) {
- c.confirmedScid = scid
-}
-
-// BroadcastHeight returns the height at which the funding tx was broadcast.
-func (c *OpenChannel) BroadcastHeight() uint32 {
- c.RLock()
- defer c.RUnlock()
-
- return c.FundingBroadcastHeight
-}
-
-// SetBroadcastHeight sets the FundingBroadcastHeight.
-func (c *OpenChannel) SetBroadcastHeight(height uint32) {
- c.Lock()
- defer c.Unlock()
-
- c.FundingBroadcastHeight = height
-}
-
// amendOpenChannelTlvData updates the channel with the given auxiliary TLV
// data.
func amendOpenChannelTlvData(channel *OpenChannel, auxData openChannelTlvData) {
@@ -1082,15 +677,6 @@ func extractOpenChannelTlvData(channel *OpenChannel) openChannelTlvData {
return auxData
}
-// Refresh updates the in-memory channel state using the latest state observed
-// on disk.
-func (c *OpenChannel) Refresh() error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.RefreshChannel(c)
-}
-
// RefreshChannel updates the in-memory channel state using the latest state
// observed on disk.
func (c *ChannelStateDB) RefreshChannel(channel *OpenChannel) error {
@@ -1358,21 +944,6 @@ func fullSyncOpenChannel(tx kvdb.RwTx, c *OpenChannel) error {
return putOpenChannel(chanBucket, c)
}
-// MarkConfirmationHeight updates the channel's confirmation height once the
-// channel opening transaction receives one confirmation.
-func (c *OpenChannel) MarkConfirmationHeight(height uint32) error {
- c.Lock()
- defer c.Unlock()
-
- if err := c.Db.MarkChannelConfirmationHeight(c, height); err != nil {
- return err
- }
-
- c.ConfirmationHeight = height
-
- return nil
-}
-
// MarkChannelConfirmationHeight updates the channel's confirmation height once
// the channel opening transaction receives one confirmation.
func (c *ChannelStateDB) MarkChannelConfirmationHeight(channel *OpenChannel,
@@ -1400,30 +971,6 @@ func (c *ChannelStateDB) MarkChannelConfirmationHeight(channel *OpenChannel,
}, func() {})
}
-// ResetCloseConfirmationHeight clears the channel's close confirmation height
-// when the spending transaction is reorged out.
-func (c *OpenChannel) ResetCloseConfirmationHeight() error {
- return c.MarkCloseConfirmationHeight(fn.None[uint32]())
-}
-
-// MarkCloseConfirmationHeight updates the channel's close confirmation height
-// when the closing transaction is first detected in a block (spend height).
-func (c *OpenChannel) MarkCloseConfirmationHeight(
- height fn.Option[uint32]) error {
-
- c.Lock()
- defer c.Unlock()
-
- err := c.Db.MarkChannelCloseConfirmationHeight(c, height)
- if err != nil {
- return err
- }
-
- c.CloseConfirmationHeight = height
-
- return nil
-}
-
// MarkChannelCloseConfirmationHeight updates the channel's close confirmation
// height when the closing transaction is first detected in a block.
func (c *ChannelStateDB) MarkChannelCloseConfirmationHeight(
@@ -1451,22 +998,6 @@ func (c *ChannelStateDB) MarkChannelCloseConfirmationHeight(
}, func() {})
}
-// MarkAsOpen marks a channel as fully open given a locator that uniquely
-// describes its location within the chain.
-func (c *OpenChannel) MarkAsOpen(openLoc lnwire.ShortChannelID) error {
- c.Lock()
- defer c.Unlock()
-
- if err := c.Db.MarkChannelOpen(c, openLoc); err != nil {
- return err
- }
-
- c.IsPending = false
- c.ShortChannelID = openLoc
-
- return nil
-}
-
// MarkChannelOpen marks a channel as fully open given a locator that uniquely
// describes its location within the chain.
func (c *ChannelStateDB) MarkChannelOpen(channel *OpenChannel,
@@ -1495,21 +1026,6 @@ func (c *ChannelStateDB) MarkChannelOpen(channel *OpenChannel,
}, func() {})
}
-// MarkRealScid marks the zero-conf channel's confirmed ShortChannelID. This
-// should only be done if IsZeroConf returns true.
-func (c *OpenChannel) MarkRealScid(realScid lnwire.ShortChannelID) error {
- c.Lock()
- defer c.Unlock()
-
- if err := c.Db.MarkChannelRealScid(c, realScid); err != nil {
- return err
- }
-
- c.confirmedScid = realScid
-
- return nil
-}
-
// MarkChannelRealScid marks the zero-conf channel's confirmed ShortChannelID.
func (c *ChannelStateDB) MarkChannelRealScid(channel *OpenChannel,
realScid lnwire.ShortChannelID) error {
@@ -1536,21 +1052,6 @@ func (c *ChannelStateDB) MarkChannelRealScid(channel *OpenChannel,
}, func() {})
}
-// MarkScidAliasNegotiated adds ScidAliasFeatureBit to ChanType in-memory and
-// in the database.
-func (c *OpenChannel) MarkScidAliasNegotiated() error {
- c.Lock()
- defer c.Unlock()
-
- if err := c.Db.MarkChannelScidAliasNegotiated(c); err != nil {
- return err
- }
-
- c.ChanType |= ScidAliasFeatureBit
-
- return nil
-}
-
// MarkChannelScidAliasNegotiated adds ScidAliasFeatureBit to ChanType in the
// database.
func (c *ChannelStateDB) MarkChannelScidAliasNegotiated(
@@ -1578,16 +1079,6 @@ func (c *ChannelStateDB) MarkChannelScidAliasNegotiated(
}, func() {})
}
-// MarkDataLoss marks sets the channel status to LocalDataLoss and stores the
-// passed commitPoint for use to retrieve funds in case the remote force closes
-// the channel.
-func (c *OpenChannel) MarkDataLoss(commitPoint *btcec.PublicKey) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.MarkChannelDataLoss(c, commitPoint)
-}
-
// MarkChannelDataLoss marks the channel as local-data-loss and stores the
// commit point needed if the remote force closes.
func (c *ChannelStateDB) MarkChannelDataLoss(channel *OpenChannel,
@@ -1605,12 +1096,6 @@ func (c *ChannelStateDB) MarkChannelDataLoss(channel *OpenChannel,
return c.putChanStatus(channel, ChanStatusLocalDataLoss, putCommitPoint)
}
-// DataLossCommitPoint retrieves the stored commit point set during
-// MarkDataLoss. If not found ErrNoCommitPoint is returned.
-func (c *OpenChannel) DataLossCommitPoint() (*btcec.PublicKey, error) {
- return c.Db.FetchChannelDataLossCommitPoint(c)
-}
-
// FetchChannelDataLossCommitPoint retrieves the commit point stored when the
// channel was marked as local-data-loss.
func (c *ChannelStateDB) FetchChannelDataLossCommitPoint(
@@ -1651,37 +1136,11 @@ func (c *ChannelStateDB) FetchChannelDataLossCommitPoint(
return commitPoint, nil
}
-// MarkBorked marks the event when the channel as reached an irreconcilable
-// state, such as a channel breach or state desynchronization. Borked channels
-// should never be added to the switch.
-func (c *OpenChannel) MarkBorked() error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.MarkChannelBorked(c)
-}
-
// MarkChannelBorked marks the channel as irreconcilable.
func (c *ChannelStateDB) MarkChannelBorked(channel *OpenChannel) error {
return c.ApplyChannelStatus(channel, ChanStatusBorked)
}
-// SecondCommitmentPoint returns the second per-commitment-point for use in the
-// channel_ready message.
-func (c *OpenChannel) SecondCommitmentPoint() (*btcec.PublicKey, error) {
- c.RLock()
- defer c.RUnlock()
-
- // Since we start at commitment height = 0, the second per commitment
- // point is actually at the 1st index.
- revocation, err := c.RevocationProducer.AtIndex(1)
- if err != nil {
- return nil, err
- }
-
- return input.ComputeCommitmentPoint(revocation[:]), nil
-}
-
var (
// DeriveMusig2Shachain derives a shachain producer for the taproot
// channel from normal shachain revocation root.
@@ -1692,145 +1151,6 @@ var (
NewMusigVerificationNonce = cstate.NewMusigVerificationNonce
)
-// ChanSyncMsg returns the ChannelReestablish message that should be sent upon
-// reconnection with the remote peer that we're maintaining this channel with.
-// The information contained within this message is necessary to re-sync our
-// commitment chains in the case of a last or only partially processed message.
-// When the remote party receives this message one of three things may happen:
-//
-// 1. We're fully synced and no messages need to be sent.
-// 2. We didn't get the last CommitSig message they sent, so they'll re-send
-// it.
-// 3. We didn't get the last RevokeAndAck message they sent, so they'll
-// re-send it.
-//
-// If this is a restored channel, having status ChanStatusRestored, then we'll
-// modify our typical chan sync message to ensure they force close even if
-// we're on the very first state.
-func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
-
- c.Lock()
- defer c.Unlock()
-
- // The remote commitment height that we'll send in the
- // ChannelReestablish message is our current commitment height plus
- // one. If the receiver thinks that our commitment height is actually
- // *equal* to this value, then they'll re-send the last commitment that
- // they sent but we never fully processed.
- localHeight := c.LocalCommitment.CommitHeight
- nextLocalCommitHeight := localHeight + 1
-
- // The second value we'll send is the height of the remote commitment
- // from our PoV. If the receiver thinks that their height is actually
- // *one plus* this value, then they'll re-send their last revocation.
- remoteChainTipHeight := c.RemoteCommitment.CommitHeight
-
- // If this channel has undergone a commitment update, then in order to
- // prove to the remote party our knowledge of their prior commitment
- // state, we'll also send over the last commitment secret that the
- // remote party sent.
- var lastCommitSecret [32]byte
- if remoteChainTipHeight != 0 {
- remoteSecret, err := c.RevocationStore.LookUp(
- remoteChainTipHeight - 1,
- )
- if err != nil {
- return nil, err
- }
- lastCommitSecret = [32]byte(*remoteSecret)
- }
-
- // Additionally, we'll send over the current unrevoked commitment on
- // our local commitment transaction.
- currentCommitSecret, err := c.RevocationProducer.AtIndex(
- localHeight,
- )
- if err != nil {
- return nil, err
- }
-
- // If we've restored this channel, then we'll purposefully give them an
- // invalid LocalUnrevokedCommitPoint so they'll force close the channel
- // allowing us to sweep our funds.
- if c.hasChanStatus(ChanStatusRestored) {
- currentCommitSecret[0] ^= 1
-
- // If this is a tweakless channel, then we'll purposefully send
- // a next local height taht's invalid to trigger a force close
- // on their end. We do this as tweakless channels don't require
- // that the commitment point is valid, only that it's present.
- if c.ChanType.IsTweakless() {
- nextLocalCommitHeight = 0
- }
- }
-
- // If this is a taproot channel, then we'll need to generate our next
- // verification nonce to send to the remote party. They'll use this to
- // sign the next update to our commitment transaction.
- var (
- nextTaprootNonce lnwire.OptMusig2NonceTLV
- nextLocalNonces lnwire.OptLocalNonces
- )
- if c.ChanType.IsTaproot() {
- taprootRevProducer, err := DeriveMusig2Shachain(
- c.RevocationProducer,
- )
- if err != nil {
- return nil, err
- }
-
- nextNonce, err := NewMusigVerificationNonce(
- c.LocalChanCfg.MultiSigKey.PubKey,
- nextLocalCommitHeight, taprootRevProducer,
- )
- if err != nil {
- return nil, fmt.Errorf("unable to gen next "+
- "nonce: %w", err)
- }
-
- fundingTxid := c.FundingOutpoint.Hash
- nonce := nextNonce.PubNonce
-
- // Final taproot channels use the map-based LocalNonces
- // field keyed by funding TXID. Staging channels use the
- // legacy single LocalNonce field.
- if c.ChanType.IsTaprootFinal() {
- noncesMap := make(map[chainhash.Hash]lnwire.Musig2Nonce)
- noncesMap[fundingTxid] = nonce
- nextLocalNonces = lnwire.SomeLocalNonces(
- lnwire.LocalNoncesData{NoncesMap: noncesMap},
- )
- } else {
- nextTaprootNonce = lnwire.SomeMusig2Nonce(nonce)
- }
- }
-
- return &lnwire.ChannelReestablish{
- ChanID: lnwire.NewChanIDFromOutPoint(
- c.FundingOutpoint,
- ),
- NextLocalCommitHeight: nextLocalCommitHeight,
- RemoteCommitTailHeight: remoteChainTipHeight,
- LastRemoteCommitSecret: lastCommitSecret,
- LocalUnrevokedCommitPoint: input.ComputeCommitmentPoint(
- currentCommitSecret[:],
- ),
- LocalNonce: nextTaprootNonce,
- LocalNonces: nextLocalNonces,
- }, nil
-}
-
-// MarkShutdownSent serialises and persist the given ShutdownInfo for this
-// channel. Persisting this info represents the fact that we have sent the
-// Shutdown message to the remote side and hence that we should re-transmit the
-// same Shutdown message on re-establish.
-func (c *OpenChannel) MarkShutdownSent(info *ShutdownInfo) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.StoreChannelShutdownInfo(c, info)
-}
-
// StoreChannelShutdownInfo persists the ShutdownInfo for the target channel.
func (c *ChannelStateDB) StoreChannelShutdownInfo(channel *OpenChannel,
info *ShutdownInfo) error {
@@ -1854,16 +1174,6 @@ func (c *ChannelStateDB) StoreChannelShutdownInfo(channel *OpenChannel,
}, func() {})
}
-// ShutdownInfo decodes the shutdown info stored for this channel and returns
-// the result. If no shutdown info has been persisted for this channel then the
-// ErrNoShutdownInfo error is returned.
-func (c *OpenChannel) ShutdownInfo() (fn.Option[ShutdownInfo], error) {
- c.RLock()
- defer c.RUnlock()
-
- return c.Db.FetchChannelShutdownInfo(c)
-}
-
// FetchChannelShutdownInfo fetches the persisted ShutdownInfo for the target
// channel.
func (c *ChannelStateDB) FetchChannelShutdownInfo(
@@ -1922,18 +1232,6 @@ func isChannelBorked(channel *OpenChannel, chanBucket kvdb.RBucket) (
return diskChannel.ChannelStatusForStore() != ChanStatusDefault, nil
}
-// MarkCommitmentBroadcasted marks the channel as a commitment transaction has
-// been broadcast, either our own or the remote, and we should watch the chain
-// for it to confirm before taking any further action. It takes as argument the
-// closing tx _we believe_ will appear in the chain. This is only used to
-// republish this tx at startup to ensure propagation, and we should still
-// handle the case where a different tx actually hits the chain.
-func (c *OpenChannel) MarkCommitmentBroadcasted(closeTx *wire.MsgTx,
- closer lntypes.ChannelParty) error {
-
- return c.Db.MarkChannelCommitmentBroadcasted(c, closeTx, closer)
-}
-
// MarkChannelCommitmentBroadcasted marks the channel as having a commitment
// transaction broadcast.
func (c *ChannelStateDB) MarkChannelCommitmentBroadcasted(
@@ -1946,19 +1244,6 @@ func (c *ChannelStateDB) MarkChannelCommitmentBroadcasted(
)
}
-// MarkCoopBroadcasted marks the channel to indicate that a cooperative close
-// transaction has been broadcast, either our own or the remote, and that we
-// should watch the chain for it to confirm before taking further action. It
-// takes as argument a cooperative close tx that could appear on chain, and
-// should be rebroadcast upon startup. This is only used to republish and
-// ensure propagation, and we should still handle the case where a different tx
-// actually hits the chain.
-func (c *OpenChannel) MarkCoopBroadcasted(closeTx *wire.MsgTx,
- closer lntypes.ChannelParty) error {
-
- return c.Db.MarkChannelCoopBroadcasted(c, closeTx, closer)
-}
-
// MarkChannelCoopBroadcasted marks the channel as having a cooperative close
// transaction broadcast.
func (c *ChannelStateDB) MarkChannelCoopBroadcasted(channel *OpenChannel,
@@ -2005,12 +1290,6 @@ func (c *ChannelStateDB) markBroadcasted(channel *OpenChannel,
return c.putChanStatus(channel, status, putClosingTx)
}
-// BroadcastedCommitment retrieves the stored unilateral closing tx set during
-// MarkCommitmentBroadcasted. If not found ErrNoCloseTx is returned.
-func (c *OpenChannel) BroadcastedCommitment() (*wire.MsgTx, error) {
- return c.Db.FetchChannelBroadcastedCommitment(c)
-}
-
// FetchChannelBroadcastedCommitment fetches the stored unilateral closing
// transaction.
func (c *ChannelStateDB) FetchChannelBroadcastedCommitment(
@@ -2019,12 +1298,6 @@ func (c *ChannelStateDB) FetchChannelBroadcastedCommitment(
return c.getClosingTx(channel, forceCloseTxKey)
}
-// BroadcastedCooperative retrieves the stored cooperative closing tx set during
-// MarkCoopBroadcasted. If not found ErrNoCloseTx is returned.
-func (c *OpenChannel) BroadcastedCooperative() (*wire.MsgTx, error) {
- return c.Db.FetchChannelBroadcastedCooperative(c)
-}
-
// FetchChannelBroadcastedCooperative fetches the stored cooperative closing
// transaction.
func (c *ChannelStateDB) FetchChannelBroadcastedCooperative(
@@ -2245,24 +1518,6 @@ func fetchOpenChannel(chanBucket kvdb.RBucket,
return channel, nil
}
-// SyncPending writes the contents of the channel to the database while it's in
-// the pending (waiting for funding confirmation) state. The IsPending flag
-// will be set to true. When the channel's funding transaction is confirmed,
-// the channel should be marked as "open" and the IsPending flag set to false.
-// Note that this function also creates a LinkNode relationship between this
-// newly created channel and a new LinkNode instance. This allows listing all
-// channels in the database globally, or according to the LinkNode they were
-// created with.
-//
-// TODO(roasbeef): addr param should eventually be an lnwire.NetAddress type
-// that includes service bits.
-func (c *OpenChannel) SyncPending(addr net.Addr, pendingHeight uint32) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.SyncPendingChannel(c, addr, pendingHeight)
-}
-
// SyncPendingChannel writes a pending channel to the store and records the
// funding broadcast height.
func (c *ChannelStateDB) SyncPendingChannel(channel *OpenChannel,
@@ -2310,43 +1565,6 @@ func syncNewChannel(tx kvdb.RwTx, c *OpenChannel, addrs []net.Addr,
return putLinkNode(nodeInfoBucket, linkNode)
}
-// UpdateCommitment updates the local commitment state. It locks in the pending
-// local updates that were received by us from the remote party. The commitment
-// state completely describes the balance state at this point in the commitment
-// chain. In addition to that, it persists all the remote log updates that we
-// have acked, but not signed a remote commitment for yet. These need to be
-// persisted to be able to produce a valid commit signature if a restart would
-// occur. This method its to be called when we revoke our prior commitment
-// state.
-//
-// A map is returned of all the htlc resolutions that were locked in this
-// commitment. Keys correspond to htlc indices and values indicate whether the
-// htlc was settled or failed.
-func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment,
- unsignedAckedUpdates []LogUpdate) (map[uint64]bool, error) {
-
- c.Lock()
- defer c.Unlock()
-
- // If this is a restored channel, then we want to avoid mutating the
- // state as all, as it's impossible to do so in a protocol compliant
- // manner.
- if c.hasChanStatus(ChanStatusRestored) {
- return nil, ErrNoRestoredChannelMutation
- }
-
- finalHtlcs, err := c.Db.UpdateChannelCommitment(
- c, newCommitment, unsignedAckedUpdates,
- )
- if err != nil {
- return nil, err
- }
-
- c.LocalCommitment = *newCommitment
-
- return finalHtlcs, nil
-}
-
// UpdateChannelCommitment updates the local commitment state.
func (c *ChannelStateDB) UpdateChannelCommitment(channel *OpenChannel,
newCommitment *ChannelCommitment,
@@ -2528,48 +1746,6 @@ func processFinalHtlc(finalHtlcsBucket walletdb.ReadWriteBucket, upd LogUpdate,
return nil
}
-// ActiveHtlcs returns a slice of HTLC's which are currently active on *both*
-// commitment transactions.
-func (c *OpenChannel) ActiveHtlcs() []HTLC {
- c.RLock()
- defer c.RUnlock()
-
- // We'll only return HTLC's that are locked into *both* commitment
- // transactions. So we'll iterate through their set of HTLC's to note
- // which ones are present on their commitment.
- remoteHtlcs := make(map[[32]byte]struct{})
- for _, htlc := range c.RemoteCommitment.Htlcs {
- log.Tracef("RemoteCommitment has htlc: id=%v, update=%v "+
- "incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
- htlc.Incoming)
-
- onionHash := sha256.Sum256(htlc.OnionBlob[:])
- remoteHtlcs[onionHash] = struct{}{}
- }
-
- // Now that we know which HTLC's they have, we'll only mark the HTLC's
- // as active if *we* know them as well.
- activeHtlcs := make([]HTLC, 0, len(remoteHtlcs))
- for _, htlc := range c.LocalCommitment.Htlcs {
- log.Tracef("LocalCommitment has htlc: id=%v, update=%v "+
- "incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
- htlc.Incoming)
-
- onionHash := sha256.Sum256(htlc.OnionBlob[:])
- if _, ok := remoteHtlcs[onionHash]; !ok {
- log.Tracef("Skipped htlc due to onion mismatched: "+
- "id=%v, update=%v incoming=%v",
- htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming)
-
- continue
- }
-
- activeHtlcs = append(activeHtlcs, htlc)
- }
-
- return activeHtlcs
-}
-
// serializeHtlcExtraData encodes a TLV stream of extra data to be stored with a
// HTLC. It uses the update_add_htlc TLV types, because this is where extra
// data is passed with a HTLC. At present blinding points are the only extra
@@ -2921,26 +2097,6 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) {
return &d, nil
}
-// AppendRemoteCommitChain appends a new CommitDiff to the end of the
-// commitment chain for the remote party. This method is to be used once we
-// have prepared a new commitment state for the remote party, but before we
-// transmit it to the remote party. The contents of the argument should be
-// sufficient to retransmit the updates and signature needed to reconstruct the
-// state in full, in the case that we need to retransmit.
-func (c *OpenChannel) AppendRemoteCommitChain(diff *CommitDiff) error {
- c.Lock()
- defer c.Unlock()
-
- // If this is a restored channel, then we want to avoid mutating the
- // state at all, as it's impossible to do so in a protocol compliant
- // manner.
- if c.hasChanStatus(ChanStatusRestored) {
- return ErrNoRestoredChannelMutation
- }
-
- return c.Db.AppendRemoteCommitChain(c, diff)
-}
-
// AppendRemoteCommitChain appends a new CommitDiff to the remote party's
// commitment chain.
func (c *ChannelStateDB) AppendRemoteCommitChain(channel *OpenChannel,
@@ -3013,16 +2169,6 @@ func (c *ChannelStateDB) AppendRemoteCommitChain(channel *OpenChannel,
}, func() {})
}
-// RemoteCommitChainTip returns the "tip" of the current remote commitment
-// chain. This value will be non-nil iff, we've created a new commitment for
-// the remote party that they haven't yet ACK'd. In this case, their commitment
-// chain will have a length of two: their current unrevoked commitment, and
-// this new pending commitment. Once they revoked their prior state, we'll swap
-// these pointers, causing the tip and the tail to point to the same entry.
-func (c *OpenChannel) RemoteCommitChainTip() (*CommitDiff, error) {
- return c.Db.RemoteCommitChainTip(c)
-}
-
// RemoteCommitChainTip returns the "tip" of the current remote commitment
// chain.
func (c *ChannelStateDB) RemoteCommitChainTip(channel *OpenChannel) (
@@ -3065,12 +2211,6 @@ func (c *ChannelStateDB) RemoteCommitChainTip(channel *OpenChannel) (
return cd, nil
}
-// UnsignedAckedUpdates retrieves the persisted unsigned acked remote log
-// updates that still need to be signed for.
-func (c *OpenChannel) UnsignedAckedUpdates() ([]LogUpdate, error) {
- return c.Db.UnsignedAckedUpdates(c)
-}
-
// UnsignedAckedUpdates retrieves the persisted unsigned acked remote log
// updates that still need to be signed for.
func (c *ChannelStateDB) UnsignedAckedUpdates(channel *OpenChannel) (
@@ -3108,12 +2248,6 @@ func (c *ChannelStateDB) UnsignedAckedUpdates(channel *OpenChannel) (
return updates, nil
}
-// RemoteUnsignedLocalUpdates retrieves the persisted, unsigned local log
-// updates that the remote still needs to sign for.
-func (c *OpenChannel) RemoteUnsignedLocalUpdates() ([]LogUpdate, error) {
- return c.Db.RemoteUnsignedLocalUpdates(c)
-}
-
// RemoteUnsignedLocalUpdates retrieves the persisted, unsigned local log
// updates that the remote still needs to sign for.
func (c *ChannelStateDB) RemoteUnsignedLocalUpdates(channel *OpenChannel) (
@@ -3152,20 +2286,6 @@ func (c *ChannelStateDB) RemoteUnsignedLocalUpdates(channel *OpenChannel) (
return updates, nil
}
-// InsertNextRevocation inserts the _next_ commitment point (revocation) into
-// the database, and also modifies the internal RemoteNextRevocation attribute
-// to point to the passed key. This method is to be using during final channel
-// set up, _after_ the channel has been fully confirmed.
-//
-// NOTE: If this method isn't called, then the target channel won't be able to
-// propose new states for the commitment state of the remote party.
-func (c *OpenChannel) InsertNextRevocation(revKey *btcec.PublicKey) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.InsertNextRevocation(c, revKey)
-}
-
// InsertNextRevocation inserts the next commitment point into the persisted
// channel state.
func (c *ChannelStateDB) InsertNextRevocation(channel *OpenChannel,
@@ -3191,33 +2311,6 @@ func (c *ChannelStateDB) InsertNextRevocation(channel *OpenChannel,
return nil
}
-// AdvanceCommitChainTail records the new state transition within an on-disk
-// append-only log which records all state transitions by the remote peer. In
-// the case of an uncooperative broadcast of a prior state by the remote peer,
-// this log can be consulted in order to reconstruct the state needed to
-// rectify the situation. This method will add the current commitment for the
-// remote party to the revocation log, and promote the current pending
-// commitment to the current remote commitment. The updates parameter is the
-// set of local updates that the peer still needs to send us a signature for.
-// We store this set of updates in case we go down.
-func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg,
- updates []LogUpdate, ourOutputIndex, theirOutputIndex uint32) error {
-
- c.Lock()
- defer c.Unlock()
-
- // If this is a restored channel, then we want to avoid mutating the
- // state at all, as it's impossible to do so in a protocol compliant
- // manner.
- if c.hasChanStatus(ChanStatusRestored) {
- return ErrNoRestoredChannelMutation
- }
-
- return c.Db.AdvanceCommitChainTail(
- c, fwdPkg, updates, ourOutputIndex, theirOutputIndex,
- )
-}
-
// AdvanceCommitChainTail records the new state transition within the
// revocation log and promotes the pending remote commitment to the current
// remote commitment.
@@ -3400,39 +2493,6 @@ func putFinalHtlc(finalHtlcsBucket kvdb.RwBucket, id uint64,
return finalHtlcsBucket.Put(key[:], []byte{byte(finalHtlcByte)})
}
-// NextLocalHtlcIndex returns the next unallocated local htlc index. To ensure
-// this always returns the next index that has been not been allocated, this
-// will first try to examine any pending commitments, before falling back to the
-// last locked-in remote commitment.
-func (c *OpenChannel) NextLocalHtlcIndex() (uint64, error) {
- // First, load the most recent commit diff that we initiated for the
- // remote party. If no pending commit is found, this is not treated as
- // a critical error, since we can always fall back.
- pendingRemoteCommit, err := c.RemoteCommitChainTip()
- if err != nil && err != ErrNoPendingCommit {
- return 0, err
- }
-
- // If a pending commit was found, its local htlc index will be at least
- // as large as the one on our local commitment.
- if pendingRemoteCommit != nil {
- return pendingRemoteCommit.Commitment.LocalHtlcIndex, nil
- }
-
- // Otherwise, fallback to using the local htlc index of their commitment.
- return c.RemoteCommitment.LocalHtlcIndex, nil
-}
-
-// LoadFwdPkgs scans the forwarding log for any packages that haven't been
-// processed, and returns their deserialized log updates in map indexed by the
-// remote commitment height at which the updates were locked in.
-func (c *OpenChannel) LoadFwdPkgs() ([]*FwdPkg, error) {
- c.RLock()
- defer c.RUnlock()
-
- return c.Db.LoadFwdPkgs(c)
-}
-
// LoadFwdPkgs scans the forwarding log for any packages that haven't been
// processed, and returns their deserialized log updates in map indexed by the
// remote commitment height at which the updates were locked in.
@@ -3454,16 +2514,6 @@ func (c *ChannelStateDB) LoadFwdPkgs(channel *OpenChannel) ([]*FwdPkg,
return fwdPkgs, nil
}
-// AckAddHtlcs updates the AckAddFilter containing any of the provided AddRefs
-// indicating that a response to this Add has been committed to the remote party.
-// Doing so will prevent these Add HTLCs from being reforwarded internally.
-func (c *OpenChannel) AckAddHtlcs(addRefs ...AddRef) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.AckAddHtlcs(c, addRefs...)
-}
-
// AckAddHtlcs updates the AckAddFilter containing any of the provided AddRefs
// indicating that a response to this Add has been committed to the remote party.
// Doing so will prevent these Add HTLCs from being reforwarded internally.
@@ -3476,17 +2526,6 @@ func (c *ChannelStateDB) AckAddHtlcs(channel *OpenChannel,
}, func() {})
}
-// AckSettleFails updates the SettleFailFilter containing any of the provided
-// SettleFailRefs, indicating that the response has been delivered to the
-// incoming link, corresponding to a particular AddRef. Doing so will prevent
-// the responses from being retransmitted internally.
-func (c *OpenChannel) AckSettleFails(settleFailRefs ...SettleFailRef) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.AckSettleFails(c, settleFailRefs...)
-}
-
// AckSettleFails updates the SettleFailFilter containing any of the provided
// SettleFailRefs, indicating that the response has been delivered to the
// incoming link, corresponding to a particular AddRef. Doing so will prevent
@@ -3500,15 +2539,6 @@ func (c *ChannelStateDB) AckSettleFails(channel *OpenChannel,
}, func() {})
}
-// SetFwdFilter atomically sets the forwarding filter for the forwarding package
-// identified by `height`.
-func (c *OpenChannel) SetFwdFilter(height uint64, fwdFilter *PkgFilter) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.SetFwdFilter(c, height, fwdFilter)
-}
-
// SetFwdFilter atomically sets the forwarding filter for the forwarding package
// identified by `height`.
func (c *ChannelStateDB) SetFwdFilter(channel *OpenChannel, height uint64,
@@ -3520,18 +2550,6 @@ func (c *ChannelStateDB) SetFwdFilter(channel *OpenChannel, height uint64,
}, func() {})
}
-// RemoveFwdPkgs atomically removes forwarding packages specified by the remote
-// commitment heights. If one of the intermediate RemovePkg calls fails, then the
-// later packages won't be removed.
-//
-// NOTE: This method should only be called on packages marked FwdStateCompleted.
-func (c *OpenChannel) RemoveFwdPkgs(heights ...uint64) error {
- c.Lock()
- defer c.Unlock()
-
- return c.Db.RemoveFwdPkgs(c, heights...)
-}
-
// RemoveFwdPkgs atomically removes forwarding packages specified by the remote
// commitment heights. If one of the intermediate RemovePkg calls fails, then the
// later packages won't be removed.
@@ -3597,18 +2615,6 @@ func (c *ChannelStateDB) revocationLogTailCommitHeight(
return height, nil
}
-// CommitmentHeight returns the current commitment height. The commitment
-// height represents the number of updates to the commitment state to date.
-// This value is always monotonically increasing. This method is provided in
-// order to allow multiple instances of a particular open channel to obtain a
-// consistent view of the number of channel updates to date.
-func (c *OpenChannel) CommitmentHeight() (uint64, error) {
- c.RLock()
- defer c.RUnlock()
-
- return c.Db.CommitmentHeight(c)
-}
-
// CommitmentHeight returns the current commitment height. The commitment
// height represents the number of updates to the commitment state to date.
// This value is always monotonically increasing. This method is provided in
@@ -3646,20 +2652,6 @@ func (c *ChannelStateDB) CommitmentHeight(channel *OpenChannel) (
return height, nil
}
-// FindPreviousState scans through the append-only log in an attempt to recover
-// the previous channel state indicated by the update number. This method is
-// intended to be used for obtaining the relevant data needed to claim all
-// funds rightfully spendable in the case of an on-chain broadcast of the
-// commitment transaction.
-func (c *OpenChannel) FindPreviousState(
- updateNum uint64) (*RevocationLog, *ChannelCommitment, error) {
-
- c.RLock()
- defer c.RUnlock()
-
- return c.Db.FindPreviousState(c, updateNum)
-}
-
// FindPreviousState scans through the append-only log in an attempt to recover
// the previous channel state indicated by the update number. This method is
// intended to be used for obtaining the relevant data needed to claim all
@@ -3734,25 +2726,6 @@ const (
// was closed.
type ChannelCloseSummary = cstate.ChannelCloseSummary
-// CloseChannel closes a previously active Lightning channel. Closing a
-// channel entails persisting a record of the close while either purging the
-// nested per-channel state inline (synchronous backends like bbolt and etcd)
-// or skipping the cascading delete on tombstone-enabled backends, where the
-// outpoint-index flip to outpointClosed is the authoritative marker. The
-// compact summary written to closedChannelBucket and the historical record
-// under historicalChannelBucket are populated identically across both paths,
-// so historical reads remain uniform regardless of backend. The optional set
-// of channel statuses is OR'd into the chanStatus written to the historical
-// bucket and is used to record close initiators.
-func (c *OpenChannel) CloseChannel(summary *ChannelCloseSummary,
- statuses ...ChannelStatus) error {
-
- c.Lock()
- defer c.Unlock()
-
- return c.Db.CloseChannel(c, summary, statuses...)
-}
-
// CloseChannel closes the supplied channel via the strategy selected at DB
// construction. On synchronous backends the channel's nested state — the
// revocation log, the per-channel forwarding-package bucket, and the
@@ -3978,125 +2951,6 @@ func (c *ChannelStateDB) closeChannelTombstone(channel *OpenChannel,
// ChannelSnapshot is a frozen snapshot of the current channel state.
type ChannelSnapshot = cstate.ChannelSnapshot
-// Snapshot returns a read-only snapshot of the current channel state. This
-// snapshot includes information concerning the current settled balance within
-// the channel, metadata detailing total flows, and any outstanding HTLCs.
-func (c *OpenChannel) Snapshot() *ChannelSnapshot {
- c.RLock()
- defer c.RUnlock()
-
- localCommit := c.LocalCommitment
- snapshot := &ChannelSnapshot{
- RemoteIdentity: *c.IdentityPub,
- ChannelPoint: c.FundingOutpoint,
- Capacity: c.Capacity,
- TotalMSatSent: c.TotalMSatSent,
- TotalMSatReceived: c.TotalMSatReceived,
- ChainHash: c.ChainHash,
- ChannelCommitment: ChannelCommitment{
- LocalBalance: localCommit.LocalBalance,
- RemoteBalance: localCommit.RemoteBalance,
- CommitHeight: localCommit.CommitHeight,
- CommitFee: localCommit.CommitFee,
- },
- }
-
- localCommit.CustomBlob.WhenSome(func(blob tlv.Blob) {
- blobCopy := make([]byte, len(blob))
- copy(blobCopy, blob)
-
- snapshot.ChannelCommitment.CustomBlob = fn.Some(blobCopy)
- })
-
- // Copy over the current set of HTLCs to ensure the caller can't mutate
- // our internal state.
- snapshot.Htlcs = make([]HTLC, len(localCommit.Htlcs))
- for i, h := range localCommit.Htlcs {
- snapshot.Htlcs[i] = h.Copy()
- }
-
- return snapshot
-}
-
-// Copy returns a deep copy of the channel state.
-func (c *OpenChannel) Copy() *OpenChannel {
- c.RLock()
- defer c.RUnlock()
-
- clone := &OpenChannel{
- ChanType: c.ChanType,
- ChainHash: c.ChainHash,
- FundingOutpoint: c.FundingOutpoint,
- ShortChannelID: c.ShortChannelID,
- IsPending: c.IsPending,
- IsInitiator: c.IsInitiator,
- chanStatus: c.chanStatus,
- FundingBroadcastHeight: c.FundingBroadcastHeight,
- ConfirmationHeight: c.ConfirmationHeight,
- NumConfsRequired: c.NumConfsRequired,
- ChannelFlags: c.ChannelFlags,
- IdentityPub: c.IdentityPub,
- Capacity: c.Capacity,
- TotalMSatSent: c.TotalMSatSent,
- TotalMSatReceived: c.TotalMSatReceived,
- InitialLocalBalance: c.InitialLocalBalance,
- InitialRemoteBalance: c.InitialRemoteBalance,
- LocalChanCfg: c.LocalChanCfg,
- RemoteChanCfg: c.RemoteChanCfg,
- LocalCommitment: c.LocalCommitment.Copy(),
- RemoteCommitment: c.RemoteCommitment.Copy(),
- RemoteCurrentRevocation: c.RemoteCurrentRevocation,
- RemoteNextRevocation: c.RemoteNextRevocation,
- RevocationProducer: c.RevocationProducer,
- RevocationStore: c.RevocationStore,
- ThawHeight: c.ThawHeight,
- LastWasRevoke: c.LastWasRevoke,
- RevocationKeyLocator: c.RevocationKeyLocator,
- confirmedScid: c.confirmedScid,
- TapscriptRoot: c.TapscriptRoot,
- }
-
- if c.FundingTxn != nil {
- clone.FundingTxn = c.FundingTxn.Copy()
- }
-
- if len(c.LocalShutdownScript) > 0 {
- clone.LocalShutdownScript = make(
- lnwire.DeliveryAddress,
- len(c.LocalShutdownScript),
- )
- copy(clone.LocalShutdownScript, c.LocalShutdownScript)
- }
- if len(c.RemoteShutdownScript) > 0 {
- clone.RemoteShutdownScript = make(
- lnwire.DeliveryAddress,
- len(c.RemoteShutdownScript),
- )
- copy(clone.RemoteShutdownScript, c.RemoteShutdownScript)
- }
-
- if len(c.Memo) > 0 {
- clone.Memo = make([]byte, len(c.Memo))
- copy(clone.Memo, c.Memo)
- }
-
- c.CustomBlob.WhenSome(func(blob tlv.Blob) {
- blobCopy := make([]byte, len(blob))
- copy(blobCopy, blob)
- clone.CustomBlob = fn.Some(blobCopy)
- })
-
- return clone
-}
-
-// LatestCommitments returns the two latest commitments for both the local and
-// remote party. These commitments are read from disk to ensure that only the
-// latest fully committed state is returned. The first commitment returned is
-// the local commitment, and the second returned is the remote commitment.
-func (c *OpenChannel) LatestCommitments() (*ChannelCommitment, *ChannelCommitment, error) {
- return c.Db.LatestCommitments(c)
-}
-
// LatestCommitments returns the two latest commitments for both the local and
// remote party. These commitments are read from disk to ensure that only the
// latest fully committed state is returned. The first commitment returned is
@@ -4122,14 +2976,6 @@ func (c *ChannelStateDB) LatestCommitments(channel *OpenChannel) (
return &channel.LocalCommitment, &channel.RemoteCommitment, nil
}
-// RemoteRevocationStore returns the most up to date commitment version of the
-// revocation storage tree for the remote party. This method can be used when
-// acting on a possible contract breach to ensure, that the caller has the most
-// up to date information required to deliver justice.
-func (c *OpenChannel) RemoteRevocationStore() (shachain.Store, error) {
- return c.Db.RemoteRevocationStore(c)
-}
-
// RemoteRevocationStore returns the most up to date commitment version of the
// revocation storage tree for the remote party. This method can be used when
// acting on a possible contract breach to ensure, that the caller has the most
@@ -4155,75 +3001,6 @@ func (c *ChannelStateDB) RemoteRevocationStore(channel *OpenChannel) (
return channel.RevocationStore, nil
}
-// AbsoluteThawHeight determines a frozen channel's absolute thaw height. If the
-// channel is not frozen, then 0 is returned.
-func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
- // Only frozen channels have a thaw height.
- if !c.ChanType.IsFrozen() && !c.ChanType.HasLeaseExpiration() {
- return 0, nil
- }
-
- // If the channel has the frozen bit set and it's thaw height is below
- // the absolute threshold, then it's interpreted as a relative height to
- // the chain's current height.
- if c.ChanType.IsFrozen() && c.ThawHeight < AbsoluteThawHeightThreshold {
- // We'll only known of the channel's short ID once it's
- // confirmed.
- if c.IsPending {
- return 0, errors.New("cannot use relative thaw " +
- "height for unconfirmed channel")
- }
-
- // For non-zero-conf channels, this is the base height to use.
- blockHeightBase := c.ShortChannelID.BlockHeight
-
- // If this is a zero-conf channel, the ShortChannelID will be
- // an alias.
- if c.IsZeroConf() {
- if !c.ZeroConfConfirmed() {
- return 0, errors.New("cannot use relative " +
- "height for unconfirmed zero-conf " +
- "channel")
- }
-
- // Use the confirmed SCID's BlockHeight.
- blockHeightBase = c.confirmedScid.BlockHeight
- }
-
- return blockHeightBase + c.ThawHeight, nil
- }
-
- return c.ThawHeight, nil
-}
-
-// DeriveHeightHint derives the block height for the channel opening.
-func (c *OpenChannel) DeriveHeightHint() uint32 {
- // As a height hint, we'll try to use the opening height, but if the
- // channel isn't yet open, then we'll use the height it was broadcast
- // at. This may be an unconfirmed zero-conf channel.
- heightHint := c.ShortChanID().BlockHeight
- if heightHint == 0 {
- heightHint = c.BroadcastHeight()
- }
-
- // Since no zero-conf state is stored in a channel backup, the below
- // logic will not be triggered for restored, zero-conf channels. Set
- // the height hint for zero-conf channels.
- if c.IsZeroConf() {
- if c.ZeroConfConfirmed() {
- // If the zero-conf channel is confirmed, we'll use the
- // confirmed SCID's block height.
- heightHint = c.ZeroConfRealScid().BlockHeight
- } else {
- // The zero-conf channel is unconfirmed. We'll need to
- // use the FundingBroadcastHeight.
- heightHint = c.BroadcastHeight()
- }
- }
-
- return heightHint
-}
-
func putChannelCloseSummary(tx kvdb.RwTx, chanID []byte,
summary *ChannelCloseSummary, lastChanState *OpenChannel) error {
diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go
index a6a870c..c955ea9 100644
--- a/channeldb/channel_test.go
+++ b/channeldb/channel_test.go
@@ -1538,7 +1538,7 @@ func TestCloseInitiator(t *testing.T) {
if !dbChans[0].HasChanStatus(status) {
t.Fatalf("expected channel to have "+
"status: %v, has status: %v",
- status, dbChans[0].chanStatus)
+ status, dbChans[0].ChanStatus())
}
}
})
@@ -1620,9 +1620,8 @@ func TestHasChanStatus(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- c := &OpenChannel{
- chanStatus: test.status,
- }
+ c := &OpenChannel{}
+ c.SetChannelStatusForStore(test.status)
for status, expHas := range test.expHas {
has := c.HasChanStatus(status)
diff --git a/channeldb/db.go b/channeldb/db.go
index 1f80c8d..a66e8f1 100644
--- a/channeldb/db.go
+++ b/channeldb/db.go
@@ -1677,7 +1677,7 @@ func (c *ChannelStateDB) RepairLinkNodes(network wire.BitcoinNet) error {
// ChannelShell is a shell of a channel that is meant to be used for channel
// recovery purposes.
-type ChannelShell = chanstate.ChannelShell[*OpenChannel]
+type ChannelShell = chanstate.ChannelShell
// RestoreChannelShells is a method that allows the caller to reconstruct the
// state of an OpenChannel from the ChannelShell. We'll attempt to write the
@@ -1694,7 +1694,10 @@ func (c *ChannelStateDB) RestoreChannelShells(channelShells ...*ChannelShell) er
// been restored, this will signal to other sub-systems
// to not attempt to use the channel as if it was a
// regular one.
- channel.chanStatus |= ChanStatusRestored
+ channel.SetChannelStatusForStore(
+ channel.ChannelStatusForStore() |
+ ChanStatusRestored,
+ )
// First, we'll attempt to create a new open channel
// and link node for this channel. If the channel
diff --git a/channeldb/db_test.go b/channeldb/db_test.go
index 9bc3efc..1f87e3b 100644
--- a/channeldb/db_test.go
+++ b/channeldb/db_test.go
@@ -307,33 +307,37 @@ func genRandomChannelShell() (*ChannelShell, error) {
CsvDelay: uint16(rand.Int63()),
}
+ channel := &OpenChannel{
+ ChainHash: rev,
+ FundingOutpoint: chanPoint,
+ ShortChannelID: lnwire.NewShortChanIDFromInt(
+ uint64(rand.Int63()),
+ ),
+ IdentityPub: pub,
+ LocalChanCfg: ChannelConfig{
+ CommitmentParams: commitParams,
+ PaymentBasePoint: keychain.KeyDescriptor{
+ KeyLocator: keychain.KeyLocator{
+ Family: keychain.KeyFamily(
+ rand.Int63(),
+ ),
+ Index: uint32(rand.Int63()),
+ },
+ },
+ },
+ RemoteCurrentRevocation: pub,
+ IsPending: false,
+ RevocationStore: shachain.NewRevocationStore(),
+ RevocationProducer: shaChainProducer,
+ }
+ channel.SetChannelStatusForStore(chanStatus)
+
return &ChannelShell{
NodeAddrs: []net.Addr{&net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 18555,
}},
- Chan: &OpenChannel{
- chanStatus: chanStatus,
- ChainHash: rev,
- FundingOutpoint: chanPoint,
- ShortChannelID: lnwire.NewShortChanIDFromInt(
- uint64(rand.Int63()),
- ),
- IdentityPub: pub,
- LocalChanCfg: ChannelConfig{
- CommitmentParams: commitParams,
- PaymentBasePoint: keychain.KeyDescriptor{
- KeyLocator: keychain.KeyLocator{
- Family: keychain.KeyFamily(rand.Int63()),
- Index: uint32(rand.Int63()),
- },
- },
- },
- RemoteCurrentRevocation: pub,
- IsPending: false,
- RevocationStore: shachain.NewRevocationStore(),
- RevocationProducer: shaChainProducer,
- },
+ Chan: channel,
}, nil
}
@@ -403,7 +407,7 @@ func TestRestoreChannelShells(t *testing.T) {
}
if !nodeChans[0].HasChanStatus(ChanStatusRestored) {
t.Fatalf("node has wrong status flags: %v",
- nodeChans[0].chanStatus)
+ nodeChans[0].ChanStatus())
}
// We should also be able to find the channel if we query for it
diff --git a/chanstate/channel.go b/chanstate/channel.go
index 2389b9a..0950f4c 100644
--- a/chanstate/channel.go
+++ b/chanstate/channel.go
@@ -1,7 +1,5 @@
package chanstate
-import "net"
-
// ChanCount is used by the server in determining access control.
type ChanCount struct {
HasOpenOrClosedChan bool
@@ -18,15 +16,3 @@ type FinalHtlcInfo struct {
// on-chain.
Offchain bool
}
-
-// ChannelShell contains the minimal channel state and peer addresses needed to
-// restore a channel during recovery.
-type ChannelShell[Channel any] struct {
- // NodeAddrs is the set of addresses that this node has known to be
- // reachable at in the past.
- NodeAddrs []net.Addr
-
- // Chan is the minimal channel state required to restore the channel on
- // disk.
- Chan Channel
-}
diff --git a/chanstate/interface.go b/chanstate/interface.go
index c0a3b67..7a1322c 100644
--- a/chanstate/interface.go
+++ b/chanstate/interface.go
@@ -120,7 +120,7 @@ type OpenChannelStore[Channel any] interface {
// finally create an edge within the graph for the channel as well.
// This method is idempotent, so repeated calls with the same set of
// channel shells won't modify the database after the initial call.
- RestoreChannelShells(channelShells ...*ChannelShell[Channel]) error
+ RestoreChannelShells(channelShells ...*ChannelShell) error
}
// HistoricalChannelStore owns the post-close historical channel view.
diff --git a/chanstate/open_channel.go b/chanstate/open_channel.go
new file mode 100644
index 0000000..7cb7c83
--- /dev/null
+++ b/chanstate/open_channel.go
@@ -0,0 +1,1262 @@
+package chanstate
+
+import (
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+
+ "github.com/btcsuite/btcd/btcec/v2"
+ "github.com/btcsuite/btcd/btcutil/v2"
+ "github.com/btcsuite/btcd/chainhash/v2"
+ "github.com/btcsuite/btcd/wire/v2"
+ "github.com/lightningnetwork/lnd/fn/v2"
+ "github.com/lightningnetwork/lnd/htlcswitch/hop"
+ "github.com/lightningnetwork/lnd/input"
+ "github.com/lightningnetwork/lnd/keychain"
+ "github.com/lightningnetwork/lnd/lntypes"
+ "github.com/lightningnetwork/lnd/lnwire"
+ "github.com/lightningnetwork/lnd/shachain"
+ "github.com/lightningnetwork/lnd/tlv"
+)
+
+// ChannelShell contains the minimal channel state and peer addresses needed to
+// restore a channel during recovery.
+type ChannelShell struct {
+ // NodeAddrs is the set of addresses that this node has known to be
+ // reachable at in the past.
+ NodeAddrs []net.Addr
+
+ // Chan is the minimal OpenChannel state required to restore the
+ // channel on disk.
+ Chan *OpenChannel
+}
+
+// OpenChannel encapsulates the persistent and dynamic state of an open channel
+// with a remote node. An open channel supports several options for on-disk
+// serialization depending on the exact context. Full (upon channel creation)
+// state commitments, and partial (due to a commitment update) writes are
+// supported. Each partial write due to a state update appends the new update
+// to an on-disk log, which can then subsequently be queried in order to
+// "time-travel" to a prior state.
+type OpenChannel struct {
+ // ChanType denotes which type of channel this is.
+ ChanType ChannelType
+
+ // ChainHash is a hash which represents the blockchain that this
+ // channel will be opened within. This value is typically the genesis
+ // hash. In the case that the original chain went through a contentious
+ // hard-fork, then this value will be tweaked using the unique fork
+ // point on each branch.
+ ChainHash chainhash.Hash
+
+ // FundingOutpoint is the outpoint of the final funding transaction.
+ // This value uniquely and globally identifies the channel within the
+ // target blockchain as specified by the chain hash parameter.
+ FundingOutpoint wire.OutPoint
+
+ // ShortChannelID encodes the exact location in the chain in which the
+ // channel was initially confirmed. This includes: the block height,
+ // transaction index, and the output within the target transaction.
+ //
+ // If IsZeroConf(), then this will the "base" (very first) ALIAS scid
+ // and the confirmed SCID will be stored in ConfirmedScid.
+ ShortChannelID lnwire.ShortChannelID
+
+ // IsPending indicates whether a channel's funding transaction has been
+ // confirmed.
+ IsPending bool
+
+ // IsInitiator is a bool which indicates if we were the original
+ // initiator for the channel. This value may affect how higher levels
+ // negotiate fees, or close the channel.
+ IsInitiator bool
+
+ // chanStatus is the current status of this channel. If it is not in
+ // the state Default, it should not be used for forwarding payments.
+ chanStatus ChannelStatus
+
+ // FundingBroadcastHeight is the height in which the funding
+ // transaction was broadcast. This value can be used by higher level
+ // sub-systems to determine if a channel is stale and/or should have
+ // been confirmed before a certain height.
+ FundingBroadcastHeight uint32
+
+ // ConfirmationHeight records the block height at which the funding
+ // transaction was first confirmed.
+ ConfirmationHeight uint32
+
+ // CloseConfirmationHeight records the block height at which the closing
+ // transaction was first confirmed. This is used to track remaining
+ // confirmations until the channel is considered fully closed. It is
+ // None if the closing transaction has not yet been confirmed, or if
+ // this data was not available (e.g. channels closed before this
+ // field was introduced).
+ CloseConfirmationHeight fn.Option[uint32]
+
+ // NumConfsRequired is the number of confirmations a channel's funding
+ // transaction must have received in order to be considered available
+ // for normal transactional use.
+ NumConfsRequired uint16
+
+ // ChannelFlags holds the flags that were sent as part of the
+ // open_channel message.
+ ChannelFlags lnwire.FundingFlag
+
+ // IdentityPub is the identity public key of the remote node this
+ // channel has been established with.
+ IdentityPub *btcec.PublicKey
+
+ // Capacity is the total capacity of this channel.
+ Capacity btcutil.Amount
+
+ // TotalMSatSent is the total number of milli-satoshis we've sent
+ // within this channel.
+ TotalMSatSent lnwire.MilliSatoshi
+
+ // TotalMSatReceived is the total number of milli-satoshis we've
+ // received within this channel.
+ TotalMSatReceived lnwire.MilliSatoshi
+
+ // InitialLocalBalance is the balance we have during the channel
+ // opening. When we are not the initiator, this value represents the
+ // push amount.
+ InitialLocalBalance lnwire.MilliSatoshi
+
+ // InitialRemoteBalance is the balance they have during the channel
+ // opening.
+ InitialRemoteBalance lnwire.MilliSatoshi
+
+ // LocalChanCfg is the channel configuration for the local node.
+ LocalChanCfg ChannelConfig
+
+ // RemoteChanCfg is the channel configuration for the remote node.
+ RemoteChanCfg ChannelConfig
+
+ // LocalCommitment is the current local commitment state for the local
+ // party. This is stored distinct from the state of the remote party
+ // as there are certain asymmetric parameters which affect the
+ // structure of each commitment.
+ LocalCommitment ChannelCommitment
+
+ // RemoteCommitment is the current remote commitment state for the
+ // remote party. This is stored distinct from the state of the local
+ // party as there are certain asymmetric parameters which affect the
+ // structure of each commitment.
+ RemoteCommitment ChannelCommitment
+
+ // RemoteCurrentRevocation is the current revocation for their
+ // commitment transaction. However, since this the derived public key,
+ // we don't yet have the private key so we aren't yet able to verify
+ // that it's actually in the hash chain.
+ RemoteCurrentRevocation *btcec.PublicKey
+
+ // RemoteNextRevocation is the revocation key to be used for the *next*
+ // commitment transaction we create for the local node. Within the
+ // specification, this value is referred to as the
+ // per-commitment-point.
+ RemoteNextRevocation *btcec.PublicKey
+
+ // RevocationProducer is used to generate the revocation in such a way
+ // that remote side might store it efficiently and have the ability to
+ // restore the revocation by index if needed. Current implementation of
+ // secret producer is shachain producer.
+ RevocationProducer shachain.Producer
+
+ // RevocationStore is used to efficiently store the revocations for
+ // previous channels states sent to us by remote side. Current
+ // implementation of secret store is shachain store.
+ RevocationStore shachain.Store
+
+ // FundingTxn is the transaction containing this channel's funding
+ // outpoint. Upon restarts, this txn will be rebroadcast if the channel
+ // is found to be pending.
+ //
+ // NOTE: This value will only be populated for single-funder channels
+ // for which we are the initiator, and that we also have the funding
+ // transaction for. One can check this by using the HasFundingTx()
+ // method on the ChanType field.
+ FundingTxn *wire.MsgTx
+
+ // LocalShutdownScript is set to a pre-set script if the channel was
+ // opened by the local node with option_upfront_shutdown_script set. If
+ // the option was not set, the field is empty.
+ LocalShutdownScript lnwire.DeliveryAddress
+
+ // RemoteShutdownScript is set to a pre-set script if the channel was
+ // opened by the remote node with option_upfront_shutdown_script set. If
+ // the option was not set, the field is empty.
+ RemoteShutdownScript lnwire.DeliveryAddress
+
+ // ThawHeight is the height when a frozen channel once again becomes a
+ // normal channel. If this is zero, then there're no restrictions on
+ // this channel. If the value is lower than 500,000, then it's
+ // interpreted as a relative height, or an absolute height otherwise.
+ ThawHeight uint32
+
+ // LastWasRevoke is a boolean that determines if the last update we sent
+ // was a revocation (true) or a commitment signature (false).
+ LastWasRevoke bool
+
+ // RevocationKeyLocator stores the KeyLocator information that we will
+ // need to derive the shachain root for this channel. This allows us to
+ // have private key isolation from lnd.
+ RevocationKeyLocator keychain.KeyLocator
+
+ // confirmedScid is the confirmed ShortChannelID for a zero-conf
+ // channel. If the channel is unconfirmed, then this will be the
+ // default ShortChannelID. This is only set for zero-conf channels.
+ confirmedScid lnwire.ShortChannelID
+
+ // Memo is any arbitrary information we wish to store locally about the
+ // channel that will be useful to our future selves.
+ Memo []byte
+
+ // TapscriptRoot is an optional tapscript root used to derive the MuSig2
+ // funding output.
+ TapscriptRoot fn.Option[chainhash.Hash]
+
+ // CustomBlob is an optional blob that can be used to store information
+ // specific to a custom channel type. This information is only created
+ // at channel funding time, and after wards is to be considered
+ // immutable.
+ CustomBlob fn.Option[tlv.Blob]
+
+ // Db persists channel state through the Store contract. This field
+ // intentionally keeps the existing name while callers still construct
+ // channels through the channeldb compatibility alias. The store
+ // interface keeps receiver methods backend independent while the KV
+ // implementation remains in channeldb.
+ Db Store[*OpenChannel]
+
+ // TODO(roasbeef): just need to store local and remote HTLC's?
+
+ sync.RWMutex
+}
+
+// String returns a string representation of the channel.
+func (c *OpenChannel) String() string {
+ indexStr := "height=%v, local_htlc_index=%v, local_log_index=%v, " +
+ "remote_htlc_index=%v, remote_log_index=%v"
+
+ commit := c.LocalCommitment
+ local := fmt.Sprintf(indexStr, commit.CommitHeight,
+ commit.LocalHtlcIndex, commit.LocalLogIndex,
+ commit.RemoteHtlcIndex, commit.RemoteLogIndex,
+ )
+
+ commit = c.RemoteCommitment
+ remote := fmt.Sprintf(indexStr, commit.CommitHeight,
+ commit.LocalHtlcIndex, commit.LocalLogIndex,
+ commit.RemoteHtlcIndex, commit.RemoteLogIndex,
+ )
+
+ return fmt.Sprintf("SCID=%v, status=%v, initiator=%v, pending=%v, "+
+ "local commitment has %s, remote commitment has %s",
+ c.ShortChannelID, c.chanStatus, c.IsInitiator, c.IsPending,
+ local, remote,
+ )
+}
+
+// Initiator returns the ChannelParty that originally opened this channel.
+func (c *OpenChannel) Initiator() lntypes.ChannelParty {
+ c.RLock()
+ defer c.RUnlock()
+
+ if c.IsInitiator {
+ return lntypes.Local
+ }
+
+ return lntypes.Remote
+}
+
+// ShortChanID returns the current ShortChannelID of this channel.
+func (c *OpenChannel) ShortChanID() lnwire.ShortChannelID {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.ShortChannelID
+}
+
+// ZeroConfRealScid returns the zero-conf channel's confirmed scid. This should
+// only be called if IsZeroConf returns true.
+func (c *OpenChannel) ZeroConfRealScid() lnwire.ShortChannelID {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.confirmedScid
+}
+
+// ZeroConfConfirmed returns whether the zero-conf channel has confirmed. This
+// should only be called if IsZeroConf returns true.
+func (c *OpenChannel) ZeroConfConfirmed() bool {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.confirmedScid != hop.Source
+}
+
+// IsZeroConf returns whether the option_zeroconf channel type was negotiated.
+func (c *OpenChannel) IsZeroConf() bool {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.ChanType.HasZeroConf()
+}
+
+// IsOptionScidAlias returns whether the option_scid_alias channel type was
+// negotiated.
+func (c *OpenChannel) IsOptionScidAlias() bool {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.ChanType.HasScidAliasChan()
+}
+
+// NegotiatedAliasFeature returns whether the option-scid-alias feature bit was
+// negotiated.
+func (c *OpenChannel) NegotiatedAliasFeature() bool {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.ChanType.HasScidAliasFeature()
+}
+
+// ChanStatus returns the current ChannelStatus of this channel.
+func (c *OpenChannel) ChanStatus() ChannelStatus {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.chanStatus
+}
+
+// ChannelStatusForStore returns the in-memory channel status without taking
+// the channel mutex.
+//
+// TODO(chanstate): remove the ForStore accessors once the KV-backed store code
+// has moved out of channeldb or no longer needs direct access to OpenChannel's
+// private persistence fields.
+//
+// NOTE: This is a preliminary migration hook for KV-backed store code that
+// still lives in channeldb during this refactor. Callers are responsible for
+// synchronization. Normal callers should use ChanStatus.
+func (c *OpenChannel) ChannelStatusForStore() ChannelStatus {
+ return c.chanStatus
+}
+
+// SetChannelStatusForStore updates the in-memory channel status without taking
+// the channel mutex.
+//
+// NOTE: This is a preliminary migration hook for KV-backed store code that
+// still lives in channeldb during this refactor. Callers are responsible for
+// synchronization. Normal callers should use ApplyChanStatus or
+// ClearChanStatus when the status change must be persisted.
+func (c *OpenChannel) SetChannelStatusForStore(status ChannelStatus) {
+ c.chanStatus = status
+}
+
+// ApplyChanStatus allows the caller to modify the internal channel state in a
+// thead-safe manner.
+func (c *OpenChannel) ApplyChanStatus(status ChannelStatus) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.ApplyChannelStatus(c, status)
+}
+
+// ClearChanStatus allows the caller to clear a particular channel status from
+// the primary channel status bit field. After this method returns, a call to
+// HasChanStatus(status) should return false.
+func (c *OpenChannel) ClearChanStatus(status ChannelStatus) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.ClearChannelStatus(c, status)
+}
+
+// HasChanStatus returns true if the internal bitfield channel status of the
+// target channel has the specified status bit set.
+func (c *OpenChannel) HasChanStatus(status ChannelStatus) bool {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.hasChanStatus(status)
+}
+
+func (c *OpenChannel) hasChanStatus(status ChannelStatus) bool {
+ // Special case ChanStatusDefualt since it isn't actually flag, but a
+ // particular combination (or lack-there-of) of flags.
+ if status == ChanStatusDefault {
+ return c.chanStatus == ChanStatusDefault
+ }
+
+ return c.chanStatus&status == status
+}
+
+// HasChanStatusForStore returns true if the internal bitfield channel status
+// has the specified status bit set, without taking the channel mutex.
+//
+// NOTE: This is a preliminary migration hook for KV-backed store code that
+// still lives in channeldb during this refactor. Callers are responsible for
+// synchronization. Normal callers should use HasChanStatus.
+func (c *OpenChannel) HasChanStatusForStore(status ChannelStatus) bool {
+ return c.hasChanStatus(status)
+}
+
+// ConfirmedScidForStore returns the in-memory confirmed SCID without taking
+// the channel mutex.
+//
+// NOTE: This is a preliminary migration hook for KV-backed store code that
+// still lives in channeldb during this refactor. Callers are responsible for
+// synchronization. Normal callers should use ZeroConfRealScid.
+func (c *OpenChannel) ConfirmedScidForStore() lnwire.ShortChannelID {
+ return c.confirmedScid
+}
+
+// SetConfirmedScidForStore updates the in-memory confirmed SCID without taking
+// the channel mutex.
+//
+// NOTE: This is a preliminary migration hook for KV-backed store code that
+// still lives in channeldb during this refactor. Callers are responsible for
+// synchronization.
+func (c *OpenChannel) SetConfirmedScidForStore(scid lnwire.ShortChannelID) {
+ c.confirmedScid = scid
+}
+
+// BroadcastHeight returns the height at which the funding tx was broadcast.
+func (c *OpenChannel) BroadcastHeight() uint32 {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.FundingBroadcastHeight
+}
+
+// SetBroadcastHeight sets the FundingBroadcastHeight.
+func (c *OpenChannel) SetBroadcastHeight(height uint32) {
+ c.Lock()
+ defer c.Unlock()
+
+ c.FundingBroadcastHeight = height
+}
+
+// Refresh updates the in-memory channel state using the latest state observed
+// on disk.
+func (c *OpenChannel) Refresh() error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.RefreshChannel(c)
+}
+
+// MarkConfirmationHeight updates the channel's confirmation height once the
+// channel opening transaction receives one confirmation.
+func (c *OpenChannel) MarkConfirmationHeight(height uint32) error {
+ c.Lock()
+ defer c.Unlock()
+
+ if err := c.Db.MarkChannelConfirmationHeight(c, height); err != nil {
+ return err
+ }
+
+ c.ConfirmationHeight = height
+
+ return nil
+}
+
+// ResetCloseConfirmationHeight clears the channel's close confirmation height
+// when the spending transaction is reorged out.
+func (c *OpenChannel) ResetCloseConfirmationHeight() error {
+ return c.MarkCloseConfirmationHeight(fn.None[uint32]())
+}
+
+// MarkCloseConfirmationHeight updates the channel's close confirmation height
+// when the closing transaction is first detected in a block (spend height).
+func (c *OpenChannel) MarkCloseConfirmationHeight(
+ height fn.Option[uint32]) error {
+
+ c.Lock()
+ defer c.Unlock()
+
+ err := c.Db.MarkChannelCloseConfirmationHeight(c, height)
+ if err != nil {
+ return err
+ }
+
+ c.CloseConfirmationHeight = height
+
+ return nil
+}
+
+// MarkAsOpen marks a channel as fully open given a locator that uniquely
+// describes its location within the chain.
+func (c *OpenChannel) MarkAsOpen(openLoc lnwire.ShortChannelID) error {
+ c.Lock()
+ defer c.Unlock()
+
+ if err := c.Db.MarkChannelOpen(c, openLoc); err != nil {
+ return err
+ }
+
+ c.IsPending = false
+ c.ShortChannelID = openLoc
+
+ return nil
+}
+
+// MarkRealScid marks the zero-conf channel's confirmed ShortChannelID. This
+// should only be done if IsZeroConf returns true.
+func (c *OpenChannel) MarkRealScid(realScid lnwire.ShortChannelID) error {
+ c.Lock()
+ defer c.Unlock()
+
+ if err := c.Db.MarkChannelRealScid(c, realScid); err != nil {
+ return err
+ }
+
+ c.confirmedScid = realScid
+
+ return nil
+}
+
+// MarkScidAliasNegotiated adds ScidAliasFeatureBit to ChanType in-memory and
+// in the database.
+func (c *OpenChannel) MarkScidAliasNegotiated() error {
+ c.Lock()
+ defer c.Unlock()
+
+ if err := c.Db.MarkChannelScidAliasNegotiated(c); err != nil {
+ return err
+ }
+
+ c.ChanType |= ScidAliasFeatureBit
+
+ return nil
+}
+
+// MarkDataLoss marks sets the channel status to LocalDataLoss and stores the
+// passed commitPoint for use to retrieve funds in case the remote force closes
+// the channel.
+func (c *OpenChannel) MarkDataLoss(commitPoint *btcec.PublicKey) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.MarkChannelDataLoss(c, commitPoint)
+}
+
+// DataLossCommitPoint retrieves the stored commit point set during
+// MarkDataLoss. If not found ErrNoCommitPoint is returned.
+func (c *OpenChannel) DataLossCommitPoint() (*btcec.PublicKey, error) {
+ return c.Db.FetchChannelDataLossCommitPoint(c)
+}
+
+// MarkBorked marks the event when the channel as reached an irreconcilable
+// state, such as a channel breach or state desynchronization. Borked channels
+// should never be added to the switch.
+func (c *OpenChannel) MarkBorked() error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.MarkChannelBorked(c)
+}
+
+// SecondCommitmentPoint returns the second per-commitment-point for use in the
+// channel_ready message.
+func (c *OpenChannel) SecondCommitmentPoint() (*btcec.PublicKey, error) {
+ c.RLock()
+ defer c.RUnlock()
+
+ // Since we start at commitment height = 0, the second per commitment
+ // point is actually at the 1st index.
+ revocation, err := c.RevocationProducer.AtIndex(1)
+ if err != nil {
+ return nil, err
+ }
+
+ return input.ComputeCommitmentPoint(revocation[:]), nil
+}
+
+// ChanSyncMsg returns the ChannelReestablish message that should be sent upon
+// reconnection with the remote peer that we're maintaining this channel with.
+// The information contained within this message is necessary to re-sync our
+// commitment chains in the case of a last or only partially processed message.
+// When the remote party receives this message one of three things may happen:
+//
+// 1. We're fully synced and no messages need to be sent.
+// 2. We didn't get the last CommitSig message they sent, so they'll re-send
+// it.
+// 3. We didn't get the last RevokeAndAck message they sent, so they'll
+// re-send it.
+//
+// If this is a restored channel, having status ChanStatusRestored, then we'll
+// modify our typical chan sync message to ensure they force close even if
+// we're on the very first state.
+func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
+ c.Lock()
+ defer c.Unlock()
+
+ // The remote commitment height that we'll send in the
+ // ChannelReestablish message is our current commitment height plus
+ // one. If the receiver thinks that our commitment height is actually
+ // *equal* to this value, then they'll re-send the last commitment that
+ // they sent but we never fully processed.
+ localHeight := c.LocalCommitment.CommitHeight
+ nextLocalCommitHeight := localHeight + 1
+
+ // The second value we'll send is the height of the remote commitment
+ // from our PoV. If the receiver thinks that their height is actually
+ // *one plus* this value, then they'll re-send their last revocation.
+ remoteChainTipHeight := c.RemoteCommitment.CommitHeight
+
+ // If this channel has undergone a commitment update, then in order to
+ // prove to the remote party our knowledge of their prior commitment
+ // state, we'll also send over the last commitment secret that the
+ // remote party sent.
+ var lastCommitSecret [32]byte
+ if remoteChainTipHeight != 0 {
+ remoteSecret, err := c.RevocationStore.LookUp(
+ remoteChainTipHeight - 1,
+ )
+ if err != nil {
+ return nil, err
+ }
+ lastCommitSecret = [32]byte(*remoteSecret)
+ }
+
+ // Additionally, we'll send over the current unrevoked commitment on
+ // our local commitment transaction.
+ currentCommitSecret, err := c.RevocationProducer.AtIndex(
+ localHeight,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ // If we've restored this channel, then we'll purposefully give them an
+ // invalid LocalUnrevokedCommitPoint so they'll force close the channel
+ // allowing us to sweep our funds.
+ if c.hasChanStatus(ChanStatusRestored) {
+ currentCommitSecret[0] ^= 1
+
+ // If this is a tweakless channel, then we'll purposefully send
+ // a next local height taht's invalid to trigger a force close
+ // on their end. We do this as tweakless channels don't require
+ // that the commitment point is valid, only that it's present.
+ if c.ChanType.IsTweakless() {
+ nextLocalCommitHeight = 0
+ }
+ }
+
+ // If this is a taproot channel, then we'll need to generate our next
+ // verification nonce to send to the remote party. They'll use this to
+ // sign the next update to our commitment transaction.
+ var (
+ nextTaprootNonce lnwire.OptMusig2NonceTLV
+ nextLocalNonces lnwire.OptLocalNonces
+ )
+ if c.ChanType.IsTaproot() {
+ taprootRevProducer, err := DeriveMusig2Shachain(
+ c.RevocationProducer,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ nextNonce, err := NewMusigVerificationNonce(
+ c.LocalChanCfg.MultiSigKey.PubKey,
+ nextLocalCommitHeight, taprootRevProducer,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("unable to gen next "+
+ "nonce: %w", err)
+ }
+
+ fundingTxid := c.FundingOutpoint.Hash
+ nonce := nextNonce.PubNonce
+
+ // Final taproot channels use the map-based LocalNonces
+ // field keyed by funding TXID. Staging channels use the
+ // legacy single LocalNonce field.
+ if c.ChanType.IsTaprootFinal() {
+ noncesMap := make(map[chainhash.Hash]lnwire.Musig2Nonce)
+ noncesMap[fundingTxid] = nonce
+ nextLocalNonces = lnwire.SomeLocalNonces(
+ lnwire.LocalNoncesData{NoncesMap: noncesMap},
+ )
+ } else {
+ nextTaprootNonce = lnwire.SomeMusig2Nonce(nonce)
+ }
+ }
+
+ return &lnwire.ChannelReestablish{
+ ChanID: lnwire.NewChanIDFromOutPoint(
+ c.FundingOutpoint,
+ ),
+ NextLocalCommitHeight: nextLocalCommitHeight,
+ RemoteCommitTailHeight: remoteChainTipHeight,
+ LastRemoteCommitSecret: lastCommitSecret,
+ LocalUnrevokedCommitPoint: input.ComputeCommitmentPoint(
+ currentCommitSecret[:],
+ ),
+ LocalNonce: nextTaprootNonce,
+ LocalNonces: nextLocalNonces,
+ }, nil
+}
+
+// MarkShutdownSent serialises and persist the given ShutdownInfo for this
+// channel. Persisting this info represents the fact that we have sent the
+// Shutdown message to the remote side and hence that we should re-transmit the
+// same Shutdown message on re-establish.
+func (c *OpenChannel) MarkShutdownSent(info *ShutdownInfo) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.StoreChannelShutdownInfo(c, info)
+}
+
+// ShutdownInfo decodes the shutdown info stored for this channel and returns
+// the result. If no shutdown info has been persisted for this channel then the
+// ErrNoShutdownInfo error is returned.
+func (c *OpenChannel) ShutdownInfo() (fn.Option[ShutdownInfo], error) {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.Db.FetchChannelShutdownInfo(c)
+}
+
+// MarkCommitmentBroadcasted marks the channel as a commitment transaction has
+// been broadcast, either our own or the remote, and we should watch the chain
+// for it to confirm before taking any further action. It takes as argument the
+// closing tx _we believe_ will appear in the chain. This is only used to
+// republish this tx at startup to ensure propagation, and we should still
+// handle the case where a different tx actually hits the chain.
+func (c *OpenChannel) MarkCommitmentBroadcasted(closeTx *wire.MsgTx,
+ closer lntypes.ChannelParty) error {
+
+ return c.Db.MarkChannelCommitmentBroadcasted(c, closeTx, closer)
+}
+
+// MarkCoopBroadcasted marks the channel to indicate that a cooperative close
+// transaction has been broadcast, either our own or the remote, and that we
+// should watch the chain for it to confirm before taking further action. It
+// takes as argument a cooperative close tx that could appear on chain, and
+// should be rebroadcast upon startup. This is only used to republish and
+// ensure propagation, and we should still handle the case where a different tx
+// actually hits the chain.
+func (c *OpenChannel) MarkCoopBroadcasted(closeTx *wire.MsgTx,
+ closer lntypes.ChannelParty) error {
+
+ return c.Db.MarkChannelCoopBroadcasted(c, closeTx, closer)
+}
+
+// BroadcastedCommitment retrieves the stored unilateral closing tx set during
+// MarkCommitmentBroadcasted. If not found ErrNoCloseTx is returned.
+func (c *OpenChannel) BroadcastedCommitment() (*wire.MsgTx, error) {
+ return c.Db.FetchChannelBroadcastedCommitment(c)
+}
+
+// BroadcastedCooperative retrieves the stored cooperative closing tx set during
+// MarkCoopBroadcasted. If not found ErrNoCloseTx is returned.
+func (c *OpenChannel) BroadcastedCooperative() (*wire.MsgTx, error) {
+ return c.Db.FetchChannelBroadcastedCooperative(c)
+}
+
+// SyncPending writes the contents of the channel to the database while it's in
+// the pending (waiting for funding confirmation) state. The IsPending flag
+// will be set to true. When the channel's funding transaction is confirmed,
+// the channel should be marked as "open" and the IsPending flag set to false.
+// Note that this function also creates a LinkNode relationship between this
+// newly created channel and a new LinkNode instance. This allows listing all
+// channels in the database globally, or according to the LinkNode they were
+// created with.
+//
+// TODO(roasbeef): addr param should eventually be an lnwire.NetAddress type
+// that includes service bits.
+func (c *OpenChannel) SyncPending(addr net.Addr, pendingHeight uint32) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.SyncPendingChannel(c, addr, pendingHeight)
+}
+
+// UpdateCommitment updates the local commitment state. It locks in the pending
+// local updates that were received by us from the remote party. The commitment
+// state completely describes the balance state at this point in the commitment
+// chain. In addition to that, it persists all the remote log updates that we
+// have acked, but not signed a remote commitment for yet. These need to be
+// persisted to be able to produce a valid commit signature if a restart would
+// occur. This method its to be called when we revoke our prior commitment
+// state.
+//
+// A map is returned of all the htlc resolutions that were locked in this
+// commitment. Keys correspond to htlc indices and values indicate whether the
+// htlc was settled or failed.
+func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment,
+ unsignedAckedUpdates []LogUpdate) (map[uint64]bool, error) {
+
+ c.Lock()
+ defer c.Unlock()
+
+ // If this is a restored channel, then we want to avoid mutating the
+ // state as all, as it's impossible to do so in a protocol compliant
+ // manner.
+ if c.hasChanStatus(ChanStatusRestored) {
+ return nil, ErrNoRestoredChannelMutation
+ }
+
+ finalHtlcs, err := c.Db.UpdateChannelCommitment(
+ c, newCommitment, unsignedAckedUpdates,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ c.LocalCommitment = *newCommitment
+
+ return finalHtlcs, nil
+}
+
+// ActiveHtlcs returns a slice of HTLC's which are currently active on *both*
+// commitment transactions.
+func (c *OpenChannel) ActiveHtlcs() []HTLC {
+ c.RLock()
+ defer c.RUnlock()
+
+ // We'll only return HTLC's that are locked into *both* commitment
+ // transactions. So we'll iterate through their set of HTLC's to note
+ // which ones are present on their commitment.
+ remoteHtlcs := make(map[[32]byte]struct{})
+ for _, htlc := range c.RemoteCommitment.Htlcs {
+ log.Tracef("RemoteCommitment has htlc: id=%v, update=%v "+
+ "incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
+ htlc.Incoming)
+
+ onionHash := sha256.Sum256(htlc.OnionBlob[:])
+ remoteHtlcs[onionHash] = struct{}{}
+ }
+
+ // Now that we know which HTLC's they have, we'll only mark the HTLC's
+ // as active if *we* know them as well.
+ activeHtlcs := make([]HTLC, 0, len(remoteHtlcs))
+ for _, htlc := range c.LocalCommitment.Htlcs {
+ log.Tracef("LocalCommitment has htlc: id=%v, update=%v "+
+ "incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
+ htlc.Incoming)
+
+ onionHash := sha256.Sum256(htlc.OnionBlob[:])
+ if _, ok := remoteHtlcs[onionHash]; !ok {
+ log.Tracef("Skipped htlc due to onion mismatched: "+
+ "id=%v, update=%v incoming=%v",
+ htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming)
+
+ continue
+ }
+
+ activeHtlcs = append(activeHtlcs, htlc)
+ }
+
+ return activeHtlcs
+}
+
+// AppendRemoteCommitChain appends a new CommitDiff to the end of the
+// commitment chain for the remote party. This method is to be used once we
+// have prepared a new commitment state for the remote party, but before we
+// transmit it to the remote party. The contents of the argument should be
+// sufficient to retransmit the updates and signature needed to reconstruct the
+// state in full, in the case that we need to retransmit.
+func (c *OpenChannel) AppendRemoteCommitChain(diff *CommitDiff) error {
+ c.Lock()
+ defer c.Unlock()
+
+ // If this is a restored channel, then we want to avoid mutating the
+ // state at all, as it's impossible to do so in a protocol compliant
+ // manner.
+ if c.hasChanStatus(ChanStatusRestored) {
+ return ErrNoRestoredChannelMutation
+ }
+
+ return c.Db.AppendRemoteCommitChain(c, diff)
+}
+
+// RemoteCommitChainTip returns the "tip" of the current remote commitment
+// chain. This value will be non-nil iff, we've created a new commitment for
+// the remote party that they haven't yet ACK'd. In this case, their commitment
+// chain will have a length of two: their current unrevoked commitment, and
+// this new pending commitment. Once they revoked their prior state, we'll swap
+// these pointers, causing the tip and the tail to point to the same entry.
+func (c *OpenChannel) RemoteCommitChainTip() (*CommitDiff, error) {
+ return c.Db.RemoteCommitChainTip(c)
+}
+
+// UnsignedAckedUpdates retrieves the persisted unsigned acked remote log
+// updates that still need to be signed for.
+func (c *OpenChannel) UnsignedAckedUpdates() ([]LogUpdate, error) {
+ return c.Db.UnsignedAckedUpdates(c)
+}
+
+// RemoteUnsignedLocalUpdates retrieves the persisted, unsigned local log
+// updates that the remote still needs to sign for.
+func (c *OpenChannel) RemoteUnsignedLocalUpdates() ([]LogUpdate, error) {
+ return c.Db.RemoteUnsignedLocalUpdates(c)
+}
+
+// InsertNextRevocation inserts the _next_ commitment point (revocation) into
+// the database, and also modifies the internal RemoteNextRevocation attribute
+// to point to the passed key. This method is to be using during final channel
+// set up, _after_ the channel has been fully confirmed.
+//
+// NOTE: If this method isn't called, then the target channel won't be able to
+// propose new states for the commitment state of the remote party.
+func (c *OpenChannel) InsertNextRevocation(revKey *btcec.PublicKey) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.InsertNextRevocation(c, revKey)
+}
+
+// AdvanceCommitChainTail records the new state transition within an on-disk
+// append-only log which records all state transitions by the remote peer. In
+// the case of an uncooperative broadcast of a prior state by the remote peer,
+// this log can be consulted in order to reconstruct the state needed to
+// rectify the situation. This method will add the current commitment for the
+// remote party to the revocation log, and promote the current pending
+// commitment to the current remote commitment. The updates parameter is the
+// set of local updates that the peer still needs to send us a signature for.
+// We store this set of updates in case we go down.
+func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg,
+ updates []LogUpdate, ourOutputIndex, theirOutputIndex uint32) error {
+
+ c.Lock()
+ defer c.Unlock()
+
+ // If this is a restored channel, then we want to avoid mutating the
+ // state at all, as it's impossible to do so in a protocol compliant
+ // manner.
+ if c.hasChanStatus(ChanStatusRestored) {
+ return ErrNoRestoredChannelMutation
+ }
+
+ return c.Db.AdvanceCommitChainTail(
+ c, fwdPkg, updates, ourOutputIndex, theirOutputIndex,
+ )
+}
+
+// NextLocalHtlcIndex returns the next unallocated local htlc index. To ensure
+// this always returns the next index that has been not been allocated, this
+// will first try to examine any pending commitments, before falling back to the
+// last locked-in remote commitment.
+func (c *OpenChannel) NextLocalHtlcIndex() (uint64, error) {
+ // First, load the most recent commit diff that we initiated for the
+ // remote party. If no pending commit is found, this is not treated as
+ // a critical error, since we can always fall back.
+ pendingRemoteCommit, err := c.RemoteCommitChainTip()
+ if err != nil && !errors.Is(err, ErrNoPendingCommit) {
+ return 0, err
+ }
+
+ // If a pending commit was found, its local htlc index will be at least
+ // as large as the one on our local commitment.
+ if pendingRemoteCommit != nil {
+ return pendingRemoteCommit.Commitment.LocalHtlcIndex, nil
+ }
+
+ // Otherwise, fallback to using the local htlc index of their
+ // commitment.
+ return c.RemoteCommitment.LocalHtlcIndex, nil
+}
+
+// LoadFwdPkgs scans the forwarding log for any packages that haven't been
+// processed, and returns their deserialized log updates in map indexed by the
+// remote commitment height at which the updates were locked in.
+func (c *OpenChannel) LoadFwdPkgs() ([]*FwdPkg, error) {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.Db.LoadFwdPkgs(c)
+}
+
+// AckAddHtlcs updates the AckAddFilter containing any of the provided AddRefs
+// indicating that a response to this Add has been committed to the remote
+// party. Doing so will prevent these Add HTLCs from being reforwarded
+// internally.
+func (c *OpenChannel) AckAddHtlcs(addRefs ...AddRef) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.AckAddHtlcs(c, addRefs...)
+}
+
+// AckSettleFails updates the SettleFailFilter containing any of the provided
+// SettleFailRefs, indicating that the response has been delivered to the
+// incoming link, corresponding to a particular AddRef. Doing so will prevent
+// the responses from being retransmitted internally.
+func (c *OpenChannel) AckSettleFails(settleFailRefs ...SettleFailRef) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.AckSettleFails(c, settleFailRefs...)
+}
+
+// SetFwdFilter atomically sets the forwarding filter for the forwarding package
+// identified by `height`.
+func (c *OpenChannel) SetFwdFilter(height uint64, fwdFilter *PkgFilter) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.SetFwdFilter(c, height, fwdFilter)
+}
+
+// RemoveFwdPkgs atomically removes forwarding packages specified by the
+// remote commitment heights. If one of the intermediate RemovePkg calls fails,
+// then the later packages won't be removed.
+//
+// NOTE: This method should only be called on packages marked FwdStateCompleted.
+func (c *OpenChannel) RemoveFwdPkgs(heights ...uint64) error {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.RemoveFwdPkgs(c, heights...)
+}
+
+// CommitmentHeight returns the current commitment height. The commitment
+// height represents the number of updates to the commitment state to date.
+// This value is always monotonically increasing. This method is provided in
+// order to allow multiple instances of a particular open channel to obtain a
+// consistent view of the number of channel updates to date.
+func (c *OpenChannel) CommitmentHeight() (uint64, error) {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.Db.CommitmentHeight(c)
+}
+
+// FindPreviousState scans through the append-only log in an attempt to recover
+// the previous channel state indicated by the update number. This method is
+// intended to be used for obtaining the relevant data needed to claim all
+// funds rightfully spendable in the case of an on-chain broadcast of the
+// commitment transaction.
+func (c *OpenChannel) FindPreviousState(
+ updateNum uint64) (*RevocationLog, *ChannelCommitment, error) {
+
+ c.RLock()
+ defer c.RUnlock()
+
+ return c.Db.FindPreviousState(c, updateNum)
+}
+
+// CloseChannel closes a previously active Lightning channel. Closing a
+// channel entails persisting a record of the close while either purging the
+// nested per-channel state inline (synchronous backends like bbolt and etcd)
+// or skipping the cascading delete on tombstone-enabled backends, where the
+// outpoint-index flip to outpointClosed is the authoritative marker. The
+// compact summary written to closedChannelBucket and the historical record
+// under historicalChannelBucket are populated identically across both paths,
+// so historical reads remain uniform regardless of backend. The optional set
+// of channel statuses is OR'd into the chanStatus written to the historical
+// bucket and is used to record close initiators.
+func (c *OpenChannel) CloseChannel(summary *ChannelCloseSummary,
+ statuses ...ChannelStatus) error {
+
+ c.Lock()
+ defer c.Unlock()
+
+ return c.Db.CloseChannel(c, summary, statuses...)
+}
+
+// Snapshot returns a read-only snapshot of the current channel state. This
+// snapshot includes information concerning the current settled balance within
+// the channel, metadata detailing total flows, and any outstanding HTLCs.
+func (c *OpenChannel) Snapshot() *ChannelSnapshot {
+ c.RLock()
+ defer c.RUnlock()
+
+ localCommit := c.LocalCommitment
+ snapshot := &ChannelSnapshot{
+ RemoteIdentity: *c.IdentityPub,
+ ChannelPoint: c.FundingOutpoint,
+ Capacity: c.Capacity,
+ TotalMSatSent: c.TotalMSatSent,
+ TotalMSatReceived: c.TotalMSatReceived,
+ ChainHash: c.ChainHash,
+ ChannelCommitment: ChannelCommitment{
+ LocalBalance: localCommit.LocalBalance,
+ RemoteBalance: localCommit.RemoteBalance,
+ CommitHeight: localCommit.CommitHeight,
+ CommitFee: localCommit.CommitFee,
+ },
+ }
+
+ localCommit.CustomBlob.WhenSome(func(blob tlv.Blob) {
+ blobCopy := make([]byte, len(blob))
+ copy(blobCopy, blob)
+
+ snapshot.ChannelCommitment.CustomBlob = fn.Some(blobCopy)
+ })
+
+ // Copy over the current set of HTLCs to ensure the caller can't mutate
+ // our internal state.
+ snapshot.Htlcs = make([]HTLC, len(localCommit.Htlcs))
+ for i, h := range localCommit.Htlcs {
+ snapshot.Htlcs[i] = h.Copy()
+ }
+
+ return snapshot
+}
+
+// Copy returns a deep copy of the channel state.
+func (c *OpenChannel) Copy() *OpenChannel {
+ c.RLock()
+ defer c.RUnlock()
+
+ clone := &OpenChannel{
+ ChanType: c.ChanType,
+ ChainHash: c.ChainHash,
+ FundingOutpoint: c.FundingOutpoint,
+ ShortChannelID: c.ShortChannelID,
+ IsPending: c.IsPending,
+ IsInitiator: c.IsInitiator,
+ chanStatus: c.chanStatus,
+ FundingBroadcastHeight: c.FundingBroadcastHeight,
+ ConfirmationHeight: c.ConfirmationHeight,
+ NumConfsRequired: c.NumConfsRequired,
+ ChannelFlags: c.ChannelFlags,
+ IdentityPub: c.IdentityPub,
+ Capacity: c.Capacity,
+ TotalMSatSent: c.TotalMSatSent,
+ TotalMSatReceived: c.TotalMSatReceived,
+ InitialLocalBalance: c.InitialLocalBalance,
+ InitialRemoteBalance: c.InitialRemoteBalance,
+ LocalChanCfg: c.LocalChanCfg,
+ RemoteChanCfg: c.RemoteChanCfg,
+ LocalCommitment: c.LocalCommitment.Copy(),
+ RemoteCommitment: c.RemoteCommitment.Copy(),
+ RemoteCurrentRevocation: c.RemoteCurrentRevocation,
+ RemoteNextRevocation: c.RemoteNextRevocation,
+ RevocationProducer: c.RevocationProducer,
+ RevocationStore: c.RevocationStore,
+ ThawHeight: c.ThawHeight,
+ LastWasRevoke: c.LastWasRevoke,
+ RevocationKeyLocator: c.RevocationKeyLocator,
+ confirmedScid: c.confirmedScid,
+ TapscriptRoot: c.TapscriptRoot,
+ }
+
+ if c.FundingTxn != nil {
+ clone.FundingTxn = c.FundingTxn.Copy()
+ }
+
+ if len(c.LocalShutdownScript) > 0 {
+ clone.LocalShutdownScript = make(
+ lnwire.DeliveryAddress,
+ len(c.LocalShutdownScript),
+ )
+ copy(clone.LocalShutdownScript, c.LocalShutdownScript)
+ }
+ if len(c.RemoteShutdownScript) > 0 {
+ clone.RemoteShutdownScript = make(
+ lnwire.DeliveryAddress,
+ len(c.RemoteShutdownScript),
+ )
+ copy(clone.RemoteShutdownScript, c.RemoteShutdownScript)
+ }
+
+ if len(c.Memo) > 0 {
+ clone.Memo = make([]byte, len(c.Memo))
+ copy(clone.Memo, c.Memo)
+ }
+
+ c.CustomBlob.WhenSome(func(blob tlv.Blob) {
+ blobCopy := make([]byte, len(blob))
+ copy(blobCopy, blob)
+ clone.CustomBlob = fn.Some(blobCopy)
+ })
+
+ return clone
+}
+
+// LatestCommitments returns the two latest commitments for both the local and
+// remote party. These commitments are read from disk to ensure that only the
+// latest fully committed state is returned. The first commitment returned is
+// the local commitment, and the second returned is the remote commitment.
+func (c *OpenChannel) LatestCommitments() (*ChannelCommitment,
+ *ChannelCommitment, error) {
+
+ return c.Db.LatestCommitments(c)
+}
+
+// RemoteRevocationStore returns the most up to date commitment version of the
+// revocation storage tree for the remote party. This method can be used when
+// acting on a possible contract breach to ensure, that the caller has the most
+// up to date information required to deliver justice.
+func (c *OpenChannel) RemoteRevocationStore() (shachain.Store, error) {
+ return c.Db.RemoteRevocationStore(c)
+}
+
+// AbsoluteThawHeight determines a frozen channel's absolute thaw height. If the
+// channel is not frozen, then 0 is returned.
+func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
+ // Only frozen channels have a thaw height.
+ if !c.ChanType.IsFrozen() && !c.ChanType.HasLeaseExpiration() {
+ return 0, nil
+ }
+
+ // If the channel has the frozen bit set and it's thaw height is below
+ // the absolute threshold, then it's interpreted as a relative height to
+ // the chain's current height.
+ if c.ChanType.IsFrozen() && c.ThawHeight < AbsoluteThawHeightThreshold {
+ // We'll only known of the channel's short ID once it's
+ // confirmed.
+ if c.IsPending {
+ return 0, errors.New("cannot use relative thaw " +
+ "height for unconfirmed channel")
+ }
+
+ // For non-zero-conf channels, this is the base height to use.
+ blockHeightBase := c.ShortChannelID.BlockHeight
+
+ // If this is a zero-conf channel, the ShortChannelID will be
+ // an alias.
+ if c.IsZeroConf() {
+ if !c.ZeroConfConfirmed() {
+ return 0, errors.New("cannot use relative " +
+ "height for unconfirmed zero-conf " +
+ "channel")
+ }
+
+ // Use the confirmed SCID's BlockHeight.
+ blockHeightBase = c.confirmedScid.BlockHeight
+ }
+
+ return blockHeightBase + c.ThawHeight, nil
+ }
+
+ return c.ThawHeight, nil
+}
+
+// DeriveHeightHint derives the block height for the channel opening.
+func (c *OpenChannel) DeriveHeightHint() uint32 {
+ // As a height hint, we'll try to use the opening height, but if the
+ // channel isn't yet open, then we'll use the height it was broadcast
+ // at. This may be an unconfirmed zero-conf channel.
+ heightHint := c.ShortChanID().BlockHeight
+ if heightHint == 0 {
+ heightHint = c.BroadcastHeight()
+ }
+
+ // Since no zero-conf state is stored in a channel backup, the below
+ // logic will not be triggered for restored, zero-conf channels. Set
+ // the height hint for zero-conf channels.
+ if c.IsZeroConf() {
+ if c.ZeroConfConfirmed() {
+ // If the zero-conf channel is confirmed, we'll use the
+ // confirmed SCID's block height.
+ heightHint = c.ZeroConfRealScid().BlockHeight
+ } else {
+ // The zero-conf channel is unconfirmed. We'll need to
+ // use the FundingBroadcastHeight.
+ heightHint = c.BroadcastHeight()
+ }
+ }
+
+ return heightHint
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.