From ee34b9c7b01c06332b6351f73acf7c637a96b2fa Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 2 Aug 2018 16:45:03 -0700 Subject: [PATCH 1/3] channeldb/error: adds ErrDBReversion error Adds an error for signaling that we detected the case where a user is trying to revert to prior version before a db migration. --- channeldb/error.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/channeldb/error.go b/channeldb/error.go index 6ee24a87e..5c8e20982 100644 --- a/channeldb/error.go +++ b/channeldb/error.go @@ -7,6 +7,10 @@ var ( // created. ErrNoChanDBExists = fmt.Errorf("channel db has not yet been created") + // ErrDBReversion is returned when detecting an attempt to revert to a + // prior database version. + ErrDBReversion = fmt.Errorf("channel db cannot revert to prior version") + // ErrLinkNodesNotFound is returned when node info bucket hasn't been // created. ErrLinkNodesNotFound = fmt.Errorf("no link nodes exist") From e23fc40d638446557af8a9091cb0ed5354867817 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 2 Aug 2018 16:46:17 -0700 Subject: [PATCH 2/3] channeldb/db: check for db reversions on startup --- channeldb/db.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/channeldb/db.go b/channeldb/db.go index f3b6ede1d..3f3ced0b9 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -663,12 +663,24 @@ func (d *DB) syncVersions(versions []version) error { } } - // If the current database version matches the latest version number, - // then we don't need to perform any migrations. latestVersion := getLatestDBVersion(versions) log.Infof("Checking for schema update: latest_version=%v, "+ "db_version=%v", latestVersion, meta.DbVersionNumber) - if meta.DbVersionNumber == latestVersion { + + switch { + + // If the database reports a higher version that we are aware of, the + // user is probably trying to revert to a prior version of lnd. We fail + // here to prevent reversions and unintended corruption. + case meta.DbVersionNumber > latestVersion: + log.Errorf("Refusing to revert from db_version=%d to "+ + "lower version=%d", meta.DbVersionNumber, + latestVersion) + return ErrDBReversion + + // If the current database version matches the latest version number, + // then we don't need to perform any migrations. + case meta.DbVersionNumber == latestVersion: return nil } From dff7706d04161062338ccb143608101ccc84e277 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 2 Aug 2018 18:17:25 -0700 Subject: [PATCH 3/3] channeldb/meta_test: adds TestMigrationReversion Adds a test asserting that we will fail to open a channeldb with a version higher than the highest known version to the current build of lnd. --- channeldb/meta_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/channeldb/meta_test.go b/channeldb/meta_test.go index 5890d6692..dbed0a2be 100644 --- a/channeldb/meta_test.go +++ b/channeldb/meta_test.go @@ -2,6 +2,7 @@ package channeldb import ( "bytes" + "io/ioutil" "testing" "github.com/coreos/bbolt" @@ -380,3 +381,43 @@ func TestMigrationWithoutErrors(t *testing.T) { migrationWithoutErrors, false) } + +// TestMigrationReversion tests after performing a migration to a higher +// database version, opening the database with a lower latest db version returns +// ErrDBReversion. +func TestMigrationReversion(t *testing.T) { + t.Parallel() + + tempDirName, err := ioutil.TempDir("", "channeldb") + if err != nil { + t.Fatalf("unable to create temp dir: %v", err) + } + + cdb, err := Open(tempDirName) + if err != nil { + t.Fatalf("unable to open channeldb: %v", err) + } + + // Update the database metadata to point to one more than the highest + // known version. + err = cdb.Update(func(tx *bolt.Tx) error { + newMeta := &Meta{ + DbVersionNumber: getLatestDBVersion(dbVersions) + 1, + } + + return putMeta(newMeta, tx) + }) + + // Close the database. Even if we succeeded, our next step is to reopen. + cdb.Close() + + if err != nil { + t.Fatalf("unable to increase db version: %v", err) + } + + _, err = Open(tempDirName) + if err != ErrDBReversion { + t.Fatalf("unexpected error when opening channeldb, "+ + "want: %v, got: %v", ErrDBReversion, err) + } +}