Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions wallet/recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ type RecoveryState struct {
// account + branch). This is the source of truth.
branchStates map[waddrmgr.BranchScope]*BranchRecoveryState

// accountNames maps every account loaded into the recovery state to its
// durable name. The horizon map is keyed only by branch scope (scope +
// account number + branch), but a backend resolving an emitted horizon
// must prefer the name: both store backends mask an imported account's
// number to 0, so resolving an imported-xpub horizon by number alone
// would mis-resolve to the default derived account or fail. This lookup
// lets the syncer stamp ScanHorizon.AccountName at emission.
accountNames map[waddrmgr.AccountScope]string

// accountIDs maps every account loaded into the recovery state to its
// stable Store account row identity when the active backend exposes one.
// The syncer resolves this identity when the account snapshot is loaded,
// then emits it directly with ScanHorizon so later account renames cannot
// break horizon extension.
accountIDs map[waddrmgr.AccountScope]*uint32

// watchedOutPoints contains the set of all outpoints known to the
// wallet. This is updated iteratively as new outpoints are found during
// a rescan.
Expand Down Expand Up @@ -89,6 +105,8 @@ func NewRecoveryState(recoveryWindow uint32,
branchStates: make(
map[waddrmgr.BranchScope]*BranchRecoveryState,
),
accountNames: make(map[waddrmgr.AccountScope]string),
accountIDs: make(map[waddrmgr.AccountScope]*uint32),
watchedOutPoints: make(map[wire.OutPoint]address.Address),
chainParams: chainParams,
addrMgr: addrMgr,
Expand Down Expand Up @@ -150,6 +168,41 @@ func (rs *RecoveryState) WatchListSize() int {
return len(rs.addrFilters) + len(rs.outpoints)
}

// AccountName returns the durable name of the account at the given scope and
// number, as loaded into the recovery state. The bool reports whether the
// account was found; callers that fail to find a name MUST fall back to the
// account-number identity rather than emitting an empty name, since an empty
// name would resolve to no account at the backend.
func (rs *RecoveryState) AccountName(scope waddrmgr.KeyScope,
account uint32) (string, bool) {

name, ok := rs.accountNames[waddrmgr.AccountScope{
Scope: scope,
Account: account,
}]

return name, ok
}

// AccountID returns the stable Store account ID for the given scope and number,
// as resolved when the account was loaded into the recovery state. The bool
// reports whether an account ID was found.
func (rs *RecoveryState) AccountID(scope waddrmgr.KeyScope,
account uint32) (*uint32, bool) {

id, ok := rs.accountIDs[waddrmgr.AccountScope{
Scope: scope,
Account: account,
}]
if !ok || id == nil {
return nil, false
}

idCopy := *id

return &idCopy, true
}

// GetBranchState returns the recovery state for the provided branch scope.
// It acts as the source of truth for branch states by either retrieving an
// existing in-memory BranchRecoveryState for the given `bs` (branch scope)
Expand Down Expand Up @@ -370,6 +423,22 @@ func (rs *RecoveryState) ProcessBlock(block *wire.MsgBlock) (
}, nil
}

// setAccountID records the stable store account ID for an account loaded into
// the recovery state.
func (rs *RecoveryState) setAccountID(scope waddrmgr.KeyScope,
account uint32, accountID *uint32) {

if accountID == nil {
return
}

id := *accountID
rs.accountIDs[waddrmgr.AccountScope{
Scope: scope,
Account: account,
}] = &id
}

// copyOutpointMap returns a shallow copy of the watched outpoint set.
func copyOutpointMap(src map[wire.OutPoint][]byte) map[wire.OutPoint][]byte {
dst := make(map[wire.OutPoint][]byte, len(src))
Expand All @@ -388,6 +457,14 @@ func copyOutpointMap(src map[wire.OutPoint][]byte) map[wire.OutPoint][]byte {
func (rs *RecoveryState) initAccountState(
props *waddrmgr.AccountProperties) error {

// Record the account name so emitted horizons can carry the durable,
// backend-agnostic identity rather than relying on the account number,
// which both store backends mask to 0 for imported accounts.
rs.accountNames[waddrmgr.AccountScope{
Scope: props.KeyScope,
Account: props.AccountNumber,
}] = props.AccountName

initBranch := func(branch uint32, lastKnownIndex uint32) error {
bs := waddrmgr.BranchScope{
Scope: props.KeyScope,
Expand Down
Loading
Loading