Skip to content

identity/role: GetWalletID swallows storage errors, so a transient DB blip creates a duplicate wallet - #2170

Open
Effi-S wants to merge 1 commit into
mainfrom
fix-2063
Open

identity/role: GetWalletID swallows storage errors, so a transient DB blip creates a duplicate wallet#2170
Effi-S wants to merge 1 commit into
mainfrom
fix-2063

Conversation

@Effi-S

@Effi-S Effi-S commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #2063

Summary

Registry.GetWalletID swallows every storage error and returns ("", nil), which its callers treat
identically to "no wallet is bound to this identity." A transient storage error (DB blip, timeout)
therefore looks exactly like "not registered yet," and the wallet-lookup fallback chain in Lookup
creates a brand-new wallet and a second identity binding for an identity that already has one.

Where

token/services/identity/role/registry.go:237-246:

func (r *Registry) GetWalletID(ctx context.Context, identity driver.Identity) (string, error) {
	wID, err := r.Storage.GetWalletID(ctx, identity, int(r.Role.ID()))
	if err != nil {
		//nolint:nilerr
		return "", nil
	}
	...
	return wID, nil
}

All three call sites treat err == nil && len(wID) == 0 as "not found, try the next fallback":
registry.go:85-92, registry.go:114-117, registry.go:131-133.

Impact

Lookup (registry.go:72-166) is the fallback path used by WalletByID (registry.go:271-281)
whenever the in-memory cache misses. If the storage-level GetWalletID call fails transiently (not
"no row found," but an actual error — timeout, connection reset, etc.), the registry cannot
distinguish that from "this identity has never been registered." It falls through to
WalletFactory.NewWallet (registry.go:293), creating a second wallet and a second identity→wallet
binding for the same identity. This duplicates wallet state and, depending on the wallet
implementation, can duplicate key material bookkeeping.

Reproduction

Not yet committed. Mock idriver.WalletStoreService.GetWalletID to return a non-nil error (e.g. a
simulated transient DB error) on the first call for an identity that has a real binding, and a
successful lookup on a second, independent call. Assert Registry.GetWalletID propagates the error
rather than returning ("", nil), and that WalletByID does not create a duplicate wallet when the
underlying storage error is transient.

Suggested fix

Propagate the error and let callers decide:

func (r *Registry) GetWalletID(ctx context.Context, identity driver.Identity) (string, error) {
	wID, err := r.Storage.GetWalletID(ctx, identity, int(r.Role.ID()))
	if err != nil {
		return "", errors.Wrapf(err, "failed to get wallet id for identity [%s]", identity)
	}
	...
	return wID, nil
}

This requires updating the three call sites in Lookup to distinguish "storage error" (abort/retry)
from "no row found" (continue to the next fallback) — today they're merged into one case. This
should land together with #8 (WalletByID write-lock scope), since both touch the same
creation path and fixing one without the other leaves the duplicate-wallet risk only partially
addressed.

Severity

MEDIUM — requires a transient storage failure to trigger, but results in persisted duplicate wallet
state, which is hard to reconcile after the fact.

@Effi-S Effi-S added this to the Q3/26 milestone Aug 10, 2026
@Effi-S Effi-S self-assigned this Aug 10, 2026
Comment thread create_pr_from_issue.py Fixed
Comment thread cmd/benchmarking/bench_parse.py Fixed
Comment thread cmd/benchmarking/bench_parse.py Fixed
@Effi-S Effi-S closed this Aug 10, 2026
@Effi-S Effi-S reopened this Aug 10, 2026
@Effi-S
Effi-S marked this pull request as ready for review August 10, 2026 13:00
@Effi-S
Effi-S requested a review from AkramBitar August 10, 2026 13:00
@Effi-S
Effi-S force-pushed the fix-2063 branch 2 times, most recently from 9f855b1 to ae3090b Compare August 10, 2026 14:18

@AkramBitar AkramBitar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e

@AkramBitar AkramBitar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to review

@Effi-S
Effi-S force-pushed the fix-2063 branch 4 times, most recently from 88787f5 to 32c7544 Compare August 12, 2026 16:15
… an empty string issue

Signed-off-by: Effi-S <effi.szt@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

identity/role: GetWalletID swallows storage errors, so a transient DB blip creates a duplicate wallet

2 participants