mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
7cf28773bf
Previously, in `migration25.OpenChannel`, there was a private field `chanStatus` used to keep track of the channel status. The following migrations, `migration26` and `migration27` also have their own `OpenChannel` defined, with `migration26` inherited from `migration25`, and `migration27` inherited from `migration26`. The private field `chanStatus`, however, is NOT inherited and each of the migrations uses its own. This is fine for reading and writing as, under the hood, the `chanStatus` is just a `uint8` value. Because each migration has its own fetcher and putter, it can safely access its private field to read and write it correctly. The issue pops up when we use the method `migration25.FundingTxPresent()`. Because it's evaluating its channel status using its own private field `chanStatus`, this field would always be the default value(`ChanStatusDefault`), leading the statement `!c.hasChanStatus(ChanStatusRestored)` to always be true. Thus a restored channel will be mistakenly considered to have funding tx present, causing failures in reading the channel info in the following migrations. We fix this by exporting the `ChanStatus` field so its value can be set by following migrations.
230 lines
6.9 KiB
Go
230 lines
6.9 KiB
Go
package migration27
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
|
|
mig25 "github.com/lightningnetwork/lnd/channeldb/migration25"
|
|
mig26 "github.com/lightningnetwork/lnd/channeldb/migration26"
|
|
mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
|
|
"github.com/lightningnetwork/lnd/kvdb"
|
|
"github.com/lightningnetwork/lnd/tlv"
|
|
)
|
|
|
|
const (
|
|
// A tlv type definition used to serialize and deserialize a KeyLocator
|
|
// from the database.
|
|
keyLocType tlv.Type = 1
|
|
|
|
// A tlv type used to serialize and deserialize the
|
|
// `InitialLocalBalance` field.
|
|
initialLocalBalanceType tlv.Type = 2
|
|
|
|
// A tlv type used to serialize and deserialize the
|
|
// `InitialRemoteBalance` field.
|
|
initialRemoteBalanceType tlv.Type = 3
|
|
)
|
|
|
|
var (
|
|
// chanInfoKey can be accessed within the bucket for a channel
|
|
// (identified by its chanPoint). This key stores all the static
|
|
// information for a channel which is decided at the end of the
|
|
// funding flow.
|
|
chanInfoKey = []byte("chan-info-key")
|
|
|
|
// localUpfrontShutdownKey can be accessed within the bucket for a
|
|
// channel (identified by its chanPoint). This key stores an optional
|
|
// upfront shutdown script for the local peer.
|
|
localUpfrontShutdownKey = []byte("local-upfront-shutdown-key")
|
|
|
|
// remoteUpfrontShutdownKey can be accessed within the bucket for a
|
|
// channel (identified by its chanPoint). This key stores an optional
|
|
// upfront shutdown script for the remote peer.
|
|
remoteUpfrontShutdownKey = []byte("remote-upfront-shutdown-key")
|
|
|
|
// lastWasRevokeKey is a key that stores true when the last update we
|
|
// sent was a revocation and false when it was a commitment signature.
|
|
// This is nil in the case of new channels with no updates exchanged.
|
|
lastWasRevokeKey = []byte("last-was-revoke")
|
|
|
|
// ErrNoChanInfoFound is returned when a particular channel does not
|
|
// have any channels state.
|
|
ErrNoChanInfoFound = fmt.Errorf("no chan info found")
|
|
)
|
|
|
|
// OpenChannel embeds a mig26.OpenChannel with the extra update-to-date
|
|
// serialization and deserialization methods.
|
|
//
|
|
// NOTE: doesn't have the Packager field as it's not used in current migration.
|
|
type OpenChannel struct {
|
|
mig26.OpenChannel
|
|
}
|
|
|
|
// FetchChanInfo deserializes the channel info based on the legacy boolean.
|
|
func FetchChanInfo(chanBucket kvdb.RBucket, c *OpenChannel, legacy bool) error {
|
|
infoBytes := chanBucket.Get(chanInfoKey)
|
|
if infoBytes == nil {
|
|
return ErrNoChanInfoFound
|
|
}
|
|
r := bytes.NewReader(infoBytes)
|
|
|
|
var (
|
|
chanType mig.ChannelType
|
|
chanStatus mig.ChannelStatus
|
|
)
|
|
|
|
if err := mig.ReadElements(r,
|
|
&chanType, &c.ChainHash, &c.FundingOutpoint,
|
|
&c.ShortChannelID, &c.IsPending, &c.IsInitiator,
|
|
&chanStatus, &c.FundingBroadcastHeight,
|
|
&c.NumConfsRequired, &c.ChannelFlags,
|
|
&c.IdentityPub, &c.Capacity, &c.TotalMSatSent,
|
|
&c.TotalMSatReceived,
|
|
); err != nil {
|
|
return fmt.Errorf("ReadElements got: %v", err)
|
|
}
|
|
|
|
c.ChanType = mig25.ChannelType(chanType)
|
|
c.ChanStatus = mig25.ChannelStatus(chanStatus)
|
|
|
|
// For single funder channels that we initiated and have the funding
|
|
// transaction to, read the funding txn.
|
|
if c.FundingTxPresent() {
|
|
if err := mig.ReadElement(r, &c.FundingTxn); err != nil {
|
|
return fmt.Errorf("read FundingTxn got: %v", err)
|
|
}
|
|
}
|
|
|
|
if err := mig.ReadChanConfig(r, &c.LocalChanCfg); err != nil {
|
|
return fmt.Errorf("read LocalChanCfg got: %v", err)
|
|
}
|
|
if err := mig.ReadChanConfig(r, &c.RemoteChanCfg); err != nil {
|
|
return fmt.Errorf("read RemoteChanCfg got: %v", err)
|
|
}
|
|
|
|
// Retrieve the boolean stored under lastWasRevokeKey.
|
|
lastWasRevokeBytes := chanBucket.Get(lastWasRevokeKey)
|
|
if lastWasRevokeBytes == nil {
|
|
// If nothing has been stored under this key, we store false in
|
|
// the OpenChannel struct.
|
|
c.LastWasRevoke = false
|
|
} else {
|
|
// Otherwise, read the value into the LastWasRevoke field.
|
|
revokeReader := bytes.NewReader(lastWasRevokeBytes)
|
|
err := mig.ReadElements(revokeReader, &c.LastWasRevoke)
|
|
if err != nil {
|
|
return fmt.Errorf("read LastWasRevoke got: %v", err)
|
|
}
|
|
}
|
|
|
|
// Make the tlv stream based on the legacy param.
|
|
var (
|
|
ts *tlv.Stream
|
|
err error
|
|
localBalance uint64
|
|
remoteBalance uint64
|
|
)
|
|
|
|
keyLocRecord := mig25.MakeKeyLocRecord(
|
|
keyLocType, &c.RevocationKeyLocator,
|
|
)
|
|
|
|
// If it's legacy, create the stream with a single tlv record.
|
|
if legacy {
|
|
ts, err = tlv.NewStream(keyLocRecord)
|
|
} else {
|
|
// Otherwise, for the new format, we will encode the balance
|
|
// fields in the tlv stream too.
|
|
ts, err = tlv.NewStream(
|
|
keyLocRecord,
|
|
tlv.MakePrimitiveRecord(
|
|
initialLocalBalanceType, &localBalance,
|
|
),
|
|
tlv.MakePrimitiveRecord(
|
|
initialRemoteBalanceType, &remoteBalance,
|
|
),
|
|
)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("create tlv stream got: %v", err)
|
|
}
|
|
|
|
if err := ts.Decode(r); err != nil {
|
|
return fmt.Errorf("decode tlv stream got: %v", err)
|
|
}
|
|
|
|
// For the new format, attach the balance fields.
|
|
if !legacy {
|
|
c.InitialLocalBalance = lnwire.MilliSatoshi(localBalance)
|
|
c.InitialRemoteBalance = lnwire.MilliSatoshi(remoteBalance)
|
|
}
|
|
|
|
// Finally, read the optional shutdown scripts.
|
|
if err := mig25.GetOptionalUpfrontShutdownScript(
|
|
chanBucket, localUpfrontShutdownKey, &c.LocalShutdownScript,
|
|
); err != nil {
|
|
return fmt.Errorf("local shutdown script got: %v", err)
|
|
}
|
|
|
|
return mig25.GetOptionalUpfrontShutdownScript(
|
|
chanBucket, remoteUpfrontShutdownKey, &c.RemoteShutdownScript,
|
|
)
|
|
}
|
|
|
|
// PutChanInfo serializes the channel info based on the legacy boolean.
|
|
func PutChanInfo(chanBucket kvdb.RwBucket, c *OpenChannel, legacy bool) error {
|
|
var w bytes.Buffer
|
|
if err := mig.WriteElements(&w,
|
|
mig.ChannelType(c.ChanType), c.ChainHash, c.FundingOutpoint,
|
|
c.ShortChannelID, c.IsPending, c.IsInitiator,
|
|
mig.ChannelStatus(c.ChanStatus), c.FundingBroadcastHeight,
|
|
c.NumConfsRequired, c.ChannelFlags,
|
|
c.IdentityPub, c.Capacity, c.TotalMSatSent,
|
|
c.TotalMSatReceived,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
// For single funder channels that we initiated, and we have the
|
|
// funding transaction, then write the funding txn.
|
|
if c.FundingTxPresent() {
|
|
if err := mig.WriteElement(&w, c.FundingTxn); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := mig.WriteChanConfig(&w, &c.LocalChanCfg); err != nil {
|
|
return err
|
|
}
|
|
if err := mig.WriteChanConfig(&w, &c.RemoteChanCfg); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Make the tlv stream based on the legacy param.
|
|
tlvStream, err := mig26.MakeTlvStream(&c.OpenChannel, legacy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tlvStream.Encode(&w); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Finally, add optional shutdown scripts for the local and remote peer
|
|
// if they are present.
|
|
if err := mig25.PutOptionalUpfrontShutdownScript(
|
|
chanBucket, localUpfrontShutdownKey, c.LocalShutdownScript,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
return mig25.PutOptionalUpfrontShutdownScript(
|
|
chanBucket, remoteUpfrontShutdownKey, c.RemoteShutdownScript,
|
|
)
|
|
}
|