Skip to content

backend/account: fix bug in account discovery #3330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2025

Conversation

Beerosagos
Copy link
Collaborator

Recent commit 726289c made the account Initialization asynchronous, moving the account.ensureAddresses() call on a separate thread. This caused a regression inside backend.checkAccountUsed(), where the method wasn't waiting for the complete account synchronization before marking it as used.

This commit fixes the issue waiting for account sync before checking if it used.

Note: The tests didn't catch the regression: I couldn't find an easy way to add some latency in the mocked addresses verification, but it would be nice to introduce it in a test to make it more reliable. Any suggestions?

@Beerosagos Beerosagos requested a review from benma May 7, 2025 14:41
@@ -1533,6 +1533,9 @@ func (backend *Backend) checkAccountUsed(account accounts.Interface) {
log.WithError(err).Error("error initializing account")
return
}
for !account.Synced() {
time.Sleep(100 * time.Millisecond)
Copy link
Contributor

@benma benma May 8, 2025

Choose a reason for hiding this comment

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

That is not a robust solution 🙈 How about calling checkAccountUsed once the account is synced instead?

edit to clarify the downsides of this:

  • it might loop forever if the account does not sync
  • up to 100ms of delay before proceeding

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree is not very elegant, but why do you think it is not robust?

@Beerosagos Beerosagos force-pushed the account-discovery branch from 2ae0e8c to f1b75b8 Compare May 13, 2025 14:00
@Beerosagos
Copy link
Collaborator Author

Beerosagos commented May 13, 2025

@benma updated, PTAL.

While working on this I also noticed a weird bug that was sometimes showing a wrong account list in the sidebar: after some debugging I found the cause in backend.Accounts().

}
account.Observe(func(event observable.Event) {
if event.Subject == string(accountsTypes.EventSyncDone) {
f()
Copy link
Contributor

Choose a reason for hiding this comment

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

There is already an account.Observe() in addAccount(), imho it's better to just move the go backend.checkAccountUsed(account) there instead of having this big local f := func() ... here. Wdyt?

Technically it's not guaranteed that the SyncDone event will be fired after we start to observe it 🙈 But that's probably a super-edge case that we don't need to worry about 😄

Now it also occurs to me that this PR probably also fixes the bug if you receive coins on the last account, you can't add another one until you re-open the app and it realizes the previous account was used. Now it will re-check once coins arrive 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is already an account.Observe() in addAccount(), imho it's better to just move the go backend.checkAccountUsed(account) there instead of having this big local f := func() ... here. Wdyt?

I tested that, but it waits until the sync is done before scanning the next account: the whole discovery is much slower.

Now it also occurs to me that this PR probably also fixes the bug if you receive coins on the last account, you can't add another one until you re-open the app and it realizes the previous account was used. Now it will re-check once coins arrive 👍

Exactly :D

Technically it's not guaranteed that the SyncDone event will be fired after we start to observe it 🙈 But that's probably a super-edge case that we don't need to worry about 😄

I could check if the account is already synced before setting the observer, wdyt?

@Beerosagos Beerosagos force-pushed the account-discovery branch from f1b75b8 to 2361ac6 Compare May 14, 2025 10:59
@Beerosagos
Copy link
Collaborator Author

@benma updated, PTAL 🙏

Copy link
Contributor

@benma benma left a comment

Choose a reason for hiding this comment

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

utACK, but see suggestion to use slices.Clone()

Comment on lines 593 to 595
return backend.accounts
accounts := AccountsList{}
accounts = append(accounts, backend.accounts...)
return accounts
Copy link
Contributor

Choose a reason for hiding this comment

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

Use slices.Clone() instead, which I recently learned about here #3322 :P

Recent commit 726289c made the account
Initialization asynchronous, moving the `account.ensureAddresses()` call
on a separate thread. This caused a regression inside
`backend.checkAccountUsed()` where the method wasn't waiting for the
complete account synchronization before marking it as used.

This commit fixes the issue waiting for account sync before checking if
it used.

This also fixes the `backend.Accounts()` method, that was returning the
original `backend.accounts` object instead of a copy. This caused the
caller to read the account list without a proper lock, causing weird
behaviors during account discovery.
@Beerosagos Beerosagos force-pushed the account-discovery branch from 2361ac6 to c5b2896 Compare May 14, 2025 12:49
@Beerosagos Beerosagos merged commit 02a12ae into BitBoxSwiss:master May 14, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants