consolidate: merge internal/external branches

This commit is contained in:
Roy Lee 2022-09-16 09:13:09 -07:00
parent de408d4133
commit 0410b7ce01
10 changed files with 132 additions and 321 deletions

View file

@ -31,26 +31,18 @@ type BlockFilterer struct {
// Params specifies the chain params of the current network. // Params specifies the chain params of the current network.
Params *chaincfg.Params 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. // the scoped index from which it was derived.
ExReverseFilter map[string]waddrmgr.ScopedIndex ReverseFilter 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
// WathcedOutPoints is a global set of outpoints being tracked by the // WathcedOutPoints is a global set of outpoints being tracked by the
// wallet. This allows the block filterer to check for spends from an // wallet. This allows the block filterer to check for spends from an
// outpoint we own. // outpoint we own.
WatchedOutPoints map[wire.OutPoint]btcutil.Address 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. // external addresses found in a single block.
FoundExternal map[waddrmgr.KeyScope]map[uint32]struct{} FoundAddresses map[waddrmgr.ScopedIndex]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{}
// FoundOutPoints is a set of outpoints found in a single block whose // FoundOutPoints is a set of outpoints found in a single block whose
// address belongs to the wallet. // address belongs to the wallet.
@ -71,31 +63,20 @@ func NewBlockFilterer(params *chaincfg.Params,
// Construct a reverse index by address string for the requested // Construct a reverse index by address string for the requested
// external addresses. // external addresses.
nExAddrs := len(req.ExternalAddrs) nAddrs := len(req.Addresses)
exReverseFilter := make(map[string]waddrmgr.ScopedIndex, nExAddrs) reverseFilter := make(map[string]waddrmgr.ScopedIndex, nAddrs)
for scopedIndex, addr := range req.ExternalAddrs { for scopedIndex, addr := range req.Addresses {
exReverseFilter[addr.EncodeAddress()] = scopedIndex reverseFilter[addr.EncodeAddress()] = scopedIndex
} }
// Construct a reverse index by address string for the requested foundAddresses := make(map[waddrmgr.ScopedIndex]struct{})
// 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{})
foundOutPoints := make(map[wire.OutPoint]btcutil.Address) foundOutPoints := make(map[wire.OutPoint]btcutil.Address)
return &BlockFilterer{ return &BlockFilterer{
Params: params, Params: params,
ExReverseFilter: exReverseFilter, ReverseFilter: reverseFilter,
InReverseFilter: inReverseFilter,
WatchedOutPoints: req.WatchedOutPoints, WatchedOutPoints: req.WatchedOutPoints,
FoundExternal: foundExternal, FoundAddresses: foundAddresses,
FoundInternal: foundInternal,
FoundOutPoints: foundOutPoints, FoundOutPoints: foundOutPoints,
} }
} }
@ -183,12 +164,8 @@ func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool {
var isRelevant bool var isRelevant bool
for _, addr := range addrs { for _, addr := range addrs {
addrStr := addr.EncodeAddress() addrStr := addr.EncodeAddress()
if scopedIndex, ok := bf.ExReverseFilter[addrStr]; ok { if scopedIndex, ok := bf.ReverseFilter[addrStr]; ok {
bf.foundExternal(scopedIndex) bf.found(scopedIndex)
isRelevant = true
}
if scopedIndex, ok := bf.InReverseFilter[addrStr]; ok {
bf.foundInternal(scopedIndex)
isRelevant = true isRelevant = true
} }
} }
@ -196,22 +173,9 @@ func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool {
return isRelevant 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 // 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. // scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundExternal(scopedIndex waddrmgr.ScopedIndex) { func (bf *BlockFilterer) found(scopedIndex waddrmgr.ScopedIndex) {
if _, ok := bf.FoundExternal[scopedIndex.Scope]; !ok { bf.FoundAddresses[scopedIndex] = struct{}{}
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{}{}
} }

View file

@ -75,8 +75,7 @@ type (
// is also included to monitor for spends. // is also included to monitor for spends.
FilterBlocksRequest struct { FilterBlocksRequest struct {
Blocks []wtxmgr.BlockMeta Blocks []wtxmgr.BlockMeta
ExternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address Addresses map[waddrmgr.ScopedIndex]btcutil.Address
InternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address
WatchedOutPoints map[wire.OutPoint]btcutil.Address WatchedOutPoints map[wire.OutPoint]btcutil.Address
} }
@ -88,12 +87,11 @@ type (
// caller can reinitiate a request for the subsequent block after // caller can reinitiate a request for the subsequent block after
// updating the addresses of interest. // updating the addresses of interest.
FilterBlocksResponse struct { FilterBlocksResponse struct {
BatchIndex uint32 BatchIndex uint32
BlockMeta wtxmgr.BlockMeta BlockMeta wtxmgr.BlockMeta
FoundExternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{} FoundAddresses map[waddrmgr.ScopedIndex]struct{}
FoundInternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{} FoundOutPoints map[wire.OutPoint]btcutil.Address
FoundOutPoints map[wire.OutPoint]btcutil.Address RelevantTxns []*wire.MsgTx
RelevantTxns []*wire.MsgTx
} }
// BlockDisconnected is a notifcation that the block described by the // BlockDisconnected is a notifcation that the block described by the

View file

@ -12,22 +12,12 @@ func buildFilterBlocksWatchList(req *FilterBlocksRequest) ([][]byte, error) {
// Construct a watch list containing the script addresses of all // Construct a watch list containing the script addresses of all
// internal and external addresses that were requested, in addition to // internal and external addresses that were requested, in addition to
// the set of outpoints currently being watched. // the set of outpoints currently being watched.
watchListSize := len(req.ExternalAddrs) + watchListSize := len(req.Addresses) +
len(req.InternalAddrs) +
len(req.WatchedOutPoints) len(req.WatchedOutPoints)
watchList := make([][]byte, 0, watchListSize) watchList := make([][]byte, 0, watchListSize)
for _, addr := range req.ExternalAddrs { for _, addr := range req.Addresses {
p2shAddr, err := txscript.PayToAddrScript(addr)
if err != nil {
return nil, err
}
watchList = append(watchList, p2shAddr)
}
for _, addr := range req.InternalAddrs {
p2shAddr, err := txscript.PayToAddrScript(addr) p2shAddr, err := txscript.PayToAddrScript(addr)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -270,12 +270,11 @@ func (c *RPCClient) FilterBlocks(
// `BatchIndex` is returned so that the caller can compute the // `BatchIndex` is returned so that the caller can compute the
// *next* block from which to begin again. // *next* block from which to begin again.
resp := &FilterBlocksResponse{ resp := &FilterBlocksResponse{
BatchIndex: uint32(i), BatchIndex: uint32(i),
BlockMeta: blk, BlockMeta: blk,
FoundExternalAddrs: blockFilterer.FoundExternal, FoundAddresses: blockFilterer.FoundAddresses,
FoundInternalAddrs: blockFilterer.FoundInternal, FoundOutPoints: blockFilterer.FoundOutPoints,
FoundOutPoints: blockFilterer.FoundOutPoints, RelevantTxns: blockFilterer.RelevantTxns,
RelevantTxns: blockFilterer.RelevantTxns,
} }
return resp, nil return resp, nil

View file

@ -156,13 +156,8 @@ type accountInfo struct {
// The external branch is used for all addresses which are intended for // The external branch is used for all addresses which are intended for
// external use. // external use.
nextExternalIndex uint32 nextIndex [2]uint32
lastExternalAddr ManagedAddress lastAddr [2]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
// addrSchema serves as a way for an account to override its // addrSchema serves as a way for an account to override its
// corresponding address schema with a custom one. // corresponding address schema with a custom one.

View file

@ -334,8 +334,8 @@ func testExternalAddresses(tc *testContext) bool {
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error { err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
var err error var err error
addrs, err = tc.manager.NextExternalAddresses( addrs, err = tc.manager.NextAddresses(
ns, tc.internalAccount, 5, ns, tc.internalAccount, ExternalBranch, 5,
) )
return err return err
}) })
@ -371,8 +371,8 @@ func testExternalAddresses(tc *testContext) bool {
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error { err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey) ns := tx.ReadBucket(waddrmgrNamespaceKey)
var err error var err error
lastAddr, err = tc.manager.LastExternalAddress( lastAddr, err = tc.manager.LastAddress(
ns, tc.internalAccount, ns, tc.internalAccount, ExternalBranch,
) )
return err return err
}) })
@ -478,8 +478,8 @@ func testInternalAddresses(tc *testContext) bool {
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error { err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
var err error var err error
addrs, err = tc.manager.NextInternalAddresses( addrs, err = tc.manager.NextAddresses(
ns, tc.internalAccount, 5, ns, tc.internalAccount, InternalBranch, 5,
) )
return err return err
}) })
@ -515,8 +515,8 @@ func testInternalAddresses(tc *testContext) bool {
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error { err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey) ns := tx.ReadBucket(waddrmgrNamespaceKey)
var err error var err error
lastAddr, err = tc.manager.LastInternalAddress( lastAddr, err = tc.manager.LastAddress(
ns, tc.internalAccount, ns, tc.internalAccount, InternalBranch,
) )
return err return err
}) })
@ -2032,8 +2032,8 @@ func TestScopedKeyManagerManagement(t *testing.T) {
t.Fatalf("unable to fetch scope %v: %v", scope, err) t.Fatalf("unable to fetch scope %v: %v", scope, err)
} }
externalAddr, err := sMgr.NextExternalAddresses( externalAddr, err := sMgr.NextAddresses(
ns, DefaultAccountNum, 1, ns, DefaultAccountNum, ExternalBranch, 1,
) )
if err != nil { if err != nil {
t.Fatalf("unable to derive external addr: %v", err) t.Fatalf("unable to derive external addr: %v", err)
@ -2047,8 +2047,8 @@ func TestScopedKeyManagerManagement(t *testing.T) {
ScopeAddrMap[scope].ExternalAddrType) ScopeAddrMap[scope].ExternalAddrType)
} }
internalAddr, err := sMgr.NextInternalAddresses( internalAddr, err := sMgr.NextAddresses(
ns, DefaultAccountNum, 1, ns, DefaultAccountNum, InternalBranch, 1,
) )
if err != nil { if err != nil {
t.Fatalf("unable to derive internal addr: %v", err) 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 // We'll now create a new external address to ensure we
// retrieve the proper type. // retrieve the proper type.
externalAddr, err = scopedMgr.NextExternalAddresses( externalAddr, err = scopedMgr.NextAddresses(
ns, DefaultAccountNum, 1, ns, DefaultAccountNum, ExternalBranch, 1,
) )
if err != nil { if err != nil {
t.Fatalf("unable to derive external addr: %v", err) t.Fatalf("unable to derive external addr: %v", err)
} }
internalAddr, err = scopedMgr.NextInternalAddresses( internalAddr, err = scopedMgr.NextAddresses(
ns, DefaultAccountNum, 1, ns, DefaultAccountNum, InternalBranch, 1,
) )
if err != nil { if err != nil {
t.Fatalf("unable to derive internal addr: %v", err) 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 { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
lastAddr, err = scopedMgr.LastExternalAddress( lastAddr, err = scopedMgr.LastAddress(
ns, DefaultAccountNum, ns, DefaultAccountNum, ExternalBranch,
) )
if err != nil { if err != nil {
return err return err
@ -2384,8 +2384,8 @@ func testNewRawAccount(t *testing.T, _ *Manager, db walletdb.DB,
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
addrs, err := scopedMgr.NextExternalAddresses( addrs, err := scopedMgr.NextAddresses(
ns, accountNum, 1, ns, accountNum, ExternalBranch, 1,
) )
if err != nil { if err != nil {
return err return err

View file

@ -401,11 +401,13 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket,
switch row := rowInterface.(type) { switch row := rowInterface.(type) {
case *dbDefaultAccountRow: case *dbDefaultAccountRow:
acctInfo = &accountInfo{ acctInfo = &accountInfo{
acctName: row.name, acctName: row.name,
acctType: row.acctType, acctType: row.acctType,
acctKeyEncrypted: row.privKeyEncrypted, acctKeyEncrypted: row.privKeyEncrypted,
nextExternalIndex: row.nextExternalIndex, nextIndex: [2]uint32{
nextInternalIndex: row.nextInternalIndex, row.nextExternalIndex,
row.nextInternalIndex,
},
} }
// Use the crypto public key to decrypt the account public // 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) return nil, managerError(ErrDatabase, str, nil)
} }
// Derive and cache the managed address for the last external address. for branch := 0; branch < 2; branch++ {
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
// Derive and cache the managed address for the last internal address. // Derive and cache the managed address for the last external address.
branch, index = InternalBranch, acctInfo.nextInternalIndex index := acctInfo.nextIndex[branch]
if index > 0 { if index > 0 {
index-- 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. // Add it to the cache and return it when everything is successful.
s.acctInfo[account] = acctInfo s.acctInfo[account] = acctInfo
@ -516,8 +499,8 @@ func (s *ScopedKeyManager) AccountProperties(ns walletdb.ReadBucket,
return nil, err return nil, err
} }
props.AccountName = acctInfo.acctName props.AccountName = acctInfo.acctName
props.ExternalKeyCount = acctInfo.nextExternalIndex props.ExternalKeyCount = acctInfo.nextIndex[ExternalBranch]
props.InternalKeyCount = acctInfo.nextInternalIndex props.InternalKeyCount = acctInfo.nextIndex[InternalBranch]
props.AccountPubKey = acctInfo.acctKeyPub props.AccountPubKey = acctInfo.acctKeyPub
props.MasterKeyFingerprint = acctInfo.masterKeyFingerprint props.MasterKeyFingerprint = acctInfo.masterKeyFingerprint
props.AddrSchema = acctInfo.addrSchema 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. // This function MUST be called with the manager lock held for writes.
func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, 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 // The next address can only be generated for accounts that have
// already been created. // 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 // Choose the branch key and index depending on whether or not this is
// an internal address. // an internal address.
branchNum, nextIndex := ExternalBranch, acctInfo.nextExternalIndex nextIndex := acctInfo.nextIndex[branch]
if internal {
branchNum = InternalBranch
nextIndex = acctInfo.nextInternalIndex
}
// Choose the appropriate type of address to derive since it's possible // Choose the appropriate type of address to derive since it's possible
// for a watch-only account to have a different schema from the // for a watch-only account to have a different schema from the
// manager's. // manager's.
addrType := s.accountAddrType(acctInfo, internal) addrType := s.accountAddrType(acctInfo, branch == InternalBranch)
// Ensure the requested number of addresses doesn't exceed the maximum // Ensure the requested number of addresses doesn't exceed the maximum
// allowed for this account. // 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. // 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 { if err != nil {
str := fmt.Sprintf("failed to derive extended key branch %d", str := fmt.Sprintf("failed to derive extended key branch %d", branch)
branchNum)
return nil, managerError(ErrKeyChain, str, err) return nil, managerError(ErrKeyChain, str, err)
} }
defer branchKey.Zero() // Ensure branch key is zeroed when done. defer branchKey.Zero() // Ensure branch key is zeroed when done.
@ -1021,7 +999,7 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
derivationPath := DerivationPath{ derivationPath := DerivationPath{
InternalAccount: account, InternalAccount: account,
Account: acctKey.ChildIndex(), Account: acctKey.ChildIndex(),
Branch: branchNum, Branch: branch,
Index: nextIndex - 1, Index: nextIndex - 1,
} }
@ -1035,15 +1013,13 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
if err != nil { if err != nil {
return nil, err return nil, err
} }
if internal { addr.internal = branch == InternalBranch
addr.internal = true
}
managedAddr := addr managedAddr := addr
nextKey.Zero() nextKey.Zero()
info := unlockDeriveInfo{ info := unlockDeriveInfo{
managedAddr: managedAddr, managedAddr: managedAddr,
branch: branchNum, branch: branch,
index: nextIndex - 1, index: nextIndex - 1,
} }
addressInfo = append(addressInfo, &info) addressInfo = append(addressInfo, &info)
@ -1113,13 +1089,8 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
// Set the last address and next address for tracking. // Set the last address and next address for tracking.
ma := addressInfo[len(addressInfo)-1].managedAddr ma := addressInfo[len(addressInfo)-1].managedAddr
if internal { acctInfo.nextIndex[branch] = nextIndex
acctInfo.nextInternalIndex = nextIndex acctInfo.lastAddr[branch] = ma
acctInfo.lastInternalAddr = ma
} else {
acctInfo.nextExternalIndex = nextIndex
acctInfo.lastExternalAddr = ma
}
} }
ns.Tx().OnCommit(onCommit) 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 // Choose the branch key and index depending on whether or not this is
// an internal address. // an internal address.
branchNum, nextIndex := ExternalBranch, acctInfo.nextExternalIndex nextIndex := acctInfo.nextIndex[branch]
if internal {
branchNum = InternalBranch
nextIndex = acctInfo.nextInternalIndex
}
// Choose the appropriate type of address to derive since it's possible // Choose the appropriate type of address to derive since it's possible
// for a watch-only account to have a different schema from the // for a watch-only account to have a different schema from the
// manager's. // 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 // If the last index requested is already lower than the next index, we
// can return early. // 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. // 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 { if err != nil {
str := fmt.Sprintf("failed to derive extended key branch %d", str := fmt.Sprintf("failed to derive extended key branch %d",
branchNum) branch)
return managerError(ErrKeyChain, str, err) return managerError(ErrKeyChain, str, err)
} }
defer branchKey.Zero() // Ensure branch key is zeroed when done. defer branchKey.Zero() // Ensure branch key is zeroed when done.
@ -1224,7 +1191,7 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
derivationPath := DerivationPath{ derivationPath := DerivationPath{
InternalAccount: account, InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(), Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: branchNum, Branch: branch,
Index: nextIndex - 1, Index: nextIndex - 1,
} }
@ -1238,15 +1205,13 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
if err != nil { if err != nil {
return err return err
} }
if internal { addr.internal = branch == InternalBranch
addr.internal = true
}
managedAddr := addr managedAddr := addr
nextKey.Zero() nextKey.Zero()
info := unlockDeriveInfo{ info := unlockDeriveInfo{
managedAddr: managedAddr, managedAddr: managedAddr,
branch: branchNum, branch: branch,
index: nextIndex - 1, index: nextIndex - 1,
} }
addressInfo = append(addressInfo, &info) addressInfo = append(addressInfo, &info)
@ -1302,21 +1267,16 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
// Set the last address and next address for tracking. // Set the last address and next address for tracking.
ma := addressInfo[len(addressInfo)-1].managedAddr ma := addressInfo[len(addressInfo)-1].managedAddr
if internal { acctInfo.nextIndex[branch] = nextIndex
acctInfo.nextInternalIndex = nextIndex acctInfo.lastAddr[branch] = ma
acctInfo.lastInternalAddr = ma
} else {
acctInfo.nextExternalIndex = nextIndex
acctInfo.lastExternalAddr = ma
}
return nil 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. // that are intended for external use from the address manager.
func (s *ScopedKeyManager) NextExternalAddresses(ns walletdb.ReadWriteBucket, func (s *ScopedKeyManager) NextAddresses(ns walletdb.ReadWriteBucket,
account uint32, numAddresses uint32) ([]ManagedAddress, error) { account uint32, branch uint32, numAddresses uint32) ([]ManagedAddress, error) {
// Enforce maximum account number. // Enforce maximum account number.
if account > MaxAccountNum { if account > MaxAccountNum {
@ -1327,32 +1287,15 @@ func (s *ScopedKeyManager) NextExternalAddresses(ns walletdb.ReadWriteBucket,
s.mtx.Lock() s.mtx.Lock()
defer s.mtx.Unlock() defer s.mtx.Unlock()
return s.nextAddresses(ns, account, numAddresses, false) return s.nextAddresses(ns, account, branch, numAddresses)
}
// 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)
} }
// ExtendExternalAddresses ensures that all valid external keys through // ExtendExternalAddresses ensures that all valid external keys through
// lastIndex are derived and stored in the wallet. This is used to ensure that // 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 // wallet's persistent state catches up to a external child that was found
// during recovery. // during recovery.
func (s *ScopedKeyManager) ExtendExternalAddresses(ns walletdb.ReadWriteBucket, func (s *ScopedKeyManager) ExtendAddresses(ns walletdb.ReadWriteBucket,
account uint32, lastIndex uint32) error { account uint32, branch uint32, lastIndex uint32) error {
if account > MaxAccountNum { if account > MaxAccountNum {
err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil) err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil)
@ -1362,28 +1305,10 @@ func (s *ScopedKeyManager) ExtendExternalAddresses(ns walletdb.ReadWriteBucket,
s.mtx.Lock() s.mtx.Lock()
defer s.mtx.Unlock() 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 // LastAddress returns the most recently requested chained external
// 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
// address from calling NextExternalAddress for the given account. The first // address from calling NextExternalAddress for the given account. The first
// external address for the account will be returned if none have been // external address for the account will be returned if none have been
// previously requested. // 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 // 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 // than the MaxAccountNum constant or there is no account information for the
// passed account. Any other errors returned are generally unexpected. // passed account. Any other errors returned are generally unexpected.
func (s *ScopedKeyManager) LastExternalAddress(ns walletdb.ReadBucket, func (s *ScopedKeyManager) LastAddress(ns walletdb.ReadBucket,
account uint32) (ManagedAddress, error) { account, branch uint32) (ManagedAddress, error) {
// Enforce maximum account number. // Enforce maximum account number.
if account > MaxAccountNum { if account > MaxAccountNum {
@ -1410,47 +1335,13 @@ func (s *ScopedKeyManager) LastExternalAddress(ns walletdb.ReadBucket,
return nil, err return nil, err
} }
if acctInfo.nextExternalIndex > 0 { if acctInfo.nextIndex[branch] > 0 {
return acctInfo.lastExternalAddr, nil return acctInfo.lastAddr[branch], nil
} }
return nil, managerError(ErrAddressNotFound, "no previous external address", 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 // NewRawAccount creates a new account for the scoped manager. This method
// differs from the NewAccount method in that this method takes the account // differs from the NewAccount method in that this method takes the account
// number *directly*, rather than taking a string name for the account, then // number *directly*, rather than taking a string name for the account, then

View file

@ -326,14 +326,14 @@ func (w *Wallet) ImportAccountDryRun(name string,
// attempt, we'll want to invalidate the cache for it. // attempt, we'll want to invalidate the cache for it.
defer manager.InvalidateAccountCache(accountProps.AccountNumber) defer manager.InvalidateAccountCache(accountProps.AccountNumber)
externalAddrs, err = manager.NextExternalAddresses( externalAddrs, err = manager.NextAddresses(
ns, accountProps.AccountNumber, numAddrs, ns, accountProps.AccountNumber, waddrmgr.ExternalBranch, numAddrs,
) )
if err != nil { if err != nil {
return err return err
} }
internalAddrs, err = manager.NextInternalAddresses( internalAddrs, err = manager.NextAddresses(
ns, accountProps.AccountNumber, numAddrs, ns, accountProps.AccountNumber, waddrmgr.InternalBranch, numAddrs,
) )
if err != nil { if err != nil {
return err return err

View file

@ -261,11 +261,7 @@ func (rs *RecoveryState) AddWatchedOutPoint(outPoint *wire.OutPoint,
type ScopeRecoveryState struct { type ScopeRecoveryState struct {
// ExternalBranch is the recovery state of addresses generated for // ExternalBranch is the recovery state of addresses generated for
// external use, i.e. receiving addresses. // external use, i.e. receiving addresses.
ExternalBranch *BranchRecoveryState AccountBranches [][2]*BranchRecoveryState
// InternalBranch is the recovery state of addresses generated for
// internal use, i.e. change addresses.
InternalBranch *BranchRecoveryState
} }
// NewScopeRecoveryState initializes an ScopeRecoveryState with the chosen // NewScopeRecoveryState initializes an ScopeRecoveryState with the chosen

View file

@ -954,22 +954,12 @@ func expandScopeHorizons(ns walletdb.ReadWriteBucket,
return nil return nil
} }
// externalKeyPath returns the relative external derivation path /0/0/index. // keyPath returns the relative external derivation path /account/branch/index.
func externalKeyPath(index uint32) waddrmgr.DerivationPath { func keyPath(account, branch, index uint32) waddrmgr.DerivationPath {
return waddrmgr.DerivationPath{ return waddrmgr.DerivationPath{
InternalAccount: waddrmgr.DefaultAccountNum, InternalAccount: account,
Account: waddrmgr.DefaultAccountNum, Account: account,
Branch: waddrmgr.ExternalBranch, Branch: branch,
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,
Index: index, Index: index,
} }
} }
@ -982,8 +972,7 @@ func newFilterBlocksRequest(batch []wtxmgr.BlockMeta,
filterReq := &chain.FilterBlocksRequest{ filterReq := &chain.FilterBlocksRequest{
Blocks: batch, Blocks: batch,
ExternalAddrs: make(map[waddrmgr.ScopedIndex]btcutil.Address), Addresses: make(map[waddrmgr.ScopedIndex]btcutil.Address),
InternalAddrs: make(map[waddrmgr.ScopedIndex]btcutil.Address),
WatchedOutPoints: recoveryState.WatchedOutPoints(), WatchedOutPoints: recoveryState.WatchedOutPoints(),
} }
@ -1112,23 +1101,12 @@ func logFilterBlocksResp(block wtxmgr.BlockMeta,
resp *chain.FilterBlocksResponse) { resp *chain.FilterBlocksResponse) {
// Log the number of external addresses found in this block. // Log the number of external addresses found in this block.
var nFoundExternal int var nFoundAddresses int
for _, indexes := range resp.FoundExternalAddrs { nFoundAddresses += len(resp.FoundAddresses)
nFoundExternal += len(indexes)
}
if nFoundExternal > 0 {
log.Infof("Recovered %d external addrs at height=%d hash=%v",
nFoundExternal, block.Height, block.Hash)
}
// Log the number of internal addresses found in this block. if nFoundAddresses > 0 {
var nFoundInternal int log.Infof("Recovered %d addrs at height=%d hash=%v",
for _, indexes := range resp.FoundInternalAddrs { nFoundAddresses, block.Height, block.Hash)
nFoundInternal += len(indexes)
}
if nFoundInternal > 0 {
log.Infof("Recovered %d internal addrs at height=%d hash=%v",
nFoundInternal, block.Height, block.Hash)
} }
// Log the number of outpoints found in this block. // 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 { err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
maddr, err := manager.LastExternalAddress(addrmgrNs, account) maddr, err := manager.LastAddress(addrmgrNs, account, waddrmgr.ExternalBranch)
if err != nil { if err != nil {
// If no address exists yet, create the first external // If no address exists yet, create the first external
// address. // address.
@ -2954,7 +2932,7 @@ func (w *Wallet) newAddress(addrmgrNs walletdb.ReadWriteBucket, account uint32,
} }
// Get next address from wallet. // Get next address from wallet.
addrs, err := manager.NextExternalAddresses(addrmgrNs, account, 1) addrs, err := manager.NextAddresses(addrmgrNs, account, waddrmgr.ExternalBranch, 1)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -3012,7 +2990,7 @@ func (w *Wallet) newChangeAddress(addrmgrNs walletdb.ReadWriteBucket,
} }
// Get next chained change address from wallet for account. // 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 { if err != nil {
return nil, err return nil, err
} }