mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-06 10:22:08 +01:00
105 lines
3.3 KiB
Go
105 lines
3.3 KiB
Go
|
package migration3
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
|
||
|
"github.com/lightningnetwork/lnd/kvdb"
|
||
|
"github.com/lightningnetwork/lnd/tlv"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// cChanDetailsBkt is a top-level bucket storing:
|
||
|
// channel-id => cChannelSummary -> encoded ClientChanSummary.
|
||
|
// => cChanDBID -> db-assigned-id
|
||
|
cChanDetailsBkt = []byte("client-channel-detail-bucket")
|
||
|
|
||
|
// cChanDBID is a key used in the cChanDetailsBkt to store the
|
||
|
// db-assigned-id of a channel.
|
||
|
cChanDBID = []byte("client-channel-db-id")
|
||
|
|
||
|
// cChanIDIndexBkt is a top-level bucket storing:
|
||
|
// db-assigned-id -> channel-ID
|
||
|
cChanIDIndexBkt = []byte("client-channel-id-index")
|
||
|
|
||
|
// cChannelSummary is a key used in cChanDetailsBkt to store the encoded
|
||
|
// body of ClientChanSummary.
|
||
|
cChannelSummary = []byte("client-channel-summary")
|
||
|
|
||
|
// ErrUninitializedDB signals that top-level buckets for the database
|
||
|
// have not been initialized.
|
||
|
ErrUninitializedDB = errors.New("db not initialized")
|
||
|
|
||
|
// ErrCorruptChanDetails signals that the clients channel detail's
|
||
|
// on-disk structure deviates from what is expected.
|
||
|
ErrCorruptChanDetails = errors.New("channel details corrupted")
|
||
|
|
||
|
byteOrder = binary.BigEndian
|
||
|
)
|
||
|
|
||
|
// MigrateChannelIDIndex adds a new channel ID index to the tower client db.
|
||
|
// This index is a mapping from db-assigned ID (a uint64 encoded using BigSize
|
||
|
// encoding) to real channel ID (32 bytes). This mapping will allow us to
|
||
|
// persist channel pointers with fewer bytes in the future.
|
||
|
func MigrateChannelIDIndex(tx kvdb.RwTx) error {
|
||
|
log.Infof("Migrating the tower client db to add a new channel ID " +
|
||
|
"index which stores a mapping from db-assigned ID to real " +
|
||
|
"channel ID")
|
||
|
|
||
|
// Create a new top-level bucket for the new index.
|
||
|
indexBkt, err := tx.CreateTopLevelBucket(cChanIDIndexBkt)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Get the top-level channel-details bucket. The keys of this bucket
|
||
|
// are the real channel IDs.
|
||
|
chanDetailsBkt := tx.ReadWriteBucket(cChanDetailsBkt)
|
||
|
if chanDetailsBkt == nil {
|
||
|
return ErrUninitializedDB
|
||
|
}
|
||
|
|
||
|
// Iterate over the keys of the channel-details bucket.
|
||
|
return chanDetailsBkt.ForEach(func(chanID, _ []byte) error {
|
||
|
// Ask the db for a new, unique, ID for the index bucket.
|
||
|
nextSeq, err := indexBkt.NextSequence()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Encode the sequence number using BigSize encoding.
|
||
|
var newIndex bytes.Buffer
|
||
|
err = tlv.WriteVarInt(&newIndex, nextSeq, &[8]byte{})
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Add the mapping from the db-assigned ID to the channel ID
|
||
|
// to the new index.
|
||
|
newIndexBytes := newIndex.Bytes()
|
||
|
err = indexBkt.Put(newIndexBytes, chanID)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
chanDetails := chanDetailsBkt.NestedReadWriteBucket(chanID)
|
||
|
if chanDetails == nil {
|
||
|
return ErrCorruptChanDetails
|
||
|
}
|
||
|
|
||
|
// Here we ensure that the channel-details bucket includes a
|
||
|
// channel summary. The only reason we do this is so that we can
|
||
|
// simulate a migration fail in a test to ensure that a
|
||
|
// migration fail results in an untouched db.
|
||
|
chanSummaryBytes := chanDetails.Get(cChannelSummary)
|
||
|
if chanSummaryBytes == nil {
|
||
|
return ErrCorruptChanDetails
|
||
|
}
|
||
|
|
||
|
// In the channel-details sub-bucket for this channel, add the
|
||
|
// new DB-assigned ID for this channel under the cChanDBID key.
|
||
|
return chanDetails.Put(cChanDBID, newIndexBytes)
|
||
|
})
|
||
|
}
|