decred.org/dcrwallet/v3@v3.1.0/wallet/udb/addressmanager_test.go (about)

     1  // Copyright (c) 2014 The btcsuite developers
     2  // Copyright (c) 2015-2019 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package udb
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"fmt"
    12  	"os"
    13  	"path/filepath"
    14  	"reflect"
    15  	"testing"
    16  
    17  	"decred.org/dcrwallet/v3/errors"
    18  	"decred.org/dcrwallet/v3/wallet/walletdb"
    19  	"github.com/decred/dcrd/chaincfg/v3"
    20  	"github.com/decred/dcrd/dcrutil/v4"
    21  	"github.com/decred/dcrd/hdkeychain/v3"
    22  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    23  )
    24  
    25  // testContext is used to store context information about a running test which
    26  // is passed into helper functions.
    27  type testContext struct {
    28  	t            *testing.T
    29  	db           walletdb.DB
    30  	manager      *Manager
    31  	account      uint32
    32  	create       bool
    33  	unlocked     bool
    34  	watchingOnly bool
    35  }
    36  
    37  // expectedAddr is used to house the expected return values from a managed
    38  // address.  Not all fields for used for all managed address types.
    39  type expectedAddr struct {
    40  	address     string
    41  	addressHash []byte
    42  	internal    bool
    43  	compressed  bool
    44  	imported    bool
    45  	pubKey      []byte
    46  	privKey     []byte
    47  	privKeyWIF  string
    48  	script      []byte
    49  }
    50  
    51  // testNamePrefix is a helper to return a prefix to show for test errors based
    52  // on the state of the test context.
    53  func testNamePrefix(tc *testContext) string {
    54  	prefix := "Open "
    55  	if tc.create {
    56  		prefix = "Create "
    57  	}
    58  
    59  	return prefix + fmt.Sprintf("account #%d", tc.account)
    60  }
    61  
    62  // testManagedPubKeyAddress ensures the data returned by exported functions
    63  // provided by the passed managed public key address matches the corresponding
    64  // fields in the provided expected address.
    65  func testManagedPubKeyAddress(tc *testContext, prefix string, gotAddr ManagedPubKeyAddress, wantAddr *expectedAddr) bool {
    66  	// Ensure pubkey is the expected value for the managed address.
    67  	gpubBytes := gotAddr.PubKey()
    68  
    69  	if !bytes.Equal(gpubBytes, wantAddr.pubKey) {
    70  		tc.t.Errorf("%s PubKey: unexpected public key - got %x, want "+
    71  			"%x", prefix, gpubBytes, wantAddr.pubKey)
    72  		return false
    73  	}
    74  
    75  	return true
    76  }
    77  
    78  // testManagedScriptAddress ensures the data returned by exported functions
    79  // provided by the passed managed script address matches the corresponding
    80  // fields in the provided expected address.
    81  func testManagedScriptAddress(tc *testContext, prefix string, gotAddr ManagedScriptAddress, wantAddr *expectedAddr) bool {
    82  	if !bytes.Equal(gotAddr.AddrHash(),
    83  		dcrutil.Hash160(wantAddr.script)) {
    84  		tc.t.Errorf("%s Script: unexpected script  - got %x, want "+
    85  			"%x", prefix, gotAddr.AddrHash(),
    86  			dcrutil.Hash160(wantAddr.script))
    87  		return false
    88  	}
    89  
    90  	return true
    91  }
    92  
    93  // testAddress ensures the data returned by all exported functions provided by
    94  // the passed managed address matches the corresponding fields in the provided
    95  // expected address.  It also type asserts the managed address to determine its
    96  // specific type and calls the corresponding testing functions accordingly.
    97  func testAddress(tc *testContext, prefix string, gotAddr ManagedAddress, wantAddr *expectedAddr) bool {
    98  	if gotAddr.Account() != tc.account {
    99  		tc.t.Errorf("ManagedAddress.Account: unexpected account - got "+
   100  			"%d, want %d", gotAddr.Account(), tc.account)
   101  		return false
   102  	}
   103  
   104  	if gotAddr.Address().String() != wantAddr.address {
   105  		tc.t.Errorf("%s EncodeAddress: unexpected address - got %s, "+
   106  			"want %s", prefix, gotAddr.Address().String(),
   107  			wantAddr.address)
   108  		return false
   109  	}
   110  
   111  	if !bytes.Equal(gotAddr.AddrHash(), wantAddr.addressHash) {
   112  		tc.t.Errorf("%s AddrHash: unexpected address hash - got %x, "+
   113  			"want %x", prefix, gotAddr.AddrHash(), wantAddr.addressHash)
   114  		return false
   115  	}
   116  
   117  	if gotAddr.Internal() != wantAddr.internal {
   118  		tc.t.Errorf("%s Internal: unexpected internal flag - got %v, "+
   119  			"want %v", prefix, gotAddr.Internal(), wantAddr.internal)
   120  		return false
   121  	}
   122  
   123  	if gotAddr.Imported() != wantAddr.imported {
   124  		tc.t.Errorf("%s Imported: unexpected imported flag - got %v, "+
   125  			"want %v", prefix, gotAddr.Imported(), wantAddr.imported)
   126  		return false
   127  	}
   128  
   129  	switch addr := gotAddr.(type) {
   130  	case ManagedPubKeyAddress:
   131  		if !testManagedPubKeyAddress(tc, prefix, addr, wantAddr) {
   132  			return false
   133  		}
   134  
   135  	case ManagedScriptAddress:
   136  		if !testManagedScriptAddress(tc, prefix, addr, wantAddr) {
   137  			return false
   138  		}
   139  	}
   140  
   141  	return true
   142  }
   143  
   144  // testLocking tests the basic locking semantics of the address manager work
   145  // as expected.  Other tests ensure addresses behave as expected under locked
   146  // and unlocked conditions.
   147  func testLocking(tc *testContext, rb walletdb.ReadBucket) {
   148  	if tc.unlocked {
   149  		tc.t.Fatal("testLocking called with an unlocked manager")
   150  	}
   151  	if !tc.manager.IsLocked() {
   152  		tc.t.Fatal("IsLocked: returned false on locked manager")
   153  	}
   154  
   155  	// Locking an already lock manager should return an error.  The error
   156  	// should be ErrLocked or ErrWatchingOnly depending on the type of the
   157  	// address manager.
   158  	err := tc.manager.Lock()
   159  	wantErrCode := errors.Locked
   160  	if tc.watchingOnly {
   161  		wantErrCode = errors.WatchingOnly
   162  	}
   163  	if !errors.Is(err, wantErrCode) {
   164  		tc.t.Fatalf("Lock: unexpected error: %v", err)
   165  	}
   166  
   167  	// Ensure unlocking with the correct passphrase doesn't return any
   168  	// unexpected errors and the manager properly reports it is unlocked.
   169  	// Since watching-only address managers can't be unlocked, also ensure
   170  	// the correct error for that case.
   171  	err = tc.manager.Unlock(rb, privPassphrase)
   172  	if tc.watchingOnly {
   173  		if !errors.Is(err, errors.WatchingOnly) {
   174  			tc.t.Fatalf("Unlock: unexpected error: %v", err)
   175  		}
   176  	} else if err != nil {
   177  		tc.t.Fatalf("Unlock: unexpected error: %v", err)
   178  	}
   179  	if !tc.watchingOnly && tc.manager.IsLocked() {
   180  		tc.t.Fatal("IsLocked: returned true on unlocked manager")
   181  	}
   182  
   183  	// Unlocking the manager again is allowed.  Since watching-only address
   184  	// managers can't be unlocked, also ensure the correct error for that
   185  	// case.
   186  	err = tc.manager.Unlock(rb, privPassphrase)
   187  	if tc.watchingOnly {
   188  		if !errors.Is(err, errors.WatchingOnly) {
   189  			tc.t.Fatalf("Unlock: unexpected error: %v", err)
   190  		}
   191  	} else if err != nil {
   192  		tc.t.Fatalf("Unlock: unexpected error: %v", err)
   193  	}
   194  	if !tc.watchingOnly && tc.manager.IsLocked() {
   195  		tc.t.Fatal("IsLocked: returned true on unlocked manager")
   196  	}
   197  
   198  	// Unlocking the manager with an invalid passphrase must result in an
   199  	// error and a locked manager.
   200  	err = tc.manager.Unlock(rb, []byte("invalidpassphrase"))
   201  	wantErrCode = errors.Passphrase
   202  	if tc.watchingOnly {
   203  		wantErrCode = errors.WatchingOnly
   204  	}
   205  	if !errors.Is(err, wantErrCode) {
   206  		tc.t.Fatalf("Unlock: unexpected error: %v", err)
   207  	}
   208  	if !tc.manager.IsLocked() {
   209  		tc.t.Fatal("IsLocked: manager is unlocked after failed unlock attempt")
   210  	}
   211  }
   212  
   213  // testImportPrivateKey tests that importing private keys works properly.  It
   214  // ensures they can be retrieved by Address after they have been imported and
   215  // the addresses give the expected values when the manager is locked and
   216  // unlocked.
   217  //
   218  // This function expects the manager is already locked when called and returns
   219  // with the manager locked.
   220  func testImportPrivateKey(tc *testContext, ns walletdb.ReadWriteBucket) {
   221  	if !tc.create {
   222  		return
   223  	}
   224  
   225  	// The manager cannot be unlocked to import a private key in watching-only
   226  	// mode.
   227  	if tc.watchingOnly {
   228  		return
   229  	}
   230  
   231  	tests := []struct {
   232  		name     string
   233  		in       string
   234  		expected expectedAddr
   235  	}{
   236  		{
   237  			name: "wif for compressed pubkey address",
   238  			in:   "PtWUqkS3apLoZUevFtG3Bwt6uyX8LQfYttycGkt2XCzgxquPATQgG",
   239  			expected: expectedAddr{
   240  				address:     "TsSYVKf24LcrxyWHBqj4oBcU542PcjH1iA2",
   241  				addressHash: hexToBytes("10b601a41d2320527c95eb4cdae2c75b45ae45e1"),
   242  				internal:    false,
   243  				imported:    true,
   244  				compressed:  true,
   245  				pubKey:      hexToBytes("03df8852b90ce8da7de6bcbacd26b78534ad9e46dc1b62a01dcf43f5837d7f9f5e"),
   246  				privKey:     hexToBytes("ac4cb1a53c4f04a71fffbff26d4500c8a95443936deefd1b6ed89727a6858e08"),
   247  				// privKeyWIF is set to the in field during tests
   248  			},
   249  		},
   250  	}
   251  
   252  	if err := tc.manager.Unlock(ns, privPassphrase); err != nil {
   253  		tc.t.Fatalf("Unlock: unexpected error: %v", err)
   254  	}
   255  	tc.unlocked = true
   256  
   257  	// Only import the private keys when in the create phase of testing.
   258  	tc.account = ImportedAddrAccount
   259  	prefix := testNamePrefix(tc) + " testImportPrivateKey"
   260  	chainParams := tc.manager.ChainParams()
   261  	for i, test := range tests {
   262  		test.expected.privKeyWIF = test.in
   263  		wif, err := dcrutil.DecodeWIF(test.in, chainParams.PrivateKeyID)
   264  		if err != nil {
   265  			tc.t.Errorf("%s DecodeWIF #%d (%s) (%s): unexpected "+
   266  				"error: %v", prefix, i, test.in, test.name, err)
   267  			continue
   268  		}
   269  		addr, err := tc.manager.ImportPrivateKey(ns, wif)
   270  		if err != nil {
   271  			tc.t.Fatalf("%s ImportPrivateKey #%d (%s): "+
   272  				"unexpected error: %v", prefix, i,
   273  				test.name, err)
   274  		}
   275  		if !testAddress(tc, prefix+" ImportPrivateKey", addr, &test.expected) {
   276  			continue
   277  		}
   278  	}
   279  
   280  	// Setup a closure to test the results since the same tests need to be
   281  	// repeated with the manager unlocked and locked.
   282  	testResults := func() {
   283  		for i, test := range tests {
   284  			test.expected.privKeyWIF = test.in
   285  
   286  			// Use the Address API to retrieve each of the expected
   287  			// new addresses and ensure they're accurate.
   288  			utilAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1(0,
   289  				test.expected.addressHash, chainParams)
   290  			if err != nil {
   291  				tc.t.Fatalf("%s NewAddressPubKeyHash #%d (%s): "+
   292  					"unexpected error: %v", prefix, i, test.name, err)
   293  			}
   294  			taPrefix := fmt.Sprintf("%s Address #%d (%s)", prefix,
   295  				i, test.name)
   296  			ma, err := tc.manager.Address(ns, utilAddr)
   297  			if err != nil {
   298  				tc.t.Fatalf("%s: unexpected error: %v", taPrefix, err)
   299  				continue
   300  			}
   301  			if !testAddress(tc, taPrefix, ma, &test.expected) {
   302  				tc.t.Fatalf("testAddress for %v failed", ma.Address())
   303  			}
   304  		}
   305  	}
   306  
   307  	// The address manager could either be locked or unlocked here depending
   308  	// on whether or not it's a watching-only manager.  When it's unlocked,
   309  	// this will test both the public and private address data are accurate.
   310  	// When it's locked, it must be watching-only, so only the public
   311  	// address  nformation is tested and the private functions are checked
   312  	// to ensure they return the expected ErrWatchingOnly error.
   313  	testResults()
   314  
   315  	// Lock the manager and retest all of the addresses to ensure the
   316  	// private information returns the expected error.
   317  	if err := tc.manager.Lock(); err != nil {
   318  		tc.t.Fatalf("Lock: unexpected error: %v", err)
   319  	}
   320  	tc.unlocked = false
   321  
   322  	testResults()
   323  }
   324  
   325  // testImportScript tests that importing scripts works properly.  It ensures
   326  // they can be retrieved by Address after they have been imported and the
   327  // addresses give the expected values when the manager is locked and unlocked.
   328  //
   329  // This function expects the manager is already locked when called and returns
   330  // with the manager locked.
   331  func testImportScript(tc *testContext, wb walletdb.ReadWriteBucket) {
   332  	if !tc.create {
   333  		return
   334  	}
   335  
   336  	// The manager cannot be unlocked to import a script key in watching-only
   337  	// mode.
   338  	if tc.watchingOnly {
   339  		return
   340  	}
   341  
   342  	tests := []struct {
   343  		name     string
   344  		in       []byte
   345  		expected expectedAddr
   346  	}{
   347  		{
   348  			name: "p2sh multisig",
   349  			in: hexToBytes("51210373c717acda38b5aa4c00c33932e059cdbc" +
   350  				"11deceb5f00490a9101704cc444c5151ae"),
   351  			expected: expectedAddr{
   352  				address:     "TcsXPUraiDWZoeQBEbw7T7LSgrvD7dar9DA",
   353  				addressHash: hexToBytes("db7e6d507e3e291a5ab2fac10107f4479c1f4f9c"),
   354  				internal:    false,
   355  				imported:    true,
   356  				compressed:  false,
   357  				// script is set to the in field during tests.
   358  			},
   359  		},
   360  	}
   361  
   362  	rb := wb.(walletdb.ReadBucket)
   363  
   364  	if err := tc.manager.Unlock(rb, privPassphrase); err != nil {
   365  		tc.t.Fatalf("Unlock: unexpected error: %v", err)
   366  	}
   367  	tc.unlocked = true
   368  
   369  	// Only import the scripts when in the create phase of testing.
   370  	tc.account = ImportedAddrAccount
   371  	prefix := testNamePrefix(tc)
   372  	for i, test := range tests {
   373  		test.expected.script = test.in
   374  		prefix := fmt.Sprintf("%s ImportScript #%d (%s)", prefix,
   375  			i, test.name)
   376  
   377  		addr, err := tc.manager.ImportScript(wb, test.in)
   378  		if err != nil {
   379  			tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
   380  		}
   381  		if !testAddress(tc, prefix, addr, &test.expected) {
   382  			tc.t.Fatalf("%s: testAddress failed for %v", prefix,
   383  				addr.Address())
   384  		}
   385  	}
   386  
   387  	// Setup a closure to test the results since the same tests need to be
   388  	// repeated with the manager unlocked and locked.
   389  	chainParams := tc.manager.ChainParams()
   390  	testResults := func() {
   391  		for i, test := range tests {
   392  			test.expected.script = test.in
   393  
   394  			// Use the Address API to retrieve each of the expected
   395  			// new addresses and ensure they're accurate.
   396  			utilAddr, err := stdaddr.NewAddressScriptHashV0(test.in,
   397  				chainParams)
   398  			if err != nil {
   399  				tc.t.Fatalf("%s NewAddressScriptHash #%d (%s): "+
   400  					"unexpected error: %v", prefix, i, test.name, err)
   401  			}
   402  			taPrefix := fmt.Sprintf("%s Address #%d (%s)", prefix,
   403  				i, test.name)
   404  			ma, err := tc.manager.Address(rb, utilAddr)
   405  			if err != nil {
   406  				tc.t.Fatalf("%s: unexpected error: %v", taPrefix, err)
   407  			}
   408  			if !testAddress(tc, taPrefix, ma, &test.expected) {
   409  				tc.t.Fatalf("%s: testAddress failed for %v", prefix,
   410  					ma.Address())
   411  			}
   412  		}
   413  	}
   414  
   415  	// The address manager could either be locked or unlocked here depending
   416  	// on whether or not it's a watching-only manager.  When it's unlocked,
   417  	// this will test both the public and private address data are accurate.
   418  	// When it's locked, it must be watching-only, so only the public
   419  	// address information is tested and the private functions are checked
   420  	// to ensure they return the expected ErrWatchingOnly error.
   421  	testResults()
   422  
   423  	// Lock the manager and retest all of the addresses to ensure the
   424  	// private information returns the expected error.
   425  	if err := tc.manager.Lock(); err != nil {
   426  		tc.t.Fatalf("Lock: unexpected error: %v", err)
   427  	}
   428  	tc.unlocked = false
   429  
   430  	testResults()
   431  }
   432  
   433  func TestManagerImports(t *testing.T) {
   434  	ctx := context.Background()
   435  	db, mgr, _, _, teardown, err := cloneDB(ctx, "imports.kv")
   436  	defer teardown()
   437  	if err != nil {
   438  		t.Fatal(err)
   439  	}
   440  	defer mgr.Close()
   441  
   442  	tc := &testContext{
   443  		t:            t,
   444  		db:           db,
   445  		manager:      mgr,
   446  		account:      0,
   447  		create:       true,
   448  		watchingOnly: false,
   449  	}
   450  
   451  	testImports := func(tc *testContext) {
   452  		err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
   453  			ns := tx.ReadWriteBucket(waddrmgrBucketKey)
   454  			testImportPrivateKey(tc, ns)
   455  			testImportScript(tc, ns)
   456  			return nil
   457  		})
   458  		if err != nil {
   459  			t.Fatalf("unexpected error: %v", err)
   460  		}
   461  	}
   462  
   463  	testImports(tc)
   464  
   465  	tc.create = false
   466  	tc.watchingOnly = true
   467  
   468  	testImports(tc)
   469  }
   470  
   471  // TestImportVotingAccount tests that importing voting accounts works properly.
   472  //
   473  // This function expects the manager is already locked when called and returns
   474  // with the manager locked.
   475  func TestImportVotingAccount(t *testing.T) {
   476  	ctx := context.Background()
   477  	db, mgr, _, _, teardown, err := cloneDB(ctx, "import_voting_account.kv")
   478  	defer teardown()
   479  	if err != nil {
   480  		t.Fatal(err)
   481  	}
   482  	defer mgr.Close()
   483  	privTestNet, err := hdkeychain.NewKeyFromString("tprvZUo1ZuEfLLFWg9kt"+
   484  		"EaXHf3HKq2EdfwY5pXFZ2rbg6HFzeaoJDp1qFZnuuuN6iUAG9EyNF4sH4RmJ"+
   485  		"b395XWYpdqQoXRoKkV88HZwgq95KfiK", chaincfg.TestNet3Params())
   486  	if err != nil {
   487  		t.Fatalf("unable to parse xpriv: %v", err)
   488  	}
   489  	account1 := "account 1"
   490  
   491  	tests := []struct {
   492  		name, pass, acctName string
   493  		want                 uint32
   494  		wantErr              *errors.Error
   495  		watchingOnly, unlock bool
   496  	}{{
   497  		name:         "ok watching only",
   498  		acctName:     account1,
   499  		pass:         "abc",
   500  		want:         ImportedAddrAccount + 1,
   501  		watchingOnly: true,
   502  	}, {
   503  		name:     "ok locked",
   504  		acctName: "account 2",
   505  		pass:     "abc",
   506  		want:     ImportedAddrAccount + 2,
   507  	}, {
   508  		name:     "ok unlocked",
   509  		acctName: "account 3",
   510  		pass:     "abc",
   511  		unlock:   true,
   512  		want:     ImportedAddrAccount + 3,
   513  	}, {
   514  		name:     "name taken",
   515  		acctName: account1,
   516  		pass:     "abc",
   517  		wantErr:  &errors.Error{Kind: errors.Exist},
   518  	}, {
   519  		name:     "no password",
   520  		acctName: "account 4",
   521  		wantErr:  &errors.Error{Kind: errors.Passphrase},
   522  	}, {
   523  		name:    "no name",
   524  		pass:    "abc",
   525  		wantErr: &errors.Error{Kind: errors.Invalid},
   526  	}}
   527  
   528  	for i, test := range tests {
   529  		tc := &testContext{
   530  			t:            t,
   531  			db:           db,
   532  			manager:      mgr,
   533  			create:       true,
   534  			watchingOnly: test.watchingOnly,
   535  		}
   536  		prefix := testNamePrefix(tc)
   537  		prefix = fmt.Sprintf("%s ImportVotingAccount #%d (%s)", prefix,
   538  			i, test.name)
   539  		err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
   540  			ns := tx.ReadWriteBucket(waddrmgrBucketKey)
   541  			if test.unlock {
   542  				if err := tc.manager.Unlock(ns, privPassphrase); err != nil {
   543  					tc.t.Fatalf("%s: Unlock: unexpected error: %v", prefix, err)
   544  				}
   545  				tc.unlocked = true
   546  				defer func() {
   547  					if err := tc.manager.Lock(); err != nil {
   548  						tc.t.Fatalf("%s: Lock: unexpected error: %v",
   549  							prefix, err)
   550  					}
   551  					tc.unlocked = false
   552  				}()
   553  			}
   554  			pass := []byte(test.pass)
   555  			got, err := tc.manager.ImportVotingAccount(tx, privTestNet,
   556  				pass, test.acctName)
   557  			if test.wantErr != nil {
   558  				if err == nil {
   559  					tc.t.Fatalf("%s: wanted error %v but got none",
   560  						prefix, test.wantErr)
   561  				}
   562  				kind := err.(*errors.Error).Kind
   563  				if !test.wantErr.Is(kind) {
   564  					tc.t.Fatalf("%s: wanted error %v but got %v",
   565  						prefix, test.wantErr, kind)
   566  				}
   567  				return nil
   568  			}
   569  			if err != nil {
   570  				tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
   571  			}
   572  			if got != test.want {
   573  				tc.t.Fatalf("%s: wanted account number %d but got %d", prefix,
   574  					test.want, got)
   575  			}
   576  
   577  			if err := tc.manager.UnlockAccount(tx, got, pass); err != nil {
   578  				tc.t.Fatalf("%s: Unlock: unexpected error: %v", prefix, err)
   579  			}
   580  			defer tc.manager.mtx.Unlock()
   581  			tc.manager.mtx.Lock()
   582  			acctInfo, err := tc.manager.loadAccountInfo(ns, got)
   583  			if err != nil {
   584  				tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
   585  			}
   586  			// Enusure the account type is importedVoting.
   587  			if acctInfo.acctType != importedVoting {
   588  				tc.t.Fatalf("%s: wanted account type %v but got %v", prefix,
   589  					importedVoting, acctInfo.acctType)
   590  			}
   591  			// Enusure xpriv is the same.
   592  			if acctInfo.acctKeyPriv.String() != privTestNet.String() {
   593  				tc.t.Fatalf("%s: wanted account xpriv %v but got %v", prefix,
   594  					privTestNet, acctInfo.acctKeyPriv)
   595  			}
   596  			return nil
   597  		})
   598  		if err != nil {
   599  			t.Fatalf("unexpected error: %v", err)
   600  		}
   601  	}
   602  }
   603  
   604  // TestImportAccount tests that importing accounts works properly.
   605  //
   606  // This function expects the manager is already locked when called and returns
   607  // with the manager locked.
   608  func TestImportAccount(t *testing.T) {
   609  	ctx := context.Background()
   610  	db, mgr, _, _, teardown, err := cloneDB(ctx, "import_account.kv")
   611  	defer teardown()
   612  	if err != nil {
   613  		t.Fatal(err)
   614  	}
   615  	defer mgr.Close()
   616  	privTestNet, err := hdkeychain.NewKeyFromString("tprvZUo1ZuEfLLFWg9kt"+
   617  		"EaXHf3HKq2EdfwY5pXFZ2rbg6HFzeaoJDp1qFZnuuuN6iUAG9EyNF4sH4RmJ"+
   618  		"b395XWYpdqQoXRoKkV88HZwgq95KfiK", chaincfg.TestNet3Params())
   619  	if err != nil {
   620  		t.Fatalf("unable to parse xpriv: %v", err)
   621  	}
   622  	account1 := "account 1"
   623  
   624  	tests := []struct {
   625  		name, acctName              string
   626  		want                        uint32
   627  		wantErr                     *errors.Error
   628  		watchingOnly, discoverUsage bool
   629  	}{{
   630  		name:         "ok watching only",
   631  		acctName:     account1,
   632  		want:         ImportedAddrAccount + 1,
   633  		watchingOnly: true,
   634  	}, {
   635  		name:     "ok",
   636  		acctName: "account 2",
   637  		want:     ImportedAddrAccount + 2,
   638  	}, {
   639  		name:     "name taken",
   640  		acctName: account1,
   641  		wantErr:  &errors.Error{Kind: errors.Exist},
   642  	}, {
   643  		name:          "address exists",
   644  		acctName:      "account 3",
   645  		discoverUsage: true,
   646  		wantErr:       &errors.Error{Kind: errors.Exist},
   647  	}, {
   648  		name:    "no name",
   649  		wantErr: &errors.Error{Kind: errors.Invalid},
   650  	}}
   651  
   652  	for i, test := range tests {
   653  		tc := &testContext{
   654  			t:            t,
   655  			db:           db,
   656  			manager:      mgr,
   657  			create:       true,
   658  			watchingOnly: test.watchingOnly,
   659  		}
   660  		prefix := testNamePrefix(tc)
   661  		prefix = fmt.Sprintf("%s importAccount #%d (%s)", prefix,
   662  			i, test.name)
   663  		err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
   664  			ns := tx.ReadWriteBucket(waddrmgrBucketKey)
   665  			if err := tc.manager.Unlock(ns, privPassphrase); err != nil {
   666  				tc.t.Fatalf("%s: Unlock: unexpected error: %v", prefix, err)
   667  			}
   668  			tc.unlocked = true
   669  			defer func() {
   670  				if err := tc.manager.Lock(); err != nil {
   671  					tc.t.Fatalf("%s: Lock: unexpected error: %v",
   672  						prefix, err)
   673  				}
   674  				tc.unlocked = false
   675  			}()
   676  			if test.discoverUsage {
   677  				err = mgr.SyncAccountToAddrIndex(ns, ImportedAddrAccount+1, 0, ExternalBranch)
   678  				if err != nil {
   679  					return err
   680  				}
   681  			}
   682  			got, err := tc.manager.importAccount(tx, actBIP0044,
   683  				privTestNet, test.acctName)
   684  			if test.wantErr != nil {
   685  				if err == nil {
   686  					tc.t.Fatalf("%s: wanted error %v but got none",
   687  						prefix, test.wantErr)
   688  				}
   689  				kind := err.(*errors.Error).Kind
   690  				if !test.wantErr.Is(kind) {
   691  					tc.t.Fatalf("%s: wanted error %v but got %v",
   692  						prefix, test.wantErr, kind)
   693  				}
   694  				return nil
   695  			}
   696  			if err != nil {
   697  				tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
   698  			}
   699  			if got != test.want {
   700  				tc.t.Fatalf("%s: wanted account number %d but got %d", prefix,
   701  					test.want, got)
   702  			}
   703  			defer tc.manager.mtx.Unlock()
   704  			tc.manager.mtx.Lock()
   705  			acctInfo, err := tc.manager.loadAccountInfo(ns, got)
   706  			if err != nil {
   707  				tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
   708  			}
   709  			// Enusure xpriv is the same.
   710  			if acctInfo.acctKeyPriv.String() != privTestNet.String() {
   711  				tc.t.Fatalf("%s: wanted account xpriv %v but got %v", prefix,
   712  					privTestNet, acctInfo.acctKeyPriv)
   713  			}
   714  			return nil
   715  		})
   716  		if err != nil {
   717  			t.Fatalf("unexpected error: %v", err)
   718  		}
   719  	}
   720  }
   721  
   722  // testChangePassphrase ensures changes both the public and private passphrases
   723  // works as intended.
   724  func testChangePassphrase(tc *testContext, wb walletdb.ReadWriteBucket) {
   725  	// Force an error when changing the passphrase due to failure to
   726  	// generate a new secret key by replacing the generation function one
   727  	// that intentionally errors.
   728  	testName := "ChangePassphrase (public) with invalid new secret key"
   729  
   730  	var err error
   731  	TstRunWithReplacedNewSecretKey(func() {
   732  		err = tc.manager.ChangePassphrase(wb, pubPassphrase, pubPassphrase2, false)
   733  	})
   734  	if !errors.Is(err, errors.Crypto) {
   735  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   736  	}
   737  
   738  	rb := wb.(walletdb.ReadBucket)
   739  
   740  	// Attempt to change public passphrase with invalid old passphrase.
   741  	testName = "ChangePassphrase (public) with invalid old passphrase"
   742  	err = tc.manager.ChangePassphrase(wb, []byte("bogus"), pubPassphrase2, false)
   743  	if !errors.Is(err, errors.Passphrase) {
   744  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   745  	}
   746  
   747  	// Change the public passphrase.
   748  	testName = "ChangePassphrase (public)"
   749  	err = tc.manager.ChangePassphrase(wb, pubPassphrase, pubPassphrase2, false)
   750  	if err != nil {
   751  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   752  	}
   753  
   754  	// Ensure the public passphrase was successfully changed.
   755  	if !tc.manager.TstCheckPublicPassphrase(pubPassphrase2) {
   756  		tc.t.Fatalf("%s: passphrase does not match", testName)
   757  	}
   758  
   759  	// Change the private passphrase back to what it was.
   760  	err = tc.manager.ChangePassphrase(wb, pubPassphrase2, pubPassphrase, false)
   761  	if err != nil {
   762  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   763  	}
   764  
   765  	// Attempt to change private passphrase with invalid old passphrase.
   766  	// The error should be ErrWrongPassphrase or ErrWatchingOnly depending
   767  	// on the type of the address manager.
   768  	testName = "ChangePassphrase (private) with invalid old passphrase"
   769  	err = tc.manager.ChangePassphrase(wb, []byte("bogus"), privPassphrase2, true)
   770  	wantErrCode := errors.Passphrase
   771  	if tc.watchingOnly {
   772  		wantErrCode = errors.WatchingOnly
   773  	}
   774  	if !errors.Is(err, wantErrCode) {
   775  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   776  	}
   777  
   778  	// Everything after this point involves testing that the private
   779  	// passphrase for the address manager can be changed successfully.
   780  	// This is not possible for watching-only mode, so just exit now in that
   781  	// case.
   782  	if tc.watchingOnly {
   783  		return
   784  	}
   785  
   786  	// Change the private passphrase.
   787  	testName = "ChangePassphrase (private)"
   788  	err = tc.manager.ChangePassphrase(wb, privPassphrase, privPassphrase2, true)
   789  	if err != nil {
   790  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   791  	}
   792  
   793  	// Unlock the manager with the new passphrase to ensure it changed as
   794  	// expected.
   795  	if err := tc.manager.Unlock(rb, privPassphrase2); err != nil {
   796  		tc.t.Fatalf("%s: failed to unlock with new private "+
   797  			"passphrase: %v", testName, err)
   798  	}
   799  	tc.unlocked = true
   800  
   801  	// Change the private passphrase back to what it was while the manager
   802  	// is unlocked to ensure that path works properly as well.
   803  	err = tc.manager.ChangePassphrase(wb, privPassphrase2, privPassphrase, true)
   804  	if err != nil {
   805  		tc.t.Fatalf("%s: unexpected error: %v", testName, err)
   806  	}
   807  	if tc.manager.IsLocked() {
   808  		tc.t.Fatalf("%s: manager is locked", testName)
   809  	}
   810  
   811  	// Relock the manager for future tests.
   812  	if err := tc.manager.Lock(); err != nil {
   813  		tc.t.Fatalf("Lock: unexpected error: %v", err)
   814  	}
   815  	tc.unlocked = false
   816  }
   817  
   818  // testNewAccount tests the new account creation func of the address manager works
   819  // as expected.
   820  func testNewAccount(tc *testContext, wb walletdb.ReadWriteBucket) {
   821  	if !tc.create {
   822  		return
   823  	}
   824  
   825  	if tc.watchingOnly {
   826  		// Creating new accounts in watching-only mode should return ErrWatchingOnly
   827  		_, err := tc.manager.NewAccount(wb, "test")
   828  		if !errors.Is(err, errors.WatchingOnly) {
   829  			tc.t.Fatalf("NewAccount: expected ErrWatchingOnly, got %v", err)
   830  		}
   831  	}
   832  
   833  	// Creating new accounts when wallet is locked should return ErrLocked
   834  	_, err := tc.manager.NewAccount(wb, "test")
   835  	if !errors.Is(err, errors.Locked) {
   836  		tc.t.Fatalf("NewAccount: expected ErrLocked, got %v", err)
   837  	}
   838  
   839  	rb := wb.(walletdb.ReadBucket)
   840  
   841  	// Unlock the wallet to decrypt cointype keys required to derive
   842  	// account keys
   843  	if err := tc.manager.Unlock(rb, privPassphrase); err != nil {
   844  		tc.t.Fatalf("Unlock: unexpected error: %v", err)
   845  	}
   846  	tc.unlocked = true
   847  
   848  	testName := "acct-create"
   849  	expectedAccount := tc.account + 1
   850  	account, err := tc.manager.NewAccount(wb, testName)
   851  	if err != nil {
   852  		tc.t.Fatalf("NewAccount: unexpected error: %v", err)
   853  	}
   854  	if account != expectedAccount {
   855  		tc.t.Fatalf("NewAccount account mismatch -- got %d, want %d",
   856  			account, expectedAccount)
   857  	}
   858  
   859  	// Test duplicate account name error
   860  	_, err = tc.manager.NewAccount(wb, testName)
   861  	if !errors.Is(err, errors.Exist) {
   862  		tc.t.Fatalf("NewAccount: expected ErrExist, got %v", err)
   863  	}
   864  	// Test account name validation
   865  	testName = "" // Empty account names are not allowed
   866  	_, err = tc.manager.NewAccount(wb, testName)
   867  	if !errors.Is(err, errors.Invalid) {
   868  		tc.t.Fatalf("NewAccount: expected ErrInvalid, got %v", err)
   869  	}
   870  	testName = "imported" // A reserved account name
   871  	_, err = tc.manager.NewAccount(wb, testName)
   872  	if !errors.Is(err, errors.Invalid) {
   873  		tc.t.Fatalf("NewAccount: expected ErrInvalid, got %v", err)
   874  	}
   875  }
   876  
   877  // testLookupAccount tests the basic account lookup func of the address manager
   878  // works as expected.
   879  func testLookupAccount(tc *testContext, rb walletdb.ReadBucket) {
   880  	// Lookup accounts created earlier in testNewAccount
   881  	expectedAccounts := map[string]uint32{
   882  		TstDefaultAccountName:   DefaultAccountNum,
   883  		ImportedAddrAccountName: ImportedAddrAccount,
   884  	}
   885  	for acctName, expectedAccount := range expectedAccounts {
   886  		account, err := tc.manager.LookupAccount(rb, acctName)
   887  		if err != nil {
   888  			tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
   889  		}
   890  		if account != expectedAccount {
   891  			tc.t.Fatalf("LookupAccount account mismatch -- got %d, want %d",
   892  				account, expectedAccount)
   893  		}
   894  	}
   895  	// Test account not found error
   896  	testName := "non existent account"
   897  	_, err := tc.manager.LookupAccount(rb, testName)
   898  	if !errors.Is(err, errors.NotExist) {
   899  		tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
   900  	}
   901  
   902  	// Test last account
   903  	lastAccount, err := tc.manager.LastAccount(rb)
   904  	if err != nil {
   905  		tc.t.Fatalf("LastAccount failed: %v", err)
   906  	}
   907  
   908  	expectedLastAccount := uint32(1)
   909  	if lastAccount != expectedLastAccount {
   910  		tc.t.Fatalf("LookupAccount account mismatch -- got %d, want %d",
   911  			lastAccount, expectedLastAccount)
   912  	}
   913  }
   914  
   915  // testRenameAccount tests the rename account func of the address manager works
   916  // as expected.
   917  func testRenameAccount(tc *testContext, wb walletdb.ReadWriteBucket) {
   918  	rb := wb.(walletdb.ReadBucket)
   919  	acctName, err := tc.manager.AccountName(rb, tc.account)
   920  	if err != nil {
   921  		tc.t.Fatalf("AccountName: unexpected error: %v", err)
   922  	}
   923  	testName := acctName + "-renamed"
   924  	err = tc.manager.RenameAccount(wb, tc.account, testName)
   925  	if err != nil {
   926  		tc.t.Fatalf("RenameAccount: unexpected error: %v", err)
   927  	}
   928  	newName, err := tc.manager.AccountName(rb, tc.account)
   929  	if err != nil {
   930  		tc.t.Fatalf("AccountName: unexpected error: %v", err)
   931  	}
   932  	if newName != testName {
   933  		tc.t.Fatalf("RenameAccount account name mismatch -- got %s, want %s",
   934  			newName, testName)
   935  	}
   936  	// Test duplicate account name error
   937  	err = tc.manager.RenameAccount(wb, tc.account, testName)
   938  	if !errors.Is(err, errors.Exist) {
   939  		tc.t.Fatalf("RenameAccount: unexpected error: %v", err)
   940  	}
   941  	// Test old account name is no longer valid
   942  	_, err = tc.manager.LookupAccount(wb, acctName)
   943  	if err == nil {
   944  		tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
   945  	}
   946  	if !errors.Is(err, errors.NotExist) {
   947  		tc.t.Fatalf("LookupAccount: unexpected error: %v", err)
   948  	}
   949  }
   950  
   951  // testForEachAccount tests the retrieve all accounts func of the address
   952  // manager works as expected.
   953  func testForEachAccount(tc *testContext, rb walletdb.ReadBucket) {
   954  	prefix := testNamePrefix(tc) + " testForEachAccount"
   955  	expectedAccounts := []uint32{0, 1, 2147483647}
   956  
   957  	var accounts []uint32
   958  	err := tc.manager.ForEachAccount(rb, func(account uint32) error {
   959  		accounts = append(accounts, account)
   960  		return nil
   961  	})
   962  	if err != nil {
   963  		tc.t.Fatalf("%s: unexpected error: %v", prefix, err)
   964  	}
   965  	if len(accounts) != len(expectedAccounts) {
   966  		tc.t.Fatalf("%s: unexpected number of accounts - got "+
   967  			"%d, want %d", prefix, len(accounts), len(expectedAccounts))
   968  	}
   969  	for i, account := range accounts {
   970  		if expectedAccounts[i] != account {
   971  			tc.t.Fatalf("%s #%d: account mismatch -- got %d, want %d,"+
   972  				" accounts: %v", prefix, i, account, expectedAccounts[i], accounts)
   973  		}
   974  	}
   975  }
   976  
   977  // testEncryptDecryptErrors ensures that errors which occur while encrypting and
   978  // decrypting data return the expected errors.
   979  func testEncryptDecryptErrors(ctx context.Context, tc *testContext) {
   980  	invalidKeyType := CryptoKeyType(0xff)
   981  	if _, err := tc.manager.Encrypt(invalidKeyType, []byte{}); err == nil {
   982  		tc.t.Fatal("Encrypt accepted an invalid key type!")
   983  	}
   984  
   985  	if _, err := tc.manager.Decrypt(invalidKeyType, []byte{}); err == nil {
   986  		tc.t.Fatal("Encrypt accepted an invalid key type!")
   987  	}
   988  
   989  	if !tc.manager.IsLocked() {
   990  		tc.t.Fatal("Manager should be locked at this point.")
   991  	}
   992  
   993  	var err error
   994  	// Now the mgr is locked and encrypting/decrypting with private
   995  	// keys should fail.
   996  	_, err = tc.manager.Encrypt(CKTPrivate, []byte{})
   997  	if !errors.Is(err, errors.Locked) {
   998  		tc.t.Fatal("encryption with private key should fail when manager" +
   999  			" is locked")
  1000  	}
  1001  
  1002  	_, err = tc.manager.Decrypt(CKTPrivate, []byte{})
  1003  	if !errors.Is(err, errors.Locked) {
  1004  		tc.t.Fatal("decryption with private key should fail when manager" +
  1005  			" is locked")
  1006  
  1007  	}
  1008  
  1009  	err = walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
  1010  		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
  1011  		// Unlock the manager for these tests
  1012  		return tc.manager.Unlock(ns, privPassphrase)
  1013  	})
  1014  	if err != nil {
  1015  		tc.t.Fatalf("Attempted to unlock the manager, but failed: %v", err)
  1016  	}
  1017  
  1018  	// Make sure to cover the ErrCrypto error path in Encrypt.
  1019  	TstRunWithFailingCryptoKeyPriv(tc.manager, func() {
  1020  		_, err = tc.manager.Encrypt(CKTPrivate, []byte{})
  1021  	})
  1022  	if !errors.Is(err, errors.Crypto) {
  1023  		tc.t.Fatal("failed encryption")
  1024  	}
  1025  
  1026  	// Lock the manager.
  1027  	if err := tc.manager.Lock(); err != nil {
  1028  		tc.t.Fatal("Attempted to lock the manager, but failed:", err)
  1029  	}
  1030  }
  1031  
  1032  // testEncryptDecrypt ensures that encrypting and decrypting data with the
  1033  // the various crypto key types works as expected.
  1034  func testEncryptDecrypt(ctx context.Context, tc *testContext) {
  1035  	plainText := []byte("this is a plaintext")
  1036  
  1037  	err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
  1038  		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
  1039  		// Make sure address manager is unlocked
  1040  		return tc.manager.Unlock(ns, privPassphrase)
  1041  	})
  1042  	if err != nil {
  1043  		tc.t.Fatal("Attempted to unlock the manager, but failed: ", err)
  1044  	}
  1045  
  1046  	keyTypes := []CryptoKeyType{
  1047  		CKTPublic,
  1048  		CKTPrivate,
  1049  	}
  1050  
  1051  	for _, keyType := range keyTypes {
  1052  		cipherText, err := tc.manager.Encrypt(keyType, plainText)
  1053  		if err != nil {
  1054  			tc.t.Fatalf("Failed to encrypt plaintext: %v", err)
  1055  		}
  1056  
  1057  		decryptedCipherText, err := tc.manager.Decrypt(keyType, cipherText)
  1058  		if err != nil {
  1059  			tc.t.Fatalf("Failed to decrypt plaintext: %v", err)
  1060  		}
  1061  
  1062  		if !reflect.DeepEqual(decryptedCipherText, plainText) {
  1063  			tc.t.Fatal("Got:", decryptedCipherText, ", want:", plainText)
  1064  		}
  1065  	}
  1066  
  1067  	// Lock the manager.
  1068  	if err := tc.manager.Lock(); err != nil {
  1069  		tc.t.Fatal("Attempted to lock the manager, but failed:", err)
  1070  	}
  1071  }
  1072  
  1073  func TestManagerEncryptDecrypt(t *testing.T) {
  1074  	ctx := context.Background()
  1075  
  1076  	db, mgr, _, _, teardown, err := cloneDB(ctx, "encrypt_decrypt.kv")
  1077  	defer teardown()
  1078  	if err != nil {
  1079  		t.Fatal(err)
  1080  	}
  1081  	defer mgr.Close()
  1082  
  1083  	tc := &testContext{
  1084  		t:            t,
  1085  		db:           db,
  1086  		manager:      mgr,
  1087  		account:      0,
  1088  		create:       false,
  1089  		watchingOnly: false,
  1090  	}
  1091  
  1092  	testEncryptDecryptErrors(ctx, tc)
  1093  	testEncryptDecrypt(ctx, tc)
  1094  }
  1095  
  1096  func TestChangePassphrase(t *testing.T) {
  1097  	ctx := context.Background()
  1098  	db, mgr, _, _, teardown, err := cloneDB(ctx, "change_passphrase.kv")
  1099  	defer teardown()
  1100  	if err != nil {
  1101  		t.Fatal(err)
  1102  	}
  1103  	defer mgr.Close()
  1104  
  1105  	tc := &testContext{
  1106  		t:            t,
  1107  		db:           db,
  1108  		manager:      mgr,
  1109  		account:      0,
  1110  		create:       false,
  1111  		watchingOnly: false,
  1112  	}
  1113  
  1114  	err = walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
  1115  		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
  1116  		testChangePassphrase(tc, ns)
  1117  		return nil
  1118  	})
  1119  	if err != nil {
  1120  		tc.t.Errorf("unexpected error: %v", err)
  1121  	}
  1122  }
  1123  
  1124  // testManagerAPI tests the functions provided by the Manager API.
  1125  func testManagerAPI(ctx context.Context, tc *testContext) {
  1126  	err := walletdb.Update(ctx, tc.db, func(tx walletdb.ReadWriteTx) error {
  1127  		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
  1128  
  1129  		testLocking(tc, ns)
  1130  
  1131  		// Reset default account
  1132  		tc.account = 0
  1133  		testNewAccount(tc, ns)
  1134  		testLookupAccount(tc, ns)
  1135  		testForEachAccount(tc, ns)
  1136  
  1137  		// Rename account 1 "acct-create"
  1138  		tc.account = 1
  1139  		testRenameAccount(tc, ns)
  1140  
  1141  		return nil
  1142  	})
  1143  	if err != nil {
  1144  		tc.t.Errorf("unexpected error: %v", err)
  1145  	}
  1146  }
  1147  
  1148  // TestManagerWatchingOnly tests various facets of a watching-only address
  1149  // manager such as running the full set of API tests against a newly converted
  1150  // copy as well as when it is opened from an existing namespace.
  1151  func TestManagerWatchingOnly(t *testing.T) {
  1152  	ctx := context.Background()
  1153  	db, mgr, _, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv")
  1154  	defer teardown()
  1155  	if err != nil {
  1156  		t.Fatal(err)
  1157  	}
  1158  
  1159  	// Run all of the manager API tests in create mode
  1160  	testManagerAPI(ctx, &testContext{
  1161  		t:            t,
  1162  		db:           db,
  1163  		manager:      mgr,
  1164  		account:      0,
  1165  		create:       true,
  1166  		watchingOnly: false,
  1167  	})
  1168  	mgr.Close()
  1169  
  1170  	mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase)
  1171  	if err != nil {
  1172  		t.Fatalf("unexpected error: %v", err)
  1173  	}
  1174  	defer mgr.Close()
  1175  
  1176  	err = walletdb.Update(ctx, db, func(tx walletdb.ReadWriteTx) error {
  1177  		ns := tx.ReadWriteBucket(waddrmgrBucketKey)
  1178  		if err = mgr.ConvertToWatchingOnly(ns); err != nil {
  1179  			t.Fatalf("failed to convert manager to watching-only: %v", err)
  1180  		}
  1181  		return nil
  1182  	})
  1183  	if err != nil {
  1184  		t.Fatal(err)
  1185  	}
  1186  
  1187  	// Run all of the manager API tests against the converted manager.
  1188  	testManagerAPI(ctx, &testContext{
  1189  		t:            t,
  1190  		db:           db,
  1191  		manager:      mgr,
  1192  		account:      0,
  1193  		create:       false,
  1194  		watchingOnly: true,
  1195  	})
  1196  
  1197  	// Open the watching-only manager and run all the tests again.
  1198  	mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase)
  1199  	if err != nil {
  1200  		t.Fatalf("unexpected error: %v", err)
  1201  	}
  1202  	defer mgr.Close()
  1203  
  1204  	testManagerAPI(ctx, &testContext{
  1205  		t:            t,
  1206  		db:           db,
  1207  		manager:      mgr,
  1208  		account:      0,
  1209  		create:       false,
  1210  		watchingOnly: true,
  1211  	})
  1212  }
  1213  
  1214  // TestManager performs a full suite of tests against the address manager API in
  1215  // create mode. It makes use of a test context because the address manager is
  1216  // persistent and much of the testing involves having specific state.
  1217  func TestManager(t *testing.T) {
  1218  	if testing.Short() {
  1219  		t.Skip("short: skipping TestManager")
  1220  	}
  1221  
  1222  	ctx := context.Background()
  1223  	db, mgr, _, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv")
  1224  	defer teardown()
  1225  	if err != nil {
  1226  		t.Fatal(err)
  1227  	}
  1228  
  1229  	testManagerAPI(ctx, &testContext{
  1230  		t:            t,
  1231  		db:           db,
  1232  		manager:      mgr,
  1233  		account:      0,
  1234  		create:       true,
  1235  		watchingOnly: false,
  1236  	})
  1237  	mgr.Close()
  1238  
  1239  	// Open the manager and run all the tests again in open mode which
  1240  	// avoids reinserting new addresses like the create mode tests do.
  1241  	mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase)
  1242  	if err != nil {
  1243  		t.Fatalf("Open: unexpected error: %v", err)
  1244  	}
  1245  	defer mgr.Close()
  1246  
  1247  	tc := &testContext{
  1248  		t:            t,
  1249  		db:           db,
  1250  		manager:      mgr,
  1251  		account:      0,
  1252  		create:       false,
  1253  		watchingOnly: false,
  1254  	}
  1255  	testManagerAPI(ctx, tc)
  1256  }
  1257  
  1258  func TestMain(m *testing.M) {
  1259  	testDir, err := os.MkdirTemp("", "udb-")
  1260  	if err != nil {
  1261  		fmt.Printf("Unable to create temp directory: %v", err)
  1262  		os.Exit(1)
  1263  	}
  1264  
  1265  	emptyDbPath = filepath.Join(testDir, "empty.kv")
  1266  	teardown := func() {
  1267  		os.RemoveAll(testDir)
  1268  	}
  1269  
  1270  	ctx := context.Background()
  1271  	err = createEmptyDB(ctx)
  1272  	if err != nil {
  1273  		fmt.Printf("Unable to create empty test db: %v\n", err)
  1274  		teardown()
  1275  		os.Exit(1)
  1276  	}
  1277  
  1278  	exitCode := m.Run()
  1279  	teardown()
  1280  	os.Exit(exitCode)
  1281  }