mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
149 lines
4.4 KiB
Go
149 lines
4.4 KiB
Go
package migration27
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
mig26 "github.com/lightningnetwork/lnd/channeldb/migration26"
|
|
mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
|
|
"github.com/lightningnetwork/lnd/kvdb"
|
|
)
|
|
|
|
var (
|
|
// historicalChannelBucket stores all channels that have seen their
|
|
// commitment tx confirm. All information from their previous open state
|
|
// is retained.
|
|
historicalChannelBucket = []byte("historical-chan-bucket")
|
|
)
|
|
|
|
// MigrateHistoricalBalances patches the two new fields, `InitialLocalBalance`
|
|
// and `InitialRemoteBalance`, for all the open channels saved in historical
|
|
// channel bucket. Unlike migration 25, it will only read the old channel info
|
|
// first and then patch the new tlv records with empty values. For historical
|
|
// channels, we previously didn't save the initial balances anywhere and since
|
|
// it's corresponding open channel bucket is deleted after closure, we have
|
|
// lost that balance info.
|
|
func MigrateHistoricalBalances(tx kvdb.RwTx) error {
|
|
log.Infof("Migrating historical local and remote balances...")
|
|
|
|
// First fetch the top level bucket which stores all data related to
|
|
// historically stored channels.
|
|
rootBucket := tx.ReadWriteBucket(historicalChannelBucket)
|
|
|
|
// If no bucket is found, we can exit early.
|
|
if rootBucket == nil {
|
|
return nil
|
|
}
|
|
|
|
// Read a list of historical channels.
|
|
channels, err := findHistoricalChannels(rootBucket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Migrate the balances.
|
|
for _, c := range channels {
|
|
if err := migrateBalances(rootBucket, c); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// findHistoricalChannels finds all historical channels.
|
|
func findHistoricalChannels(historicalBucket kvdb.RBucket) ([]*OpenChannel,
|
|
error) {
|
|
|
|
channels := []*OpenChannel{}
|
|
|
|
// readChannel is a helper closure that reads the channel info from the
|
|
// historical sub-bucket.
|
|
readChannel := func(rootBucket kvdb.RBucket, cp []byte) error {
|
|
c := &OpenChannel{}
|
|
|
|
chanPointBuf := bytes.NewBuffer(cp)
|
|
err := mig.ReadOutpoint(chanPointBuf, &c.FundingOutpoint)
|
|
if err != nil {
|
|
return fmt.Errorf("read funding outpoint got: %v", err)
|
|
}
|
|
|
|
// Read the sub-bucket.
|
|
chanBucket := rootBucket.NestedReadBucket(cp)
|
|
if chanBucket == nil {
|
|
log.Errorf("unable to read bucket for chanPoint=%s",
|
|
c.FundingOutpoint)
|
|
return nil
|
|
}
|
|
|
|
// Try to fetch channel info in old format.
|
|
err = fetchChanInfoCompatible(chanBucket, c, true)
|
|
if err != nil {
|
|
return fmt.Errorf("%s: fetch chan info got: %v",
|
|
c.FundingOutpoint, err)
|
|
}
|
|
|
|
channels = append(channels, c)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Iterate the root bucket.
|
|
err := historicalBucket.ForEach(func(cp, _ []byte) error {
|
|
return readChannel(historicalBucket, cp)
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return channels, nil
|
|
}
|
|
|
|
// fetchChanInfoCompatible tries to fetch the channel info for a historical
|
|
// channel. It will first fetch the info assuming `InitialLocalBalance` and
|
|
// `InitialRemoteBalance` are not serialized. Upon receiving an error, it will
|
|
// then fetch it again assuming the two fields are present in db.
|
|
func fetchChanInfoCompatible(chanBucket kvdb.RBucket, c *OpenChannel,
|
|
legacy bool) error {
|
|
|
|
// Try to fetch the channel info assuming the historical channel in in
|
|
// the old format, where the two fields, `InitialLocalBalance` and
|
|
// `InitialRemoteBalance` are not saved to db.
|
|
err := FetchChanInfo(chanBucket, c, legacy)
|
|
if err == nil {
|
|
return err
|
|
}
|
|
|
|
// If we got an error above, the historical channel may already have
|
|
// the new fields saved. This could happen when a channel is closed
|
|
// after applying migration 25. In this case, we'll borrow the
|
|
// `FetchChanInfo` info method from migration 26 where we assume the
|
|
// two fields are saved.
|
|
return mig26.FetchChanInfo(chanBucket, &c.OpenChannel, legacy)
|
|
}
|
|
|
|
// migrateBalances serializes the channel info using the new tlv format where
|
|
// the two fields, `InitialLocalBalance` and `InitialRemoteBalance` are patched
|
|
// with empty values.
|
|
func migrateBalances(rootBucket kvdb.RwBucket, c *OpenChannel) error {
|
|
var chanPointBuf bytes.Buffer
|
|
err := mig.WriteOutpoint(&chanPointBuf, &c.FundingOutpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get the channel bucket.
|
|
chanBucket := rootBucket.NestedReadWriteBucket(chanPointBuf.Bytes())
|
|
if chanBucket == nil {
|
|
return fmt.Errorf("empty historical chan bucket")
|
|
}
|
|
|
|
// Update the channel info.
|
|
if err := PutChanInfo(chanBucket, c, false); err != nil {
|
|
return fmt.Errorf("unable to put chan info: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|