channedb: add db migration for databases before delivery script bug fix
This commit is contained in:
3 files changed
+118
-5
No files matched your search
+19
-4
@@ -39,6 +39,10 @@ var (
|
|||||||
number: 0,
|
number: 0,
|
||||||
migration: nil,
|
migration: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
number: 1,
|
||||||
|
migration: deliveryScriptBugMigration,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big endian is the preferred byte order, due to cursor scans over
|
// Big endian is the preferred byte order, due to cursor scans over
|
||||||
@@ -351,21 +355,30 @@ func (d *DB) syncVersions(versions []version) error {
|
|||||||
// If the current database version matches the latest version number,
|
// If the current database version matches the latest version number,
|
||||||
// then we don't need to perform any migrations.
|
// then we don't need to perform any migrations.
|
||||||
latestVersion := getLatestDBVersion(versions)
|
latestVersion := getLatestDBVersion(versions)
|
||||||
|
log.Infof("Checking for schema update: latest_version=%v, "+
|
||||||
|
"db_version=%v", latestVersion, meta.DbVersionNumber)
|
||||||
if meta.DbVersionNumber == latestVersion {
|
if meta.DbVersionNumber == latestVersion {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Performing database schema migration")
|
||||||
|
|
||||||
// Otherwise, we fetch the migrations which need to applied, and
|
// Otherwise, we fetch the migrations which need to applied, and
|
||||||
// execute them serially within a single database transaction to ensure
|
// execute them serially within a single database transaction to ensure
|
||||||
// the migration is atomic.
|
// the migration is atomic.
|
||||||
migrations := getMigrationsToApply(versions, meta.DbVersionNumber)
|
migrations, migrationVersions := getMigrationsToApply(versions,
|
||||||
|
meta.DbVersionNumber)
|
||||||
return d.Update(func(tx *bolt.Tx) error {
|
return d.Update(func(tx *bolt.Tx) error {
|
||||||
for _, migration := range migrations {
|
for i, migration := range migrations {
|
||||||
if migration == nil {
|
if migration == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Applying migration #%v", migrationVersions[i])
|
||||||
|
|
||||||
if err := migration(tx); err != nil {
|
if err := migration(tx); err != nil {
|
||||||
|
log.Infof("Unable to apply migration #%v",
|
||||||
|
migrationVersions[i])
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,14 +403,16 @@ func getLatestDBVersion(versions []version) uint32 {
|
|||||||
|
|
||||||
// getMigrationsToApply retrieves the migration function that should be
|
// getMigrationsToApply retrieves the migration function that should be
|
||||||
// applied to the database.
|
// applied to the database.
|
||||||
func getMigrationsToApply(versions []version, version uint32) []migration {
|
func getMigrationsToApply(versions []version, version uint32) ([]migration, []uint32) {
|
||||||
migrations := make([]migration, 0, len(versions))
|
migrations := make([]migration, 0, len(versions))
|
||||||
|
migrationVersions := make([]uint32, 0, len(versions))
|
||||||
|
|
||||||
for _, v := range versions {
|
for _, v := range versions {
|
||||||
if v.number > version {
|
if v.number > version {
|
||||||
migrations = append(migrations, v.migration)
|
migrations = append(migrations, v.migration)
|
||||||
|
migrationVersions = append(migrationVersions, v.number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return migrations
|
return migrations, migrationVersions
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ func TestOrderOfMigrations(t *testing.T) {
|
|||||||
|
|
||||||
// Retrieve the migration that should be applied to db, as far as
|
// Retrieve the migration that should be applied to db, as far as
|
||||||
// current version is 1, we skip zero and first versions.
|
// current version is 1, we skip zero and first versions.
|
||||||
migrations := getMigrationsToApply(versions, 1)
|
migrations, _ := getMigrationsToApply(versions, 1)
|
||||||
|
|
||||||
if len(migrations) != 2 {
|
if len(migrations) != 2 {
|
||||||
t.Fatal("incorrect number of migrations to apply")
|
t.Fatal("incorrect number of migrations to apply")
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package channeldb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// deliveryScriptBugMigration is a database migration that patches an incorrect
|
||||||
|
// version of the database due to a typo in the key when fetching, putting,
|
||||||
|
// deleting the delivery scripts for a channel. As of database version 1, the
|
||||||
|
// querying logic expects delivery scripts to be in the proper place within the
|
||||||
|
// node's channel schema. This migration fixes the issue in the older version
|
||||||
|
// of the database so channels can properly be read from the database.
|
||||||
|
func deliveryScriptBugMigration(tx *bolt.Tx) error {
|
||||||
|
// Get the bucket dedicated to storing the metadata for open channels.
|
||||||
|
openChanBucket := tx.Bucket(openChannelBucket)
|
||||||
|
if openChanBucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, fetch the bucket dedicated to storing metadata related to all
|
||||||
|
// nodes. All keys within this bucket are the serialized public keys of
|
||||||
|
// all our direct counterparties.
|
||||||
|
nodeMetaBucket := tx.Bucket(nodeInfoBucket)
|
||||||
|
if nodeMetaBucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Migration database from legacy channel delivery script " +
|
||||||
|
"schema")
|
||||||
|
|
||||||
|
// Finally for each node public key in the bucket, fetch all the
|
||||||
|
// channels related to this particular node.
|
||||||
|
return nodeMetaBucket.ForEach(func(k, v []byte) error {
|
||||||
|
// Within the meta node meta bucket, each key is the node's
|
||||||
|
// serialized public key. Knowing this key allows us to fetch
|
||||||
|
// the node's open channel bucket.
|
||||||
|
nodeChanBucket := openChanBucket.Bucket(k)
|
||||||
|
if nodeChanBucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we have the open channel bucket for this particular
|
||||||
|
// node, we then access another sub-bucket which is an index
|
||||||
|
// that stores the channel points (chanID's) for all active
|
||||||
|
// channels with the node.
|
||||||
|
nodeChanIDBucket := nodeChanBucket.Bucket(chanIDBucket[:])
|
||||||
|
if nodeChanIDBucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := nodeChanIDBucket.ForEach(func(k, v []byte) error {
|
||||||
|
if k == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The old delivery script key within a node's channel
|
||||||
|
// bucket for each channel was just the prefix key
|
||||||
|
// itself. So we'll check if this key stores any data,
|
||||||
|
// if not, then we don't need to migrate this channel.
|
||||||
|
oldDeliverykey := deliveryScriptsKey
|
||||||
|
deliveryScripts := nodeChanBucket.Get(oldDeliverykey)
|
||||||
|
if deliveryScripts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the stored outpoint so we can log our
|
||||||
|
// progress.
|
||||||
|
outBytes := bytes.NewReader(k)
|
||||||
|
chanID := &wire.OutPoint{}
|
||||||
|
if err := readOutpoint(outBytes, chanID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Migration delivery scripts of "+
|
||||||
|
"ChannelPoint(%v)", chanID)
|
||||||
|
|
||||||
|
// Next we manually construct the _proper_ key which
|
||||||
|
// uses the key prefix in conjunction with the chanID
|
||||||
|
// to create the final key.
|
||||||
|
deliveryKey := make([]byte, len(deliveryScriptsKey)+len(k))
|
||||||
|
copy(deliveryKey[:3], deliveryScriptsKey)
|
||||||
|
copy(deliveryKey[3:], k)
|
||||||
|
|
||||||
|
// To complete the migration for this channel, we now
|
||||||
|
// store the delivery scripts in their proper place.
|
||||||
|
return nodeChanBucket.Put(deliveryKey, deliveryScripts)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before we conclude, we'll also delete value of the incorrect
|
||||||
|
// delivery storage.
|
||||||
|
return nodeChanBucket.Delete(deliveryScriptsKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in new issue
Block a user