Skip to content
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

refactor: use withKeyring to batch account restore operation #11409

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

mikesposito
Copy link
Member

@mikesposito mikesposito commented Sep 24, 2024

Description

The problem

With the current used version of KeyringController, the addNewAccountWithoutUpdate method is not doing what its name claims - the state is updated everytime the function is called.

This is because KeyringController has no control over its messenger's listeners, and so PreferencesController and AccountsController (and so on downstream) will react to keyrings state updates everytime addNewAccountWithoutUpdate is called.

The result is that the importAdditionalAccounts helper present on mobile is incredibly inefficient, as accounts will be added one by one, executing all side effects at every single iteration.

The solution

This PR leverages the new withKeyring method from KeyringController to batch these iterations into one single atomic state update - take a look at the code for more info about how it is done

Related issues

Related: MetaMask/core#3848

Manual testing steps

  1. Go to this page...

Screenshots/Recordings

Before

After

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Copy link
Contributor

github-actions bot commented Sep 24, 2024

https://bitrise.io/ Bitrise

❌❌❌ pr_smoke_e2e_pipeline failed on Bitrise! ❌❌❌

Commit hash: 2e16358
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/3538a971-3fa6-4c0d-9e48-0a8b6e3012f0

Note

  • You can kick off another pr_smoke_e2e_pipeline on Bitrise by removing and re-applying the Run Smoke E2E label on the pull request

Tip

  • Check the documentation if you have any doubts on how to understand the failure on bitrise

@mikesposito mikesposito self-assigned this Sep 24, 2024
@mikesposito mikesposito requested a review from a team September 24, 2024 10:55
@mikesposito
Copy link
Member Author

mikesposito commented Sep 24, 2024

We could make this even more efficient by batching the queried balances, and it would look something like this:

  1. Create the maximum number of restorable accounts (20)
  2. Batch query the balance of all 20 accounts
  3. Pop accounts with empty balance, till the last with balance

This would result in 1 single network call and 1 single state update.

Additionally, it would allow to restore accounts with balance even if there's an empty balance account in the middle

@mikesposito mikesposito added Run Smoke E2E Triggers smoke e2e on Bitrise and removed Run Smoke E2E Triggers smoke e2e on Bitrise labels Sep 24, 2024
@mikesposito mikesposito force-pushed the refactor-import-additional-accounts branch from 51b0b10 to 6e55ca9 Compare September 24, 2024 11:56
@mikesposito mikesposito added Run Smoke E2E Triggers smoke e2e on Bitrise and removed Run Smoke E2E Triggers smoke e2e on Bitrise labels Sep 24, 2024
Copy link
Contributor

github-actions bot commented Sep 24, 2024

https://bitrise.io/ Bitrise

❌❌❌ pr_smoke_e2e_pipeline failed on Bitrise! ❌❌❌

Commit hash: 6e55ca9
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/98a96876-b7b4-426c-910e-7dee72b0c065

Note

  • You can kick off another pr_smoke_e2e_pipeline on Bitrise by removing and re-applying the Run Smoke E2E label on the pull request

Tip

  • Check the documentation if you have any doubts on how to understand the failure on bitrise

Comment on lines 34 to 52
await KeyringController.withKeyring(
{ type: ExtendedKeyringTypes.hd },
async (primaryKeyring) => {
let i = 0;
// seek out the first zero balance
while (i < MAX) {
const [newAccount] = await primaryKeyring.addAccounts(1);
const newAccountBalance = await getBalance(newAccount, ethQuery);

let i = 0;
// seek out the first zero balance
while (lastBalance !== ZERO_BALANCE) {
if (i === MAX) break;
await KeyringController.addNewAccountWithoutUpdate(primaryKeyring);
accounts = await KeyringController.getAccounts();
lastBalance = await getBalance(accounts[accounts.length - 1], ethQuery);
i++;
}
if (newAccountBalance === ZERO_BALANCE) {
// remove extra zero balance account we just added and break the loop
primaryKeyring.removeAccount(newAccount);
break;
}

// remove extra zero balance account potentially created from seeking ahead
if (accounts.length > 1 && lastBalance === ZERO_BALANCE) {
await KeyringController.removeAccount(accounts[accounts.length - 1]);
accounts = await KeyringController.getAccounts();
}
i++;
}
},
);
Copy link
Member Author

@mikesposito mikesposito Sep 24, 2024

Choose a reason for hiding this comment

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

KeyringController wil do all the heavy lifting here: race condition protection, primary keyring existence check, batch state update with the resulting keyring, rollback in case of error and automatic vault update

@mikesposito
Copy link
Member Author

Failed conditions
0.0% Coverage on New Code (required ≥ 40%)

This code has never been covered

Copy link
Member

@gantunesr gantunesr left a comment

Choose a reason for hiding this comment

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

LGTM! The main feedback I have is to add unit test to the method to ensure the happy path case is working as expected. A minor and optional request would be to refactor this file to TS if possible.

Comment on lines 43 to 47
} catch (error) {
// Errors are gracefully handled so that `withKeyring`
// will not rollback the primary keyring, and accounts
// created in previous loop iterations will remain in place.
Logger.error(error);
Copy link
Member

Choose a reason for hiding this comment

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

🙌

@gantunesr
Copy link
Member

gantunesr commented Sep 24, 2024

We should also manual test it. We can handle that in the accounts team.

@mikesposito
Copy link
Member Author

@gantunesr I will convert the file to typescript, add tests and post a recording of the flow

Copy link

sonarcloud bot commented Oct 3, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
11.1% Coverage on New Code (required ≥ 40%)

See analysis details on SonarCloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Run Smoke E2E Triggers smoke e2e on Bitrise team-wallet-framework
Projects
Status: Needs more work from the author
Development

Successfully merging this pull request may close these issues.

2 participants