func TestManager(t *testing.T) {
	if testing.Short() {
		t.Skip("short: skipping TestManager")
	}
	ctx := context.Background()
	db, mgr, _, _, teardown, err := cloneDB("mgr_watching_only.kv")
	defer teardown()
	if err != nil {
		t.Fatal(err)
	}
	testManagerAPI(&testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       true,
		watchingOnly: false,
	})
	mgr.Close()
	mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase)
	if err != nil {
		t.Fatalf("Open: unexpected error: %v", err)
	}
	defer mgr.Close()
	tc := &testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       false,
		watchingOnly: false,
	}
	testManagerAPI(tc)
}
TestManager performs a full suite of tests against the address manager API in
create mode. It makes use of a test context because the address manager is
persistent and much of the testing involves having specific state.
package udb
import (
	"bytes"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"testing"
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/wallet/walletdb"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/dcrutil/v4"
	"github.com/decred/dcrd/txscript/v4/stdaddr"
)
type testContext struct {
	t            *testing.T
	db           walletdb.DB
	manager      *Manager
	account      uint32
	create       bool
	unlocked     bool
	watchingOnly bool
}
type expectedAddr struct {
	address     string
	addressHash []byte
	internal    bool
	compressed  bool
	imported    bool
	pubKey      []byte
	privKey     []byte
	privKeyWIF  string
	script      []byte
}
func testNamePrefix(tc *testContext) string {
	prefix := "Open "
	if tc.create {
		prefix = "Create "
	}
	return prefix + fmt.Sprintf("account #%d", tc.account)
}
func testManagedPubKeyAddress(tc *testContext, prefix string, gotAddr ManagedPubKeyAddress, wantAddr *expectedAddr) bool {
	gpubBytes := gotAddr.PubKey()
	if !bytes.Equal(gpubBytes, wantAddr.pubKey) {
		tc.t.Errorf("%s PubKey: unexpected public key - got %x, want "+
			"%x", prefix, gpubBytes, wantAddr.pubKey)
		return false
	}
	return true
}
func testManagedScriptAddress(tc *testContext, prefix string, gotAddr ManagedScriptAddress, wantAddr *expectedAddr) bool {
	if !bytes.Equal(gotAddr.AddrHash(),
		dcrutil.Hash160(wantAddr.script)) {
		tc.t.Errorf("%s Script: unexpected script  - got %x, want "+
			"%x", prefix, gotAddr.AddrHash(),
			dcrutil.Hash160(wantAddr.script))
		return false
	}
	return true
}
func testAddress(tc *testContext, prefix string, gotAddr ManagedAddress, wantAddr *expectedAddr) bool {
	if gotAddr.Account() != tc.account {
		tc.t.Errorf("ManagedAddress.Account: unexpected account - got "+
			"%d, want %d", gotAddr.Account(), tc.account)
		return false
	}
	if gotAddr.Address().String() != wantAddr.address {
		tc.t.Errorf("%s EncodeAddress: unexpected address - got %s, "+
			"want %s", prefix, gotAddr.Address().String(),
			wantAddr.address)
		return false
	}
	if !bytes.Equal(gotAddr.AddrHash(), wantAddr.addressHash) {
		tc.t.Errorf("%s AddrHash: unexpected address hash - got %x, "+
			"want %x", prefix, gotAddr.AddrHash(), wantAddr.addressHash)
		return false
	}
	if gotAddr.Internal() != wantAddr.internal {
		tc.t.Errorf("%s Internal: unexpected internal flag - got %v, "+
			"want %v", prefix, gotAddr.Internal(), wantAddr.internal)
		return false
	}
	if gotAddr.Imported() != wantAddr.imported {
		tc.t.Errorf("%s Imported: unexpected imported flag - got %v, "+
			"want %v", prefix, gotAddr.Imported(), wantAddr.imported)
		return false
	}
	switch addr := gotAddr.(type) {
	case ManagedPubKeyAddress:
		if !testManagedPubKeyAddress(tc, prefix, addr, wantAddr) {
			return false
		}
	case ManagedScriptAddress:
		if !testManagedScriptAddress(tc, prefix, addr, wantAddr) {
			return false
		}
	}
	return true
}
func testLocking(tc *testContext, rb walletdb.ReadBucket) {
	if tc.unlocked {
		tc.t.Fatal("testLocking called with an unlocked manager")
	}
	if !tc.manager.IsLocked() {
		tc.t.Fatal("IsLocked: returned false on locked manager")
	}
	err := tc.manager.Lock()
	wantErrCode := errors.Locked
	if tc.watchingOnly {
		wantErrCode = errors.WatchingOnly
	}
	if !errors.Is(err, wantErrCode) {
		tc.t.Fatalf("Lock: unexpected error: %v", err)
	}
	err = tc.manager.Unlock(rb, privPassphrase)
	if tc.watchingOnly {
		if !errors.Is(err, errors.WatchingOnly) {
			tc.t.Fatalf("Unlock: unexpected error: %v", err)
		}
	} else if err != nil {
		tc.t.Fatalf("Unlock: unexpected error: %v", err)
	}
	if !tc.watchingOnly && tc.manager.IsLocked() {
		tc.t.Fatal("IsLocked: returned true on unlocked manager")
	}
	err = tc.manager.Unlock(rb, privPassphrase)
	if tc.watchingOnly {
		if !errors.Is(err, errors.WatchingOnly) {
			tc.t.Fatalf("Unlock: unexpected error: %v", err)
		}
	} else if err != nil {
		tc.t.Fatalf("Unlock: unexpected error: %v", err)
	}
	if !tc.watchingOnly && tc.manager.IsLocked() {
		tc.t.Fatal("IsLocked: returned true on unlocked manager")
	}
	err = tc.manager.Unlock(rb, []byte("invalidpassphrase"))
	wantErrCode = errors.Passphrase
	if tc.watchingOnly {
		wantErrCode = errors.WatchingOnly
	}
	if !errors.Is(err, wantErrCode) {
		tc.t.Fatalf("Unlock: unexpected error: %v", err)
	}
	if !tc.manager.IsLocked() {
		tc.t.Fatal("IsLocked: manager is unlocked after failed unlock attempt")
	}
}
func testImportPrivateKey(tc *testContext, ns walletdb.ReadWriteBucket) {
	if !tc.create {
		return
	}
	if tc.watchingOnly {
		return
	}
	tests := []struct {
		name     string
		in       string
		expected expectedAddr
	}{
		{
			name: "wif for compressed pubkey address",
			in:   "PtWUqkS3apLoZUevFtG3Bwt6uyX8LQfYttycGkt2XCzgxquPATQgG",
			expected: expectedAddr{
				address:     "TsSYVKf24LcrxyWHBqj4oBcU542PcjH1iA2",
				addressHash: hexToBytes("10b601a41d2320527c95eb4cdae2c75b45ae45e1"),
				internal:    false,
				imported:    true,
				compressed:  true,
				pubKey:      hexToBytes("03df8852b90ce8da7de6bcbacd26b78534ad9e46dc1b62a01dcf43f5837d7f9f5e"),
				privKey:     hexToBytes("ac4cb1a53c4f04a71fffbff26d4500c8a95443936deefd1b6ed89727a6858e08"),
			},
		},
	}
	if err := tc.manager.Unlock(ns, privPassphrase); err != nil {
		tc.t.Fatalf("Unlock: unexpected error: %v", err)
	}
	tc.unlocked = true
	tc.account = ImportedAddrAccount
	prefix := testNamePrefix(tc) + " testImportPrivateKey"
	chainParams := tc.manager.ChainParams()
	for i, test := range tests {
		test.expected.privKeyWIF = test.in
		wif, err := dcrutil.DecodeWIF(test.in, chainParams.PrivateKeyID)
		if err != nil {
			tc.t.Errorf("%s DecodeWIF #%d (%s) (%s): unexpected "+
				"error: %v", prefix, i, test.in, test.name, err)
			continue
		}
		addr, err := tc.manager.ImportPrivateKey(ns, wif)
		if err != nil {
			tc.t.Fatalf("%s ImportPrivateKey #%d (%s): "+
				"unexpected error: %v", prefix, i,
				test.name, err)
		}
		if !testAddress(tc, prefix+" ImportPrivateKey", addr, &test.expected) {
			continue
		}
	}
	testResults := func() {
		for i, test := range tests {
			test.expected.privKeyWIF = test.in
			utilAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1(0,
				test.expected.addressHash, chainParams)
			if err != nil {
				tc.t.Fatalf("%s NewAddressPubKeyHash #%d (%s): "+
					"unexpected error: %v", prefix, i, test.name, err)
			}
			taPrefix := fmt.Sprintf("%s Address #%d (%s)", prefix,
				i, test.name)
			ma, err := tc.manager.Address(ns, utilAddr)
			if err != nil {
				tc.t.Fatalf("%s: unexpected error: %v", taPrefix, err)
				continue
			}
			if !testAddress(tc, taPrefix, ma, &test.expected) {
				tc.t.Fatalf("testAddress for %v failed", ma.Address())
			}
		}
	}
	testResults()
	if err := tc.manager.Lock(); err != nil {
		tc.t.Fatalf("Lock: unexpected error: %v", err)
	}
	tc.unlocked = false
	testResults()
}
func testImportScript(tc *testContext, wb walletdb.ReadWriteBucket) {
	if !tc.create {
		return
	}
	if tc.watchingOnly {
		return
	}
	tests := []struct {
		name     string
		in       []byte
		expected expectedAddr
	}{
		{
			name: "p2sh multisig",
			in: hexToBytes("51210373c717acda38b5aa4c00c33932e059cdbc" +
				"11deceb5f00490a9101704cc444c5151ae"),
			expected: expectedAddr{
				address:     "TcsXPUraiDWZoeQBEbw7T7LSgrvD7dar9DA",
				addressHash: hexToBytes("db7e6d507e3e291a5ab2fac10107f4479c1f4f9c"),
				internal:    false,
				imported:    true,
				compressed:  false,
			},
		},
	}
	rb := wb.(walletdb.ReadBucket)
	if err := tc.manager.Unlock(rb, privPassphrase); err != nil {
		tc.t.Fatalf("Unlock: unexpected error: %v", err)
	}
	tc.unlocked = true
	tc.account = ImportedAddrAccount
	prefix := testNamePrefix(tc)
	for i, test := range tests {
		test.expected.script = test.in
		prefix := fmt.Sprintf("%s ImportScript #%d (%s)", prefix,
			i, test.name)
		addr, err := tc.manager.ImportScript(wb, test.in)
		if err != nil {
			tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
		}
		if !testAddress(tc, prefix, addr, &test.expected) {
			tc.t.Fatalf("%s: testAddress failed for %v", prefix,
				addr.Address())
		}
	}
	chainParams := tc.manager.ChainParams()
	testResults := func() {
		for i, test := range tests {
			test.expected.script = test.in
			utilAddr, err := stdaddr.NewAddressScriptHashV0(test.in,
				chainParams)
			if err != nil {
				tc.t.Fatalf("%s NewAddressScriptHash #%d (%s): "+
					"unexpected error: %v", prefix, i, test.name, err)
			}
			taPrefix := fmt.Sprintf("%s Address #%d (%s)", prefix,
				i, test.name)
			ma, err := tc.manager.Address(rb, utilAddr)
			if err != nil {
				tc.t.Fatalf("%s: unexpected error: %v", taPrefix, err)
			}
			if !testAddress(tc, taPrefix, ma, &test.expected) {
				tc.t.Fatalf("%s: testAddress failed for %v", prefix,
					ma.Address())
			}
		}
	}
	testResults()
	if err := tc.manager.Lock(); err != nil {
		tc.t.Fatalf("Lock: unexpected error: %v", err)
	}
	tc.unlocked = false
	testResults()
}
func TestManagerImports(t *testing.T) {
	ctx := context.Background()
	db, mgr, _, _, teardown, err := cloneDB("imports.kv")
	defer teardown()
	if err != nil {
		t.Fatal(err)
	}
	defer mgr.Close()
	tc := &testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       true,
		watchingOnly: false,
	}
	testImports := func(tc *testContext) {
		err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
			ns := tx.ReadWriteBucket(waddrmgrBucketKey)
			testImportPrivateKey(tc, ns)
			testImportScript(tc, ns)
			return nil
		})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
	}
	testImports(tc)
	tc.create = false
	tc.watchingOnly = true
	testImports(tc)
}
func testChangePassphrase(tc *testContext, wb walletdb.ReadWriteBucket) {
	testName := "ChangePassphrase (public) with invalid new secret key"
	var err error
	TstRunWithReplacedNewSecretKey(func() {
		err = tc.manager.ChangePassphrase(wb, pubPassphrase, pubPassphrase2, false)
	})
	if !errors.Is(err, errors.Crypto) {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	rb := wb.(walletdb.ReadBucket)
	testName = "ChangePassphrase (public) with invalid old passphrase"
	err = tc.manager.ChangePassphrase(wb, []byte("bogus"), pubPassphrase2, false)
	if !errors.Is(err, errors.Passphrase) {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	testName = "ChangePassphrase (public)"
	err = tc.manager.ChangePassphrase(wb, pubPassphrase, pubPassphrase2, false)
	if err != nil {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	if !tc.manager.TstCheckPublicPassphrase(pubPassphrase2) {
		tc.t.Fatalf("%s: passphrase does not match", testName)
	}
	err = tc.manager.ChangePassphrase(wb, pubPassphrase2, pubPassphrase, false)
	if err != nil {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	testName = "ChangePassphrase (private) with invalid old passphrase"
	err = tc.manager.ChangePassphrase(wb, []byte("bogus"), privPassphrase2, true)
	wantErrCode := errors.Passphrase
	if tc.watchingOnly {
		wantErrCode = errors.WatchingOnly
	}
	if !errors.Is(err, wantErrCode) {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	if tc.watchingOnly {
		return
	}
	testName = "ChangePassphrase (private)"
	err = tc.manager.ChangePassphrase(wb, privPassphrase, privPassphrase2, true)
	if err != nil {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	if err := tc.manager.Unlock(rb, privPassphrase2); err != nil {
		tc.t.Fatalf("%s: failed to unlock with new private "+
			"passphrase: %v", testName, err)
	}
	tc.unlocked = true
	err = tc.manager.ChangePassphrase(wb, privPassphrase2, privPassphrase, true)
	if err != nil {
		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
	}
	if tc.manager.IsLocked() {
		tc.t.Fatalf("%s: manager is locked", testName)
	}
	if err := tc.manager.Lock(); err != nil {
		tc.t.Fatalf("Lock: unexpected error: %v", err)
	}
	tc.unlocked = false
}
func testNewAccount(tc *testContext, wb walletdb.ReadWriteBucket) {
	if !tc.create {
		return
	}
	if tc.watchingOnly {
		_, err := tc.manager.NewAccount(wb, "test")
		if !errors.Is(err, errors.WatchingOnly) {
			tc.t.Fatalf("NewAccount: expected ErrWatchingOnly, got %v", err)
		}
	}
	_, err := tc.manager.NewAccount(wb, "test")
	if !errors.Is(err, errors.Locked) {
		tc.t.Fatalf("NewAccount: expected ErrLocked, got %v", err)
	}
	rb := wb.(walletdb.ReadBucket)
	if err := tc.manager.Unlock(rb, privPassphrase); err != nil {
		tc.t.Fatalf("Unlock: unexpected error: %v", err)
	}
	tc.unlocked = true
	testName := "acct-create"
	expectedAccount := tc.account + 1
	account, err := tc.manager.NewAccount(wb, testName)
	if err != nil {
		tc.t.Fatalf("NewAccount: unexpected error: %v", err)
	}
	if account != expectedAccount {
		tc.t.Fatalf("NewAccount account mismatch -- got %d, want %d",
			account, expectedAccount)
	}
	_, err = tc.manager.NewAccount(wb, testName)
	if !errors.Is(err, errors.Exist) {
		tc.t.Fatalf("NewAccount: expected ErrExist, got %v", err)
	}
	testName = ""  
	_, err = tc.manager.NewAccount(wb, testName)
	if !errors.Is(err, errors.Invalid) {
		tc.t.Fatalf("NewAccount: expected ErrInvalid, got %v", err)
	}
	testName = "imported"  
	_, err = tc.manager.NewAccount(wb, testName)
	if !errors.Is(err, errors.Invalid) {
		tc.t.Fatalf("NewAccount: expected ErrInvalid, got %v", err)
	}
}
func testLookupAccount(tc *testContext, rb walletdb.ReadBucket) {
	expectedAccounts := map[string]uint32{
		TstDefaultAccountName:   DefaultAccountNum,
		ImportedAddrAccountName: ImportedAddrAccount,
	}
	for acctName, expectedAccount := range expectedAccounts {
		account, err := tc.manager.LookupAccount(rb, acctName)
		if err != nil {
			tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
		}
		if account != expectedAccount {
			tc.t.Fatalf("LookupAccount account mismatch -- got %d, want %d",
				account, expectedAccount)
		}
	}
	testName := "non existent account"
	_, err := tc.manager.LookupAccount(rb, testName)
	if !errors.Is(err, errors.NotExist) {
		tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
	}
	lastAccount, err := tc.manager.LastAccount(rb)
	if err != nil {
		tc.t.Fatalf("LastAccount failed: %v", err)
	}
	expectedLastAccount := uint32(1)
	if lastAccount != expectedLastAccount {
		tc.t.Fatalf("LookupAccount account mismatch -- got %d, want %d",
			lastAccount, expectedLastAccount)
	}
}
func testRenameAccount(tc *testContext, wb walletdb.ReadWriteBucket) {
	rb := wb.(walletdb.ReadBucket)
	acctName, err := tc.manager.AccountName(rb, tc.account)
	if err != nil {
		tc.t.Fatalf("AccountName: unexpected error: %v", err)
	}
	testName := acctName + "-renamed"
	err = tc.manager.RenameAccount(wb, tc.account, testName)
	if err != nil {
		tc.t.Fatalf("RenameAccount: unexpected error: %v", err)
	}
	newName, err := tc.manager.AccountName(rb, tc.account)
	if err != nil {
		tc.t.Fatalf("AccountName: unexpected error: %v", err)
	}
	if newName != testName {
		tc.t.Fatalf("RenameAccount account name mismatch -- got %s, want %s",
			newName, testName)
	}
	err = tc.manager.RenameAccount(wb, tc.account, testName)
	if !errors.Is(err, errors.Exist) {
		tc.t.Fatalf("RenameAccount: unexpected error: %v", err)
	}
	_, err = tc.manager.LookupAccount(wb, acctName)
	if err == nil {
		tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
	}
	if !errors.Is(err, errors.NotExist) {
		tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
	}
}
func testForEachAccount(tc *testContext, rb walletdb.ReadBucket) {
	prefix := testNamePrefix(tc) + " testForEachAccount"
	expectedAccounts := []uint32{0, 1, 2147483647}
	var accounts []uint32
	err := tc.manager.ForEachAccount(rb, func(account uint32) error {
		accounts = append(accounts, account)
		return nil
	})
	if err != nil {
		tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
	}
	if len(accounts) != len(expectedAccounts) {
		tc.t.Fatalf("%s: unexpected number of accounts - got "+
			"%d, want %d", prefix, len(accounts), len(expectedAccounts))
	}
	for i, account := range accounts {
		if expectedAccounts[i] != account {
			tc.t.Fatalf("%s #%d: account mismatch -- got %d, want %d,"+
				" accounts: %v", prefix, i, account, expectedAccounts[i], accounts)
		}
	}
}
func testEncryptDecryptErrors(tc *testContext) {
	ctx := context.Background()
	invalidKeyType := CryptoKeyType(0xff)
	if _, err := tc.manager.Encrypt(invalidKeyType, []byte{}); err == nil {
		tc.t.Fatal("Encrypt accepted an invalid key type!")
	}
	if _, err := tc.manager.Decrypt(invalidKeyType, []byte{}); err == nil {
		tc.t.Fatal("Encrypt accepted an invalid key type!")
	}
	if !tc.manager.IsLocked() {
		tc.t.Fatal("Manager should be locked at this point.")
	}
	var err error
	_, err = tc.manager.Encrypt(CKTPrivate, []byte{})
	if !errors.Is(err, errors.Locked) {
		tc.t.Fatal("encryption with private key should fail when manager" +
			" is locked")
	}
	_, err = tc.manager.Decrypt(CKTPrivate, []byte{})
	if !errors.Is(err, errors.Locked) {
		tc.t.Fatal("decryption with private key should fail when manager" +
			" is locked")
	}
	err = walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
		return tc.manager.Unlock(ns, privPassphrase)
	})
	if err != nil {
		tc.t.Fatalf("Attempted to unlock the manager, but failed: %v", err)
	}
	TstRunWithFailingCryptoKeyPriv(tc.manager, func() {
		_, err = tc.manager.Encrypt(CKTPrivate, []byte{})
	})
	if !errors.Is(err, errors.Crypto) {
		tc.t.Fatal("failed encryption")
	}
	if err := tc.manager.Lock(); err != nil {
		tc.t.Fatal("Attempted to lock the manager, but failed:", err)
	}
}
func testEncryptDecrypt(tc *testContext) {
	ctx := context.Background()
	plainText := []byte("this is a plaintext")
	err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
		return tc.manager.Unlock(ns, privPassphrase)
	})
	if err != nil {
		tc.t.Fatal("Attempted to unlock the manager, but failed: ", err)
	}
	keyTypes := []CryptoKeyType{
		CKTPublic,
		CKTPrivate,
	}
	for _, keyType := range keyTypes {
		cipherText, err := tc.manager.Encrypt(keyType, plainText)
		if err != nil {
			tc.t.Fatalf("Failed to encrypt plaintext: %v", err)
		}
		decryptedCipherText, err := tc.manager.Decrypt(keyType, cipherText)
		if err != nil {
			tc.t.Fatalf("Failed to decrypt plaintext: %v", err)
		}
		if !reflect.DeepEqual(decryptedCipherText, plainText) {
			tc.t.Fatal("Got:", decryptedCipherText, ", want:", plainText)
		}
	}
	if err := tc.manager.Lock(); err != nil {
		tc.t.Fatal("Attempted to lock the manager, but failed:", err)
	}
}
func TestManagerEncryptDecrypt(t *testing.T) {
	db, mgr, _, _, teardown, err := cloneDB("encrypt_decrypt.kv")
	defer teardown()
	if err != nil {
		t.Fatal(err)
	}
	defer mgr.Close()
	tc := &testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       false,
		watchingOnly: false,
	}
	testEncryptDecryptErrors(tc)
	testEncryptDecrypt(tc)
}
func TestChangePassphrase(t *testing.T) {
	ctx := context.Background()
	db, mgr, _, _, teardown, err := cloneDB("change_passphrase.kv")
	defer teardown()
	if err != nil {
		t.Fatal(err)
	}
	defer mgr.Close()
	tc := &testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       false,
		watchingOnly: false,
	}
	err = walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
		testChangePassphrase(tc, ns)
		return nil
	})
	if err != nil {
		tc.t.Errorf("unexpected error: %v", err)
	}
}
func testManagerAPI(tc *testContext) {
	ctx := context.Background()
	err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
		testLocking(tc, ns)
		tc.account = 0
		testNewAccount(tc, ns)
		testLookupAccount(tc, ns)
		testForEachAccount(tc, ns)
		tc.account = 1
		testRenameAccount(tc, ns)
		return nil
	})
	if err != nil {
		tc.t.Errorf("unexpected error: %v", err)
	}
}
func TestManagerWatchingOnly(t *testing.T) {
	ctx := context.Background()
	db, mgr, _, _, teardown, err := cloneDB("mgr_watching_only.kv")
	defer teardown()
	if err != nil {
		t.Fatal(err)
	}
	testManagerAPI(&testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       true,
		watchingOnly: false,
	})
	mgr.Close()
	mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer mgr.Close()
	err = walletdb.Update(ctx, db, func(tx walletdb.ReadWriteTx) error {
		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
		if err = mgr.ConvertToWatchingOnly(ns); err != nil {
			t.Fatalf("failed to convert manager to watching-only: %v", err)
		}
		return nil
	})
	if err != nil {
		t.Fatal(err)
	}
	testManagerAPI(&testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       false,
		watchingOnly: true,
	})
	mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer mgr.Close()
	testManagerAPI(&testContext{
		t:            t,
		db:           db,
		manager:      mgr,
		account:      0,
		create:       false,
		watchingOnly: true,
	})
}