diff --git a/channeldb/migration33/log.go b/channeldb/migration33/log.go new file mode 100644 index 000000000..e9b271f5d --- /dev/null +++ b/channeldb/migration33/log.go @@ -0,0 +1,14 @@ +package migration33 + +import ( + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized as disabled. This means the package will +// not perform any logging by default until a logger is set. +var log = btclog.Disabled + +// UseLogger uses a specified Logger to output package logging info. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/channeldb/migration33/migration.go b/channeldb/migration33/migration.go new file mode 100644 index 000000000..6e2069a37 --- /dev/null +++ b/channeldb/migration33/migration.go @@ -0,0 +1,69 @@ +package migration33 + +import ( + "bytes" + + "github.com/lightningnetwork/lnd/kvdb" +) + +var ( + // resultsKey is the fixed key under which the attempt results are + // stored. + resultsKey = []byte("missioncontrol-results") + + // defaultMCNamespaceKey is the key of the default mission control store + // namespace. + defaultMCNamespaceKey = []byte("default") +) + +// MigrateMCStoreNameSpacedResults reads in all the current mission control +// entries and re-writes them under a new default namespace. +func MigrateMCStoreNameSpacedResults(tx kvdb.RwTx) error { + log.Infof("Migrating Mission Control store to use namespaced results") + + // Get the top level bucket. All the MC results are currently stored + // as KV pairs in this bucket + topLevelBucket := tx.ReadWriteBucket(resultsKey) + + // If the results bucket does not exist then there are no entries in + // the mission control store yet and so there is nothing to migrate. + if topLevelBucket == nil { + return nil + } + + // Create a new default namespace bucket under the top-level bucket. + defaultNSBkt, err := topLevelBucket.CreateBucket(defaultMCNamespaceKey) + if err != nil { + return err + } + + // Iterate through each of the existing result pairs, write them to the + // new namespaced bucket. Also collect the set of keys so that we can + // later delete them from the top level bucket. + var keys [][]byte + err = topLevelBucket.ForEach(func(k, v []byte) error { + // Skip the new default namespace key. + if bytes.Equal(k, defaultMCNamespaceKey) { + return nil + } + + // Collect the key. + keys = append(keys, k) + + // Write the pair under the default namespace. + return defaultNSBkt.Put(k, v) + }) + if err != nil { + return err + } + + // Finally, iterate through the set of keys and delete them from the + // top level bucket. + for _, k := range keys { + if err := topLevelBucket.Delete(k); err != nil { + return err + } + } + + return err +} diff --git a/channeldb/migration33/migration_test.go b/channeldb/migration33/migration_test.go new file mode 100644 index 000000000..851e2467e --- /dev/null +++ b/channeldb/migration33/migration_test.go @@ -0,0 +1,41 @@ +package migration33 + +import ( + "testing" + + "github.com/lightningnetwork/lnd/channeldb/migtest" + "github.com/lightningnetwork/lnd/kvdb" +) + +var ( + // before represents the structure of the MC store before the migration. + before = map[string]interface{}{ + "key1": "result1", + "key2": "result2", + "key3": "result3", + "key4": "result4", + } + + // after represents the expected structure of the store after the + // migration. It should be identical to before except all the kv pairs + // are now under a new default namespace key. + after = map[string]interface{}{ + string(defaultMCNamespaceKey): before, + } +) + +// TestMigrateMCStoreNameSpacedResults tests that the MC store results are +// correctly moved to be under a new default namespace bucket. +func TestMigrateMCStoreNameSpacedResults(t *testing.T) { + before := func(tx kvdb.RwTx) error { + return migtest.RestoreDB(tx, resultsKey, before) + } + + after := func(tx kvdb.RwTx) error { + return migtest.VerifyDB(tx, resultsKey, after) + } + + migtest.ApplyMigration( + t, before, after, MigrateMCStoreNameSpacedResults, false, + ) +}