From b774170afdd64e71539fc659071a1c1e921f1975 Mon Sep 17 00:00:00 2001 From: Roy Lee Date: Fri, 16 Sep 2022 09:13:09 -0700 Subject: [PATCH] consolidate: merge internal/external branches --- chain/block_filterer.go | 68 +++-------- chain/interface.go | 14 +-- chain/neutrino.go | 14 +-- chain/rpc.go | 11 +- waddrmgr/manager.go | 9 +- waddrmgr/manager_test.go | 40 +++---- waddrmgr/scoped_manager.go | 233 ++++++++++--------------------------- wallet/import.go | 8 +- wallet/recovery.go | 6 +- wallet/wallet.go | 50 +++----- 10 files changed, 132 insertions(+), 321 deletions(-) diff --git a/chain/block_filterer.go b/chain/block_filterer.go index dfcdbb4..f8bdcbc 100644 --- a/chain/block_filterer.go +++ b/chain/block_filterer.go @@ -31,26 +31,18 @@ type BlockFilterer struct { // Params specifies the chain params of the current network. Params *chaincfg.Params - // ExReverseFilter holds a reverse index mapping an external address to + // ReverseFilter holds a reverse index mapping an external address to // the scoped index from which it was derived. - ExReverseFilter map[string]waddrmgr.ScopedIndex - - // InReverseFilter holds a reverse index mapping an internal address to - // the scoped index from which it was derived. - InReverseFilter map[string]waddrmgr.ScopedIndex + ReverseFilter map[string]waddrmgr.ScopedIndex // WathcedOutPoints is a global set of outpoints being tracked by the // wallet. This allows the block filterer to check for spends from an // outpoint we own. WatchedOutPoints map[wire.OutPoint]btcutil.Address - // FoundExternal is a two-layer map recording the scope and index of + // FoundAddresses is a two-layer map recording the scope and index of // external addresses found in a single block. - FoundExternal map[waddrmgr.KeyScope]map[uint32]struct{} - - // FoundInternal is a two-layer map recording the scope and index of - // internal addresses found in a single block. - FoundInternal map[waddrmgr.KeyScope]map[uint32]struct{} + FoundAddresses map[waddrmgr.ScopedIndex]struct{} // FoundOutPoints is a set of outpoints found in a single block whose // address belongs to the wallet. @@ -71,31 +63,20 @@ func NewBlockFilterer(params *chaincfg.Params, // Construct a reverse index by address string for the requested // external addresses. - nExAddrs := len(req.ExternalAddrs) - exReverseFilter := make(map[string]waddrmgr.ScopedIndex, nExAddrs) - for scopedIndex, addr := range req.ExternalAddrs { - exReverseFilter[addr.EncodeAddress()] = scopedIndex + nAddrs := len(req.Addresses) + reverseFilter := make(map[string]waddrmgr.ScopedIndex, nAddrs) + for scopedIndex, addr := range req.Addresses { + reverseFilter[addr.EncodeAddress()] = scopedIndex } - // Construct a reverse index by address string for the requested - // internal addresses. - nInAddrs := len(req.InternalAddrs) - inReverseFilter := make(map[string]waddrmgr.ScopedIndex, nInAddrs) - for scopedIndex, addr := range req.InternalAddrs { - inReverseFilter[addr.EncodeAddress()] = scopedIndex - } - - foundExternal := make(map[waddrmgr.KeyScope]map[uint32]struct{}) - foundInternal := make(map[waddrmgr.KeyScope]map[uint32]struct{}) + foundAddresses := make(map[waddrmgr.ScopedIndex]struct{}) foundOutPoints := make(map[wire.OutPoint]btcutil.Address) return &BlockFilterer{ Params: params, - ExReverseFilter: exReverseFilter, - InReverseFilter: inReverseFilter, + ReverseFilter: reverseFilter, WatchedOutPoints: req.WatchedOutPoints, - FoundExternal: foundExternal, - FoundInternal: foundInternal, + FoundAddresses: foundAddresses, FoundOutPoints: foundOutPoints, } } @@ -183,12 +164,8 @@ func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool { var isRelevant bool for _, addr := range addrs { addrStr := addr.EncodeAddress() - if scopedIndex, ok := bf.ExReverseFilter[addrStr]; ok { - bf.foundExternal(scopedIndex) - isRelevant = true - } - if scopedIndex, ok := bf.InReverseFilter[addrStr]; ok { - bf.foundInternal(scopedIndex) + if scopedIndex, ok := bf.ReverseFilter[addrStr]; ok { + bf.found(scopedIndex) isRelevant = true } } @@ -196,22 +173,9 @@ func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool { return isRelevant } -// foundExternal marks the scoped index as found within the block filterer's +// found marks the scoped index as found within the block filterer's // FoundExternal map. If this the first index found for a particular scope, the // scope's second layer map will be initialized before marking the index. -func (bf *BlockFilterer) foundExternal(scopedIndex waddrmgr.ScopedIndex) { - if _, ok := bf.FoundExternal[scopedIndex.Scope]; !ok { - bf.FoundExternal[scopedIndex.Scope] = make(map[uint32]struct{}) - } - bf.FoundExternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{} -} - -// foundInternal marks the scoped index as found within the block filterer's -// FoundInternal map. If this the first index found for a particular scope, the -// scope's second layer map will be initialized before marking the index. -func (bf *BlockFilterer) foundInternal(scopedIndex waddrmgr.ScopedIndex) { - if _, ok := bf.FoundInternal[scopedIndex.Scope]; !ok { - bf.FoundInternal[scopedIndex.Scope] = make(map[uint32]struct{}) - } - bf.FoundInternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{} +func (bf *BlockFilterer) found(scopedIndex waddrmgr.ScopedIndex) { + bf.FoundAddresses[scopedIndex] = struct{}{} } diff --git a/chain/interface.go b/chain/interface.go index ce1a2d9..d9d723a 100644 --- a/chain/interface.go +++ b/chain/interface.go @@ -75,8 +75,7 @@ type ( // is also included to monitor for spends. FilterBlocksRequest struct { Blocks []wtxmgr.BlockMeta - ExternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address - InternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address + Addresses map[waddrmgr.ScopedIndex]btcutil.Address WatchedOutPoints map[wire.OutPoint]btcutil.Address } @@ -88,12 +87,11 @@ type ( // caller can reinitiate a request for the subsequent block after // updating the addresses of interest. FilterBlocksResponse struct { - BatchIndex uint32 - BlockMeta wtxmgr.BlockMeta - FoundExternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{} - FoundInternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{} - FoundOutPoints map[wire.OutPoint]btcutil.Address - RelevantTxns []*wire.MsgTx + BatchIndex uint32 + BlockMeta wtxmgr.BlockMeta + FoundAddresses map[waddrmgr.ScopedIndex]struct{} + FoundOutPoints map[wire.OutPoint]btcutil.Address + RelevantTxns []*wire.MsgTx } // BlockDisconnected is a notifcation that the block described by the diff --git a/chain/neutrino.go b/chain/neutrino.go index 0e7332d..e368706 100644 --- a/chain/neutrino.go +++ b/chain/neutrino.go @@ -12,22 +12,12 @@ func buildFilterBlocksWatchList(req *FilterBlocksRequest) ([][]byte, error) { // Construct a watch list containing the script addresses of all // internal and external addresses that were requested, in addition to // the set of outpoints currently being watched. - watchListSize := len(req.ExternalAddrs) + - len(req.InternalAddrs) + + watchListSize := len(req.Addresses) + len(req.WatchedOutPoints) watchList := make([][]byte, 0, watchListSize) - for _, addr := range req.ExternalAddrs { - p2shAddr, err := txscript.PayToAddrScript(addr) - if err != nil { - return nil, err - } - - watchList = append(watchList, p2shAddr) - } - - for _, addr := range req.InternalAddrs { + for _, addr := range req.Addresses { p2shAddr, err := txscript.PayToAddrScript(addr) if err != nil { return nil, err diff --git a/chain/rpc.go b/chain/rpc.go index 2eb386b..74f2c35 100644 --- a/chain/rpc.go +++ b/chain/rpc.go @@ -270,12 +270,11 @@ func (c *RPCClient) FilterBlocks( // `BatchIndex` is returned so that the caller can compute the // *next* block from which to begin again. resp := &FilterBlocksResponse{ - BatchIndex: uint32(i), - BlockMeta: blk, - FoundExternalAddrs: blockFilterer.FoundExternal, - FoundInternalAddrs: blockFilterer.FoundInternal, - FoundOutPoints: blockFilterer.FoundOutPoints, - RelevantTxns: blockFilterer.RelevantTxns, + BatchIndex: uint32(i), + BlockMeta: blk, + FoundAddresses: blockFilterer.FoundAddresses, + FoundOutPoints: blockFilterer.FoundOutPoints, + RelevantTxns: blockFilterer.RelevantTxns, } return resp, nil diff --git a/waddrmgr/manager.go b/waddrmgr/manager.go index b6efbc0..d49efeb 100644 --- a/waddrmgr/manager.go +++ b/waddrmgr/manager.go @@ -156,13 +156,8 @@ type accountInfo struct { // The external branch is used for all addresses which are intended for // external use. - nextExternalIndex uint32 - lastExternalAddr ManagedAddress - - // The internal branch is used for all adddresses which are only - // intended for internal wallet use such as change addresses. - nextInternalIndex uint32 - lastInternalAddr ManagedAddress + nextIndex [2]uint32 + lastAddr [2]ManagedAddress // addrSchema serves as a way for an account to override its // corresponding address schema with a custom one. diff --git a/waddrmgr/manager_test.go b/waddrmgr/manager_test.go index 73b8881..6c08337 100644 --- a/waddrmgr/manager_test.go +++ b/waddrmgr/manager_test.go @@ -334,8 +334,8 @@ func testExternalAddresses(tc *testContext) bool { err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) var err error - addrs, err = tc.manager.NextExternalAddresses( - ns, tc.internalAccount, 5, + addrs, err = tc.manager.NextAddresses( + ns, tc.internalAccount, ExternalBranch, 5, ) return err }) @@ -371,8 +371,8 @@ func testExternalAddresses(tc *testContext) bool { err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error { ns := tx.ReadBucket(waddrmgrNamespaceKey) var err error - lastAddr, err = tc.manager.LastExternalAddress( - ns, tc.internalAccount, + lastAddr, err = tc.manager.LastAddress( + ns, tc.internalAccount, ExternalBranch, ) return err }) @@ -478,8 +478,8 @@ func testInternalAddresses(tc *testContext) bool { err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) var err error - addrs, err = tc.manager.NextInternalAddresses( - ns, tc.internalAccount, 5, + addrs, err = tc.manager.NextAddresses( + ns, tc.internalAccount, InternalBranch, 5, ) return err }) @@ -515,8 +515,8 @@ func testInternalAddresses(tc *testContext) bool { err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error { ns := tx.ReadBucket(waddrmgrNamespaceKey) var err error - lastAddr, err = tc.manager.LastInternalAddress( - ns, tc.internalAccount, + lastAddr, err = tc.manager.LastAddress( + ns, tc.internalAccount, InternalBranch, ) return err }) @@ -2032,8 +2032,8 @@ func TestScopedKeyManagerManagement(t *testing.T) { t.Fatalf("unable to fetch scope %v: %v", scope, err) } - externalAddr, err := sMgr.NextExternalAddresses( - ns, DefaultAccountNum, 1, + externalAddr, err := sMgr.NextAddresses( + ns, DefaultAccountNum, ExternalBranch, 1, ) if err != nil { t.Fatalf("unable to derive external addr: %v", err) @@ -2047,8 +2047,8 @@ func TestScopedKeyManagerManagement(t *testing.T) { ScopeAddrMap[scope].ExternalAddrType) } - internalAddr, err := sMgr.NextInternalAddresses( - ns, DefaultAccountNum, 1, + internalAddr, err := sMgr.NextAddresses( + ns, DefaultAccountNum, InternalBranch, 1, ) if err != nil { t.Fatalf("unable to derive internal addr: %v", err) @@ -2106,15 +2106,15 @@ func TestScopedKeyManagerManagement(t *testing.T) { // We'll now create a new external address to ensure we // retrieve the proper type. - externalAddr, err = scopedMgr.NextExternalAddresses( - ns, DefaultAccountNum, 1, + externalAddr, err = scopedMgr.NextAddresses( + ns, DefaultAccountNum, ExternalBranch, 1, ) if err != nil { t.Fatalf("unable to derive external addr: %v", err) } - internalAddr, err = scopedMgr.NextInternalAddresses( - ns, DefaultAccountNum, 1, + internalAddr, err = scopedMgr.NextAddresses( + ns, DefaultAccountNum, InternalBranch, 1, ) if err != nil { t.Fatalf("unable to derive internal addr: %v", err) @@ -2177,8 +2177,8 @@ func TestScopedKeyManagerManagement(t *testing.T) { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) - lastAddr, err = scopedMgr.LastExternalAddress( - ns, DefaultAccountNum, + lastAddr, err = scopedMgr.LastAddress( + ns, DefaultAccountNum, ExternalBranch, ) if err != nil { return err @@ -2384,8 +2384,8 @@ func testNewRawAccount(t *testing.T, _ *Manager, db walletdb.DB, err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) - addrs, err := scopedMgr.NextExternalAddresses( - ns, accountNum, 1, + addrs, err := scopedMgr.NextAddresses( + ns, accountNum, ExternalBranch, 1, ) if err != nil { return err diff --git a/waddrmgr/scoped_manager.go b/waddrmgr/scoped_manager.go index 2db3aee..cc243d5 100644 --- a/waddrmgr/scoped_manager.go +++ b/waddrmgr/scoped_manager.go @@ -401,11 +401,13 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket, switch row := rowInterface.(type) { case *dbDefaultAccountRow: acctInfo = &accountInfo{ - acctName: row.name, - acctType: row.acctType, - acctKeyEncrypted: row.privKeyEncrypted, - nextExternalIndex: row.nextExternalIndex, - nextInternalIndex: row.nextInternalIndex, + acctName: row.name, + acctType: row.acctType, + acctKeyEncrypted: row.privKeyEncrypted, + nextIndex: [2]uint32{ + row.nextExternalIndex, + row.nextInternalIndex, + }, } // Use the crypto public key to decrypt the account public @@ -437,49 +439,30 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket, return nil, managerError(ErrDatabase, str, nil) } - // Derive and cache the managed address for the last external address. - branch, index := ExternalBranch, acctInfo.nextExternalIndex - if index > 0 { - index-- - } - lastExtAddrPath := DerivationPath{ - InternalAccount: account, - Account: acctInfo.acctKeyPub.ChildIndex(), - Branch: branch, - Index: index, - MasterKeyFingerprint: acctInfo.masterKeyFingerprint, - } - lastExtKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey) - if err != nil { - return nil, err - } - lastExtAddr, err := s.keyToManaged(lastExtKey, lastExtAddrPath, acctInfo) - if err != nil { - return nil, err - } - acctInfo.lastExternalAddr = lastExtAddr + for branch := 0; branch < 2; branch++ { - // Derive and cache the managed address for the last internal address. - branch, index = InternalBranch, acctInfo.nextInternalIndex - if index > 0 { - index-- + // Derive and cache the managed address for the last external address. + index := acctInfo.nextIndex[branch] + if index > 0 { + index-- + } + lastAddrPath := DerivationPath{ + InternalAccount: account, + Account: acctInfo.acctKeyPub.ChildIndex(), + Branch: uint32(branch), + Index: index, + MasterKeyFingerprint: acctInfo.masterKeyFingerprint, + } + lastKey, err := s.deriveKey(acctInfo, uint32(branch), index, hasPrivateKey) + if err != nil { + return nil, err + } + lastAddr, err := s.keyToManaged(lastKey, lastAddrPath, acctInfo) + if err != nil { + return nil, err + } + acctInfo.lastAddr[branch] = lastAddr } - lastIntAddrPath := DerivationPath{ - InternalAccount: account, - Account: acctInfo.acctKeyPub.ChildIndex(), - Branch: branch, - Index: index, - MasterKeyFingerprint: acctInfo.masterKeyFingerprint, - } - lastIntKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey) - if err != nil { - return nil, err - } - lastIntAddr, err := s.keyToManaged(lastIntKey, lastIntAddrPath, acctInfo) - if err != nil { - return nil, err - } - acctInfo.lastInternalAddr = lastIntAddr // Add it to the cache and return it when everything is successful. s.acctInfo[account] = acctInfo @@ -516,8 +499,8 @@ func (s *ScopedKeyManager) AccountProperties(ns walletdb.ReadBucket, return nil, err } props.AccountName = acctInfo.acctName - props.ExternalKeyCount = acctInfo.nextExternalIndex - props.InternalKeyCount = acctInfo.nextInternalIndex + props.ExternalKeyCount = acctInfo.nextIndex[ExternalBranch] + props.InternalKeyCount = acctInfo.nextIndex[InternalBranch] props.AccountPubKey = acctInfo.acctKeyPub props.MasterKeyFingerprint = acctInfo.masterKeyFingerprint props.AddrSchema = acctInfo.addrSchema @@ -938,7 +921,7 @@ func (s *ScopedKeyManager) accountAddrType(acctInfo *accountInfo, // // This function MUST be called with the manager lock held for writes. func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, - account uint32, numAddresses uint32, internal bool) ([]ManagedAddress, error) { + account uint32, branch uint32, numAddresses uint32) ([]ManagedAddress, error) { // The next address can only be generated for accounts that have // already been created. @@ -956,16 +939,12 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, // Choose the branch key and index depending on whether or not this is // an internal address. - branchNum, nextIndex := ExternalBranch, acctInfo.nextExternalIndex - if internal { - branchNum = InternalBranch - nextIndex = acctInfo.nextInternalIndex - } + nextIndex := acctInfo.nextIndex[branch] // Choose the appropriate type of address to derive since it's possible // for a watch-only account to have a different schema from the // manager's. - addrType := s.accountAddrType(acctInfo, internal) + addrType := s.accountAddrType(acctInfo, branch == InternalBranch) // Ensure the requested number of addresses doesn't exceed the maximum // allowed for this account. @@ -978,10 +957,9 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, } // Derive the appropriate branch key and ensure it is zeroed when done. - branchKey, err := acctKey.Derive(branchNum) + branchKey, err := acctKey.Derive(branch) if err != nil { - str := fmt.Sprintf("failed to derive extended key branch %d", - branchNum) + str := fmt.Sprintf("failed to derive extended key branch %d", branch) return nil, managerError(ErrKeyChain, str, err) } defer branchKey.Zero() // Ensure branch key is zeroed when done. @@ -1021,7 +999,7 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, derivationPath := DerivationPath{ InternalAccount: account, Account: acctKey.ChildIndex(), - Branch: branchNum, + Branch: branch, Index: nextIndex - 1, } @@ -1035,15 +1013,13 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, if err != nil { return nil, err } - if internal { - addr.internal = true - } + addr.internal = branch == InternalBranch managedAddr := addr nextKey.Zero() info := unlockDeriveInfo{ managedAddr: managedAddr, - branch: branchNum, + branch: branch, index: nextIndex - 1, } addressInfo = append(addressInfo, &info) @@ -1113,13 +1089,8 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, // Set the last address and next address for tracking. ma := addressInfo[len(addressInfo)-1].managedAddr - if internal { - acctInfo.nextInternalIndex = nextIndex - acctInfo.lastInternalAddr = ma - } else { - acctInfo.nextExternalIndex = nextIndex - acctInfo.lastExternalAddr = ma - } + acctInfo.nextIndex[branch] = nextIndex + acctInfo.lastAddr[branch] = ma } ns.Tx().OnCommit(onCommit) @@ -1152,16 +1123,12 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket, // Choose the branch key and index depending on whether or not this is // an internal address. - branchNum, nextIndex := ExternalBranch, acctInfo.nextExternalIndex - if internal { - branchNum = InternalBranch - nextIndex = acctInfo.nextInternalIndex - } + nextIndex := acctInfo.nextIndex[branch] // Choose the appropriate type of address to derive since it's possible // for a watch-only account to have a different schema from the // manager's. - addrType := s.accountAddrType(acctInfo, internal) + addrType := s.accountAddrType(acctInfo, branch == InternalBranch) // If the last index requested is already lower than the next index, we // can return early. @@ -1179,10 +1146,10 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket, } // Derive the appropriate branch key and ensure it is zeroed when done. - branchKey, err := acctKey.Derive(branchNum) + branchKey, err := acctKey.Derive(branch) if err != nil { str := fmt.Sprintf("failed to derive extended key branch %d", - branchNum) + branch) return managerError(ErrKeyChain, str, err) } defer branchKey.Zero() // Ensure branch key is zeroed when done. @@ -1224,7 +1191,7 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket, derivationPath := DerivationPath{ InternalAccount: account, Account: acctInfo.acctKeyPub.ChildIndex(), - Branch: branchNum, + Branch: branch, Index: nextIndex - 1, } @@ -1238,15 +1205,13 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket, if err != nil { return err } - if internal { - addr.internal = true - } + addr.internal = branch == InternalBranch managedAddr := addr nextKey.Zero() info := unlockDeriveInfo{ managedAddr: managedAddr, - branch: branchNum, + branch: branch, index: nextIndex - 1, } addressInfo = append(addressInfo, &info) @@ -1302,21 +1267,16 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket, // Set the last address and next address for tracking. ma := addressInfo[len(addressInfo)-1].managedAddr - if internal { - acctInfo.nextInternalIndex = nextIndex - acctInfo.lastInternalAddr = ma - } else { - acctInfo.nextExternalIndex = nextIndex - acctInfo.lastExternalAddr = ma - } + acctInfo.nextIndex[branch] = nextIndex + acctInfo.lastAddr[branch] = ma return nil } -// NextExternalAddresses returns the specified number of next chained addresses +// NextAddresses returns the specified number of next chained addresses // that are intended for external use from the address manager. -func (s *ScopedKeyManager) NextExternalAddresses(ns walletdb.ReadWriteBucket, - account uint32, numAddresses uint32) ([]ManagedAddress, error) { +func (s *ScopedKeyManager) NextAddresses(ns walletdb.ReadWriteBucket, + account uint32, branch uint32, numAddresses uint32) ([]ManagedAddress, error) { // Enforce maximum account number. if account > MaxAccountNum { @@ -1327,32 +1287,15 @@ func (s *ScopedKeyManager) NextExternalAddresses(ns walletdb.ReadWriteBucket, s.mtx.Lock() defer s.mtx.Unlock() - return s.nextAddresses(ns, account, numAddresses, false) -} - -// NextInternalAddresses returns the specified number of next chained addresses -// that are intended for internal use such as change from the address manager. -func (s *ScopedKeyManager) NextInternalAddresses(ns walletdb.ReadWriteBucket, - account uint32, numAddresses uint32) ([]ManagedAddress, error) { - - // Enforce maximum account number. - if account > MaxAccountNum { - err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil) - return nil, err - } - - s.mtx.Lock() - defer s.mtx.Unlock() - - return s.nextAddresses(ns, account, numAddresses, true) + return s.nextAddresses(ns, account, branch, numAddresses) } // ExtendExternalAddresses ensures that all valid external keys through // lastIndex are derived and stored in the wallet. This is used to ensure that // wallet's persistent state catches up to a external child that was found // during recovery. -func (s *ScopedKeyManager) ExtendExternalAddresses(ns walletdb.ReadWriteBucket, - account uint32, lastIndex uint32) error { +func (s *ScopedKeyManager) ExtendAddresses(ns walletdb.ReadWriteBucket, + account uint32, branch uint32, lastIndex uint32) error { if account > MaxAccountNum { err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil) @@ -1362,28 +1305,10 @@ func (s *ScopedKeyManager) ExtendExternalAddresses(ns walletdb.ReadWriteBucket, s.mtx.Lock() defer s.mtx.Unlock() - return s.extendAddresses(ns, account, lastIndex, false) + return s.extendAddresses(ns, account, branch, lastIndex) } -// ExtendInternalAddresses ensures that all valid internal keys through -// lastIndex are derived and stored in the wallet. This is used to ensure that -// wallet's persistent state catches up to an internal child that was found -// during recovery. -func (s *ScopedKeyManager) ExtendInternalAddresses(ns walletdb.ReadWriteBucket, - account uint32, lastIndex uint32) error { - - if account > MaxAccountNum { - err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil) - return err - } - - s.mtx.Lock() - defer s.mtx.Unlock() - - return s.extendAddresses(ns, account, lastIndex, true) -} - -// LastExternalAddress returns the most recently requested chained external +// LastAddress returns the most recently requested chained external // address from calling NextExternalAddress for the given account. The first // external address for the account will be returned if none have been // previously requested. @@ -1391,8 +1316,8 @@ func (s *ScopedKeyManager) ExtendInternalAddresses(ns walletdb.ReadWriteBucket, // This function will return an error if the provided account number is greater // than the MaxAccountNum constant or there is no account information for the // passed account. Any other errors returned are generally unexpected. -func (s *ScopedKeyManager) LastExternalAddress(ns walletdb.ReadBucket, - account uint32) (ManagedAddress, error) { +func (s *ScopedKeyManager) LastAddress(ns walletdb.ReadBucket, + account, branch uint32) (ManagedAddress, error) { // Enforce maximum account number. if account > MaxAccountNum { @@ -1410,47 +1335,13 @@ func (s *ScopedKeyManager) LastExternalAddress(ns walletdb.ReadBucket, return nil, err } - if acctInfo.nextExternalIndex > 0 { - return acctInfo.lastExternalAddr, nil + if acctInfo.nextIndex[branch] > 0 { + return acctInfo.lastAddr[branch], nil } return nil, managerError(ErrAddressNotFound, "no previous external address", nil) } -// LastInternalAddress returns the most recently requested chained internal -// address from calling NextInternalAddress for the given account. The first -// internal address for the account will be returned if none have been -// previously requested. -// -// This function will return an error if the provided account number is greater -// than the MaxAccountNum constant or there is no account information for the -// passed account. Any other errors returned are generally unexpected. -func (s *ScopedKeyManager) LastInternalAddress(ns walletdb.ReadBucket, - account uint32) (ManagedAddress, error) { - - // Enforce maximum account number. - if account > MaxAccountNum { - err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil) - return nil, err - } - - s.mtx.Lock() - defer s.mtx.Unlock() - - // Load account information for the passed account. It is typically - // cached, but if not it will be loaded from the database. - acctInfo, err := s.loadAccountInfo(ns, account) - if err != nil { - return nil, err - } - - if acctInfo.nextInternalIndex > 0 { - return acctInfo.lastInternalAddr, nil - } - - return nil, managerError(ErrAddressNotFound, "no previous internal address", nil) -} - // NewRawAccount creates a new account for the scoped manager. This method // differs from the NewAccount method in that this method takes the account // number *directly*, rather than taking a string name for the account, then diff --git a/wallet/import.go b/wallet/import.go index 7ba8991..40465ba 100644 --- a/wallet/import.go +++ b/wallet/import.go @@ -326,14 +326,14 @@ func (w *Wallet) ImportAccountDryRun(name string, // attempt, we'll want to invalidate the cache for it. defer manager.InvalidateAccountCache(accountProps.AccountNumber) - externalAddrs, err = manager.NextExternalAddresses( - ns, accountProps.AccountNumber, numAddrs, + externalAddrs, err = manager.NextAddresses( + ns, accountProps.AccountNumber, waddrmgr.ExternalBranch, numAddrs, ) if err != nil { return err } - internalAddrs, err = manager.NextInternalAddresses( - ns, accountProps.AccountNumber, numAddrs, + internalAddrs, err = manager.NextAddresses( + ns, accountProps.AccountNumber, waddrmgr.InternalBranch, numAddrs, ) if err != nil { return err diff --git a/wallet/recovery.go b/wallet/recovery.go index 41ed9b5..0f885f3 100644 --- a/wallet/recovery.go +++ b/wallet/recovery.go @@ -261,11 +261,7 @@ func (rs *RecoveryState) AddWatchedOutPoint(outPoint *wire.OutPoint, type ScopeRecoveryState struct { // ExternalBranch is the recovery state of addresses generated for // external use, i.e. receiving addresses. - ExternalBranch *BranchRecoveryState - - // InternalBranch is the recovery state of addresses generated for - // internal use, i.e. change addresses. - InternalBranch *BranchRecoveryState + AccountBranches [][2]*BranchRecoveryState } // NewScopeRecoveryState initializes an ScopeRecoveryState with the chosen diff --git a/wallet/wallet.go b/wallet/wallet.go index 732a61e..aa240f7 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -954,22 +954,12 @@ func expandScopeHorizons(ns walletdb.ReadWriteBucket, return nil } -// externalKeyPath returns the relative external derivation path /0/0/index. -func externalKeyPath(index uint32) waddrmgr.DerivationPath { +// keyPath returns the relative external derivation path /account/branch/index. +func keyPath(account, branch, index uint32) waddrmgr.DerivationPath { return waddrmgr.DerivationPath{ - InternalAccount: waddrmgr.DefaultAccountNum, - Account: waddrmgr.DefaultAccountNum, - Branch: waddrmgr.ExternalBranch, - Index: index, - } -} - -// internalKeyPath returns the relative internal derivation path /0/1/index. -func internalKeyPath(index uint32) waddrmgr.DerivationPath { - return waddrmgr.DerivationPath{ - InternalAccount: waddrmgr.DefaultAccountNum, - Account: waddrmgr.DefaultAccountNum, - Branch: waddrmgr.InternalBranch, + InternalAccount: account, + Account: account, + Branch: branch, Index: index, } } @@ -982,8 +972,7 @@ func newFilterBlocksRequest(batch []wtxmgr.BlockMeta, filterReq := &chain.FilterBlocksRequest{ Blocks: batch, - ExternalAddrs: make(map[waddrmgr.ScopedIndex]btcutil.Address), - InternalAddrs: make(map[waddrmgr.ScopedIndex]btcutil.Address), + Addresses: make(map[waddrmgr.ScopedIndex]btcutil.Address), WatchedOutPoints: recoveryState.WatchedOutPoints(), } @@ -1112,23 +1101,12 @@ func logFilterBlocksResp(block wtxmgr.BlockMeta, resp *chain.FilterBlocksResponse) { // Log the number of external addresses found in this block. - var nFoundExternal int - for _, indexes := range resp.FoundExternalAddrs { - nFoundExternal += len(indexes) - } - if nFoundExternal > 0 { - log.Infof("Recovered %d external addrs at height=%d hash=%v", - nFoundExternal, block.Height, block.Hash) - } + var nFoundAddresses int + nFoundAddresses += len(resp.FoundAddresses) - // Log the number of internal addresses found in this block. - var nFoundInternal int - for _, indexes := range resp.FoundInternalAddrs { - nFoundInternal += len(indexes) - } - if nFoundInternal > 0 { - log.Infof("Recovered %d internal addrs at height=%d hash=%v", - nFoundInternal, block.Height, block.Hash) + if nFoundAddresses > 0 { + log.Infof("Recovered %d addrs at height=%d hash=%v", + nFoundAddresses, block.Height, block.Hash) } // Log the number of outpoints found in this block. @@ -1568,7 +1546,7 @@ func (w *Wallet) CurrentAddress(account uint32, scope waddrmgr.KeyScope) (btcuti ) err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) - maddr, err := manager.LastExternalAddress(addrmgrNs, account) + maddr, err := manager.LastAddress(addrmgrNs, account, waddrmgr.ExternalBranch) if err != nil { // If no address exists yet, create the first external // address. @@ -2954,7 +2932,7 @@ func (w *Wallet) newAddress(addrmgrNs walletdb.ReadWriteBucket, account uint32, } // Get next address from wallet. - addrs, err := manager.NextExternalAddresses(addrmgrNs, account, 1) + addrs, err := manager.NextAddresses(addrmgrNs, account, waddrmgr.ExternalBranch, 1) if err != nil { return nil, nil, err } @@ -3012,7 +2990,7 @@ func (w *Wallet) newChangeAddress(addrmgrNs walletdb.ReadWriteBucket, } // Get next chained change address from wallet for account. - addrs, err := manager.NextInternalAddresses(addrmgrNs, account, 1) + addrs, err := manager.NextAddresses(addrmgrNs, account, waddrmgr.InternalBranch, 1) if err != nil { return nil, err }