mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-14 18:30:52 +02:00
channeldb: BigSize migration, store zero-conf, scid-alias bits
This introduces a BigSize migration that is used to expand the width of the ChannelStatus and ChannelType fields. Three channel "types" are added - ZeroConfBit, ScidAliasChanBit, and ScidAliasFeatureBit. ScidAliasChanBit denotes that the scid-alias channel type was negotiated for the channel. ScidAliasFeatureBit denotes that the scid-alias feature bit was negotiated during the *lifetime* of the channel. Several helper functions on the OpenChannel struct are exposed to aid callers from different packages. The RefreshShortChanID has been renamed to Refresh. A new function BroadcastHeight is used to guard access to the mutable FundingBroadcastHeight member. This prevents data races.
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
@@ -53,6 +54,14 @@ var (
|
||||
//
|
||||
outpointBucket = []byte("outpoint-bucket")
|
||||
|
||||
// chanIDBucket stores all of the 32-byte channel ID's we know about.
|
||||
// These could be derived from outpointBucket, but it is more
|
||||
// convenient to have these in their own bucket.
|
||||
//
|
||||
// chanID -> tlv stream.
|
||||
//
|
||||
chanIDBucket = []byte("chan-id-bucket")
|
||||
|
||||
// historicalChannelBucket stores all channels that have seen their
|
||||
// commitment tx confirm. All information from their previous open state
|
||||
// is retained.
|
||||
@@ -190,6 +199,10 @@ const (
|
||||
// A tlv type used to serialize and deserialize the
|
||||
// `InitialRemoteBalance` field.
|
||||
initialRemoteBalanceType tlv.Type = 3
|
||||
|
||||
// A tlv type definition used to serialize and deserialize the
|
||||
// confirmed ShortChannelID for a zero-conf channel.
|
||||
realScidType tlv.Type = 4
|
||||
)
|
||||
|
||||
// indexStatus is an enum-like type that describes what state the
|
||||
@@ -211,7 +224,7 @@ const (
|
||||
// fee negotiation, channel closing, the format of HTLCs, etc. Structure-wise,
|
||||
// a ChannelType is a bit field, with each bit denoting a modification from the
|
||||
// base channel type of single funder.
|
||||
type ChannelType uint8
|
||||
type ChannelType uint64
|
||||
|
||||
const (
|
||||
// NOTE: iota isn't used here for this enum needs to be stable
|
||||
@@ -254,6 +267,17 @@ const (
|
||||
// period of time, constraining every output that pays to the channel
|
||||
// initiator with an additional CLTV of the lease maturity.
|
||||
LeaseExpirationBit ChannelType = 1 << 6
|
||||
|
||||
// ZeroConfBit indicates that the channel is a zero-conf channel.
|
||||
ZeroConfBit ChannelType = 1 << 7
|
||||
|
||||
// ScidAliasChanBit indicates that the channel has negotiated the
|
||||
// scid-alias channel type.
|
||||
ScidAliasChanBit ChannelType = 1 << 8
|
||||
|
||||
// ScidAliasFeatureBit indicates that the scid-alias feature bit was
|
||||
// negotiated during the lifetime of this channel.
|
||||
ScidAliasFeatureBit ChannelType = 1 << 9
|
||||
)
|
||||
|
||||
// IsSingleFunder returns true if the channel type if one of the known single
|
||||
@@ -303,6 +327,22 @@ func (c ChannelType) HasLeaseExpiration() bool {
|
||||
return c&LeaseExpirationBit == LeaseExpirationBit
|
||||
}
|
||||
|
||||
// HasZeroConf returns true if the channel is a zero-conf channel.
|
||||
func (c ChannelType) HasZeroConf() bool {
|
||||
return c&ZeroConfBit == ZeroConfBit
|
||||
}
|
||||
|
||||
// HasScidAliasChan returns true if the scid-alias channel type was negotiated.
|
||||
func (c ChannelType) HasScidAliasChan() bool {
|
||||
return c&ScidAliasChanBit == ScidAliasChanBit
|
||||
}
|
||||
|
||||
// HasScidAliasFeature returns true if the scid-alias feature bit was
|
||||
// negotiated during the lifetime of this channel.
|
||||
func (c ChannelType) HasScidAliasFeature() bool {
|
||||
return c&ScidAliasFeatureBit == ScidAliasFeatureBit
|
||||
}
|
||||
|
||||
// ChannelConstraints represents a set of constraints meant to allow a node to
|
||||
// limit their exposure, enact flow control and ensure that all HTLCs are
|
||||
// economically relevant. This struct will be mirrored for both sides of the
|
||||
@@ -476,7 +516,7 @@ type ChannelCommitment struct {
|
||||
|
||||
// ChannelStatus is a bit vector used to indicate whether an OpenChannel is in
|
||||
// the default usable state, or a state where it shouldn't be used.
|
||||
type ChannelStatus uint8
|
||||
type ChannelStatus uint64
|
||||
|
||||
var (
|
||||
// ChanStatusDefault is the normal state of an open channel.
|
||||
@@ -604,6 +644,9 @@ type OpenChannel struct {
|
||||
// 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
|
||||
@@ -739,6 +782,11 @@ type OpenChannel struct {
|
||||
// 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
|
||||
|
||||
// TODO(roasbeef): eww
|
||||
Db *ChannelStateDB
|
||||
|
||||
@@ -755,6 +803,50 @@ func (c *OpenChannel) ShortChanID() lnwire.ShortChannelID {
|
||||
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()
|
||||
@@ -801,13 +893,25 @@ func (c *OpenChannel) hasChanStatus(status ChannelStatus) bool {
|
||||
return c.chanStatus&status == status
|
||||
}
|
||||
|
||||
// RefreshShortChanID updates the in-memory channel state using the latest
|
||||
// value observed on disk.
|
||||
//
|
||||
// TODO: the name of this function should be changed to reflect the fact that
|
||||
// it is not only refreshing the short channel id but all the channel state.
|
||||
// maybe Refresh/Reload?
|
||||
func (c *OpenChannel) RefreshShortChanID() error {
|
||||
// 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()
|
||||
|
||||
@@ -825,6 +929,19 @@ func (c *OpenChannel) RefreshShortChanID() error {
|
||||
return fmt.Errorf("unable to fetch chan info: %v", err)
|
||||
}
|
||||
|
||||
// Also populate the channel's commitment states for both sides
|
||||
// of the channel.
|
||||
if err := fetchChanCommitments(chanBucket, c); err != nil {
|
||||
return fmt.Errorf("unable to fetch chan commitments: "+
|
||||
"%v", err)
|
||||
}
|
||||
|
||||
// Also retrieve the current revocation state.
|
||||
if err := fetchChanRevocationState(chanBucket, c); err != nil {
|
||||
return fmt.Errorf("unable to fetch chan revocations: "+
|
||||
"%v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
if err != nil {
|
||||
@@ -931,6 +1048,7 @@ func fetchChanBucketRw(tx kvdb.RwTx, nodeKey *btcec.PublicKey,
|
||||
func (c *OpenChannel) fullSync(tx kvdb.RwTx) error {
|
||||
// Fetch the outpoint bucket and check if the outpoint already exists.
|
||||
opBucket := tx.ReadWriteBucket(outpointBucket)
|
||||
cidBucket := tx.ReadWriteBucket(chanIDBucket)
|
||||
|
||||
var chanPointBuf bytes.Buffer
|
||||
if err := writeOutpoint(&chanPointBuf, &c.FundingOutpoint); err != nil {
|
||||
@@ -942,6 +1060,11 @@ func (c *OpenChannel) fullSync(tx kvdb.RwTx) error {
|
||||
return ErrChanAlreadyExists
|
||||
}
|
||||
|
||||
cid := lnwire.NewChanIDFromOutPoint(&c.FundingOutpoint)
|
||||
if cidBucket.Get(cid[:]) != nil {
|
||||
return ErrChanAlreadyExists
|
||||
}
|
||||
|
||||
status := uint8(outpointOpen)
|
||||
|
||||
// Write the status of this outpoint as the first entry in a tlv
|
||||
@@ -962,6 +1085,10 @@ func (c *OpenChannel) fullSync(tx kvdb.RwTx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cidBucket.Put(cid[:], []byte{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First fetch the top level bucket which stores all data related to
|
||||
// current, active channels.
|
||||
openChanBucket, err := tx.CreateTopLevelBucket(openChannelBucket)
|
||||
@@ -1035,6 +1162,71 @@ func (c *OpenChannel) MarkAsOpen(openLoc lnwire.ShortChannelID) error {
|
||||
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 := kvdb.Update(c.Db.backend, func(tx kvdb.RwTx) error {
|
||||
chanBucket, err := fetchChanBucketRw(
|
||||
tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel, err := fetchOpenChannel(
|
||||
chanBucket, &c.FundingOutpoint,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel.confirmedScid = realScid
|
||||
|
||||
return putOpenChannel(chanBucket, channel)
|
||||
}, func() {}); 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 := kvdb.Update(c.Db.backend, func(tx kvdb.RwTx) error {
|
||||
chanBucket, err := fetchChanBucketRw(
|
||||
tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel, err := fetchOpenChannel(
|
||||
chanBucket, &c.FundingOutpoint,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channel.ChanType |= ScidAliasFeatureBit
|
||||
return putOpenChannel(chanBucket, channel)
|
||||
}, func() {}); 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.
|
||||
@@ -1101,6 +1293,22 @@ func (c *OpenChannel) MarkBorked() error {
|
||||
return c.putChanStatus(ChanStatusBorked)
|
||||
}
|
||||
|
||||
// SecondCommitmentPoint returns the second per-commitment-point for use in the
|
||||
// funding_locked 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
|
||||
@@ -3095,7 +3303,24 @@ func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
|
||||
return 0, errors.New("cannot use relative thaw " +
|
||||
"height for unconfirmed channel")
|
||||
}
|
||||
return c.ShortChannelID.BlockHeight + c.ThawHeight, nil
|
||||
|
||||
// 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
|
||||
@@ -3325,6 +3550,7 @@ func putChanInfo(chanBucket kvdb.RwBucket, channel *OpenChannel) error {
|
||||
tlv.MakePrimitiveRecord(
|
||||
initialRemoteBalanceType, &remoteBalance,
|
||||
),
|
||||
MakeScidRecord(realScidType, &channel.confirmedScid),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -3541,6 +3767,7 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
|
||||
tlv.MakePrimitiveRecord(
|
||||
initialRemoteBalanceType, &remoteBalance,
|
||||
),
|
||||
MakeScidRecord(realScidType, &channel.confirmedScid),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -3744,3 +3971,12 @@ func DKeyLocator(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
|
||||
func MakeKeyLocRecord(typ tlv.Type, keyLoc *keychain.KeyLocator) tlv.Record {
|
||||
return tlv.MakeStaticRecord(typ, keyLoc, 8, EKeyLocator, DKeyLocator)
|
||||
}
|
||||
|
||||
// MakeScidRecord creates a Record out of a ShortChannelID using the passed
|
||||
// Type and the EShortChannelID and DShortChannelID functions. The size will
|
||||
// always be 8 for the ShortChannelID.
|
||||
func MakeScidRecord(typ tlv.Type, scid *lnwire.ShortChannelID) tlv.Record {
|
||||
return tlv.MakeStaticRecord(
|
||||
typ, scid, 8, lnwire.EShortChannelID, lnwire.DShortChannelID,
|
||||
)
|
||||
}
|
||||
|
Reference in New Issue
Block a user