diff --git a/wallet/wallet.go b/wallet/wallet.go index ca70d7a..caae0a0 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -1376,6 +1376,33 @@ func (w *Wallet) ActiveAddresses() map[btcutil.Address]*AddressInfo { return addrs } +// ExtendActiveAddresses gets or creates the next n addresses from the +// address chain and marks each as active. This is used to recover +// deterministic (not imported) addresses from a wallet backup, or to +// keep the active addresses in sync between an encrypted wallet with +// private keys and an exported watching wallet without. +// +// A slice is returned with the btcutil.Address of each new address. +// The blockchain must be rescanned for these addresses. +func (w *Wallet) ExtendActiveAddresses(n int, keypoolSize uint) ([]btcutil.Address, error) { + if n <= 0 { + return nil, errors.New("n is not positive") + } + + last := w.addrMap[*w.chainIdxMap[w.highestUsed]] + bs := &BlockStamp{Height: last.firstBlock} + + addrs := make([]btcutil.Address, 0, n) + for i := 0; i < n; i++ { + addr, err := w.NextChainedAddress(bs, keypoolSize) + if err != nil { + return nil, err + } + addrs = append(addrs, addr) + } + return addrs, nil +} + type walletFlags struct { useEncryption bool watchingOnly bool diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index 001d899..cb71e1b 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -579,6 +579,52 @@ func TestWatchingWalletExport(t *testing.T) { } } + // Test that ExtendActiveAddresses for the watching wallet match + // manually requested addresses of the original wallet. + newAddrs := make([]btcutil.Address, 0, keypoolSize) + for i := 0; i < keypoolSize; i++ { + addr, err := w.NextChainedAddress(createdAt, keypoolSize) + if err != nil { + t.Errorf("Cannot get next chained address for original wallet: %v", err) + return + } + newAddrs = append(newAddrs, addr) + } + newWWAddrs, err := ww.ExtendActiveAddresses(keypoolSize, keypoolSize) + if err != nil { + t.Errorf("Cannot extend active addresses for watching wallet: %v", err) + return + } + for i := range newAddrs { + if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() { + t.Errorf("Extended active addresses do not match manually requested addresses.") + return + } + } + + // Test ExtendActiveAddresses for the original wallet after manually + // requesting addresses for the watching wallet. + newWWAddrs = make([]btcutil.Address, 0, keypoolSize) + for i := 0; i < keypoolSize; i++ { + addr, err := ww.NextChainedAddress(createdAt, keypoolSize) + if err != nil { + t.Errorf("Cannot get next chained address for watching wallet: %v", err) + return + } + newWWAddrs = append(newWWAddrs, addr) + } + newAddrs, err = w.ExtendActiveAddresses(keypoolSize, keypoolSize) + if err != nil { + t.Errorf("Cannot extend active addresses for original wallet: %v", err) + return + } + for i := range newAddrs { + if newAddrs[i].EncodeAddress() != newWWAddrs[i].EncodeAddress() { + t.Errorf("Extended active addresses do not match manually requested addresses.") + return + } + } + // Test (de)serialization of watching wallet. buf := new(bytes.Buffer) _, err = ww.WriteTo(buf)