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

fix: Improve migration 121.1 state validation #26773

Merged
merged 2 commits into from
Aug 29, 2024

Conversation

Gudahtt
Copy link
Member

@Gudahtt Gudahtt commented Aug 29, 2024

Description

Migration 121.1 has been updated to include more state validation, so that it does not throw an error in the event that state is corrupted in some unexpected way. Unexpected corrupted state is now reported to Sentry as well.

Open in GitHub Codespaces

Related issues

Related: #26377

Manual testing steps

The unit tests outline the scenarios that the added validation are meant to cover. Probably not worth anyone's time to manually test those.

To test the migration in general though, here are the steps:

  • Create a dev build from v12.1.0
  • Install the dev build from the dist/chrome directory and proceed
    through onboarding
  • Run this command in the background console: chrome.storage.local.get( null, (state) => { state.data.AccountsController.internalAccounts.selectedAccount = 'unknown id'; chrome.storage.local.set(state, () => chrome.runtime.reload()); } );
    • The extension should now be in a broken state
    • Note that you can use this same script to corrupt state in other ways to test other scenarios
  • Disable the extension
  • Switch to this branch and create a dev build
  • Enable and reload the extension
  • You should see in the console that migration 121.1 has run without
    error
    • You can run chrome.storage.local.get(console.log) to check that the
      AccountsController state is now valid
    • The extension should no longer be broken

Screenshots/Recordings

N/A

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.

Migration 121.1 has been updated to include more state validation, so
that it does not throw an error in the event that state is corrupted in
some unexpected way. Unexpected corrupted state is now reported to
Sentry as well.
@@ -28,22 +29,114 @@ export async function migrate(
return versionedData;
}

function transformState(state: Record<string, unknown>) {
const accountsControllerState = state?.AccountsController as
Copy link
Member Author

Choose a reason for hiding this comment

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

The intent of using unknown was to force runtime validation of types, rather than assuming state is in the correct shape. We've seen unexpected state corruption in the past, so we try to be more cautious about this. Using a type cast here is defeating the purpose of using unknown, as it lets you skip past this validation.

This also goes against our general TypeScript guidelines, see here for more information: https://github.com/MetaMask/contributor-docs/blob/main/docs/typescript.md#avoid-type-assertions-by-using-type-guards-to-improve-type-inference

}

if (
Object.keys(accountsControllerState.internalAccounts.accounts).length === 0
Copy link
Member Author

Choose a reason for hiding this comment

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

These two cases might be encountered for users who have not completed onboarding. It seemed fine to skip the migration in this case, and the types of AccountsController seem to allow it as well. A console log was added just in case this happens unexpectedly.

} else if (typeof firstAccount.id !== 'string') {
global.sentry?.captureException(
new Error(
`Migration ${version}: Invalid AccountsController internalAccounts.accounts state, entry found with an id of type '${typeof firstAccount.id}'`,
Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't strictly need to validate the presence and type of the id property, TypeScript didn't demand it of me. But it seemed like a good idea. If the id was corrupted somehow, we wouldn't want to propagate that.

@@ -25,7 +32,8 @@ describe('migration #121.1', () => {
},
};

const newStorage = await migrate(oldStorage);
const newStorage = await migrate(cloneDeep(oldStorage));
Copy link
Member Author

Choose a reason for hiding this comment

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

I updated the existing tests to use cloneDeep to ensure we don't accidentally forget to clone state in the migration itself. The clone happens in the migration in the copy+pasted portion, so it's not likely to be missed, but it's easy to cover in tests so why not.

expect(newStorage.data).toStrictEqual(oldStorage.data);
});

it('does nothing if AccountsController state is missing', async () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

I added a few tests for these "expected missing state" cases, which were already handled correctly but were not tested

@Gudahtt Gudahtt marked this pull request as ready for review August 29, 2024 19:38
@Gudahtt Gudahtt requested a review from a team as a code owner August 29, 2024 19:38
Copy link

codecov bot commented Aug 29, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 70.10%. Comparing base (b9173da) to head (afa7976).
Report is 2 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #26773      +/-   ##
===========================================
+ Coverage    70.08%   70.10%   +0.02%     
===========================================
  Files         1414     1414              
  Lines        49330    49365      +35     
  Branches     13782    13792      +10     
===========================================
+ Hits         34570    34605      +35     
  Misses       14760    14760              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@Gudahtt Gudahtt merged commit 761562a into develop Aug 29, 2024
77 of 78 checks passed
@Gudahtt Gudahtt deleted the improve-migration-121-state-validation branch August 29, 2024 20:44
@github-actions github-actions bot locked and limited conversation to collaborators Aug 29, 2024
@metamaskbot metamaskbot added the release-12.5.0 Issue or pull request that will be included in release 12.5.0 label Aug 29, 2024
@metamaskbot
Copy link
Collaborator

Builds ready [afa7976]
Page Load Metrics (1781 ± 92 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint15122213178118890
domContentLoaded15012196175317484
load15132205178119192
domInteractive126942178
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 2.67 KiB (0.08%)
  • ui: 0 Bytes (0.00%)
  • common: 0 Bytes (0.00%)

@gauthierpetetin gauthierpetetin added release-12.4.0 Issue or pull request that will be included in release 12.4.0 and removed release-12.5.0 Issue or pull request that will be included in release 12.5.0 labels Sep 11, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
release-12.4.0 Issue or pull request that will be included in release 12.4.0 team-accounts team-extension-platform team-wallet-framework
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants