waddrmgr/migrations: add migration to force rescan from birthday block
In this commit, we add a migration to force a rescan of users' wallets starting from their birthday block to ensure that their balance is reflected correctly as it is on-chain. This was inspired by the recent bug discovered where the wallet would not watch for the confirmation of a relevant transaction.
This commit is contained in:
parent
ae31984630
commit
0424fd22ec
2 changed files with 103 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
||||||
package waddrmgr
|
package waddrmgr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -26,6 +27,10 @@ var versions = []migration.Version{
|
||||||
Number: 6,
|
Number: 6,
|
||||||
Migration: populateBirthdayBlock,
|
Migration: populateBirthdayBlock,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Number: 7,
|
||||||
|
Migration: resetSyncedBlockToBirthday,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLatestVersion returns the version number of the latest database version.
|
// getLatestVersion returns the version number of the latest database version.
|
||||||
|
@ -350,3 +355,20 @@ func populateBirthdayBlock(ns walletdb.ReadWriteBucket) error {
|
||||||
Hash: *birthdayHash,
|
Hash: *birthdayHash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resetSyncedBlockToBirthday is a migration that resets the wallet's currently
|
||||||
|
// synced block to its birthday block. This essentially serves as a migration to
|
||||||
|
// force a rescan of the wallet.
|
||||||
|
func resetSyncedBlockToBirthday(ns walletdb.ReadWriteBucket) error {
|
||||||
|
syncBucket := ns.NestedReadWriteBucket(syncBucketName)
|
||||||
|
if syncBucket == nil {
|
||||||
|
return errors.New("sync bucket does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
birthdayBlock, err := fetchBirthdayBlock(ns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return putSyncedTo(ns, &birthdayBlock)
|
||||||
|
}
|
||||||
|
|
|
@ -215,3 +215,84 @@ func TestMigrationPopulateBirthdayBlockEstimateTooFar(t *testing.T) {
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMigrationResetSyncedBlockToBirthday ensures that the wallet properly sees
|
||||||
|
// its synced to block as the birthday block after resetting it.
|
||||||
|
func TestMigrationResetSyncedBlockToBirthday(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var birthdayBlock BlockStamp
|
||||||
|
beforeMigration := func(ns walletdb.ReadWriteBucket) error {
|
||||||
|
// To test this migration, we'll assume we're synced to a chain
|
||||||
|
// of 100 blocks, with our birthday being the 50th block.
|
||||||
|
block := &BlockStamp{}
|
||||||
|
for i := int32(1); i < 100; i++ {
|
||||||
|
block.Height = i
|
||||||
|
blockHash := bytes.Repeat([]byte(string(i)), 32)
|
||||||
|
copy(block.Hash[:], blockHash)
|
||||||
|
if err := putSyncedTo(ns, block); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const birthdayHeight = 50
|
||||||
|
birthdayHash, err := fetchBlockHash(ns, birthdayHeight)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
birthdayBlock = BlockStamp{
|
||||||
|
Hash: *birthdayHash, Height: birthdayHeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
return putBirthdayBlock(ns, birthdayBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
afterMigration := func(ns walletdb.ReadWriteBucket) error {
|
||||||
|
// After the migration has succeeded, we should see that the
|
||||||
|
// database's synced block now reflects the birthday block.
|
||||||
|
syncedBlock, err := fetchSyncedTo(ns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if syncedBlock.Height != birthdayBlock.Height {
|
||||||
|
return fmt.Errorf("expected synced block height %d, "+
|
||||||
|
"got %d", birthdayBlock.Height,
|
||||||
|
syncedBlock.Height)
|
||||||
|
}
|
||||||
|
if !syncedBlock.Hash.IsEqual(&birthdayBlock.Hash) {
|
||||||
|
return fmt.Errorf("expected synced block height %v, "+
|
||||||
|
"got %v", birthdayBlock.Hash, syncedBlock.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can now apply the migration and expect it not to fail.
|
||||||
|
applyMigration(
|
||||||
|
t, beforeMigration, afterMigration, resetSyncedBlockToBirthday,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMigrationResetSyncedBlockToBirthdayWithNoBirthdayBlock ensures that we
|
||||||
|
// cannot reset our synced to block to our birthday block if one isn't
|
||||||
|
// available.
|
||||||
|
func TestMigrationResetSyncedBlockToBirthdayWithNoBirthdayBlock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// To replicate the scenario where the database is not aware of a
|
||||||
|
// birthday block, we won't set one. This should cause the migration to
|
||||||
|
// fail.
|
||||||
|
beforeMigration := func(walletdb.ReadWriteBucket) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
afterMigration := func(walletdb.ReadWriteBucket) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
applyMigration(
|
||||||
|
t, beforeMigration, afterMigration, resetSyncedBlockToBirthday,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue