decred.org/dcrwallet/v3@v3.1.0/wallet/discovery_test.go (about) 1 // Copyright (c) 2020 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package wallet 6 7 import ( 8 "context" 9 "testing" 10 11 "decred.org/dcrwallet/v3/wallet/walletdb" 12 ) 13 14 // TestDiscoveryCursorPos tests that the account cursor index is not reset 15 // during address discovery such that an address could be reused. 16 func TestDiscoveryCursorPos(t *testing.T) { 17 ctx := context.Background() 18 19 cfg := basicWalletConfig 20 // normally would just do the upgrade, but the buffers record 21 // off-by-ones after the upgrade. will be fixed in a later commit. 22 cfg.DisableCoinTypeUpgrades = true 23 24 w, teardown := testWallet(ctx, t, &cfg) 25 defer teardown() 26 27 /* 28 // Upgrade the cointype before proceeding. The test is invalid if a 29 // cointype upgrade occurs during discovery. 30 err := w.UpgradeToSLIP0044CoinType(ctx) 31 if err != nil { 32 t.Fatal(err) 33 } 34 */ 35 36 // Advance the cursor within the gap limit but without recording the 37 // returned addresses in the database (these may be persisted during a 38 // later update). 39 w.addressBuffersMu.Lock() 40 xpub := w.addressBuffers[0].albExternal.branchXpub 41 w.addressBuffers[0].albExternal.cursor = 9 // 0-9 have been returned 42 w.addressBuffersMu.Unlock() 43 44 // Perform address discovery 45 // All peer funcs may be left unimplemented; wallet only records the genesis block. 46 peer := &peerFuncs{} 47 err := w.DiscoverActiveAddresses(ctx, peer, &w.chainParams.GenesisHash, false, w.GapLimit()) 48 if err != nil { 49 t.Fatal(err) 50 } 51 52 w.addressBuffersMu.Lock() 53 lastUsed := w.addressBuffers[0].albExternal.lastUsed 54 cursor := w.addressBuffers[0].albExternal.cursor 55 w.addressBuffersMu.Unlock() 56 wasLastUsed := ^uint32(0) 57 wasCursor := uint32(9) 58 if lastUsed != wasLastUsed || cursor != wasCursor { 59 t.Errorf("cursor was reset: lastUsed=%d (want %d) cursor=%d (want %d)", 60 lastUsed, wasLastUsed, cursor, wasCursor) 61 } 62 63 // Manually mark an address between the lastUsed and cursor as used, and 64 // addresses through the cursor as returned, then perform discovery 65 // again. The cursor should be reduced such that the next returned 66 // address would be the same as before, without introducing a backwards 67 // reset or wasted addresses. 68 addr4, err := deriveChildAddress(xpub, 4, w.chainParams) 69 if err != nil { 70 t.Fatal(err) 71 } 72 err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error { 73 ns := dbtx.ReadBucket(waddrmgrNamespaceKey) 74 err = w.manager.MarkReturnedChildIndex(dbtx, 0, 0, 9) // 0-9 have been returned 75 if err != nil { 76 return err 77 } 78 maddr4, err := w.manager.Address(ns, addr4) 79 if err != nil { 80 return err 81 } 82 return w.markUsedAddress("", dbtx, maddr4) 83 }) 84 if err != nil { 85 t.Fatal(err) 86 } 87 err = w.DiscoverActiveAddresses(ctx, peer, &w.chainParams.GenesisHash, false, w.GapLimit()) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 w.addressBuffersMu.Lock() 93 lastUsed = w.addressBuffers[0].albExternal.lastUsed 94 cursor = w.addressBuffers[0].albExternal.cursor 95 w.addressBuffersMu.Unlock() 96 wasLastUsed += 5 97 wasCursor -= 5 98 if lastUsed != wasLastUsed || cursor != wasCursor { 99 t.Errorf("cursor was reset: lastUsed=%d (want %d) cursor=%d (want %d)", 100 lastUsed, wasLastUsed, cursor, wasCursor) 101 } 102 }