Skip to content

Commit

Permalink
Merge pull request #4431 from leather-wallet/fix/migration-persist
Browse files Browse the repository at this point in the history
fix: migration redux persist
  • Loading branch information
alter-eggo authored Oct 31, 2023
2 parents ceb088c + 86f2168 commit cf653c1
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 18 deletions.
4 changes: 2 additions & 2 deletions src/app/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface RootState {
};
ordinals: ReturnType<typeof ordinalsSlice.reducer>;
inMemoryKeys: ReturnType<typeof inMemoryKeySlice.reducer>;
'software-keys': ReturnType<typeof keySlice.reducer>;
softwareKeys: ReturnType<typeof keySlice.reducer>;
networks: ReturnType<typeof networksSlice.reducer>;
submittedTransactions: ReturnType<typeof submittedTransactionsSlice.reducer>;
settings: ReturnType<typeof settingsSlice.reducer>;
Expand All @@ -60,7 +60,7 @@ const appReducer = combineReducers({
}),
ordinals: ordinalsSlice.reducer,
inMemoryKeys: inMemoryKeySlice.reducer,
'software-keys': keySlice.reducer,
softwareKeys: keySlice.reducer,
networks: networksSlice.reducer,
submittedTransactions: submittedTransactionsSlice.reducer,
settings: settingsSlice.reducer,
Expand Down
2 changes: 1 addition & 1 deletion src/app/store/software-keys/software-key.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { RootState } from '@app/store';

import { selectStacksChain } from '../chains/stx-chain.selectors';

const selectKeysSlice = (state: RootState) => state['software-keys'];
const selectKeysSlice = (state: RootState) => state['softwareKeys'];

export const selectDefaultSoftwareKey = createSelector(
selectKeysSlice,
Expand Down
2 changes: 1 addition & 1 deletion src/app/store/software-keys/software-key.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const keyAdapter = createEntityAdapter<KeyConfig>();
export const initialKeysState = keyAdapter.getInitialState();

export const keySlice = createSlice({
name: 'software-keys',
name: 'softwareKeys',
initialState: migrateVaultReducerStoreToNewStateStructure(initialKeysState),
reducers: {
createSoftwareWalletComplete(state, action: PayloadAction<KeyConfig>) {
Expand Down
52 changes: 50 additions & 2 deletions src/shared/storage/redux-pesist.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PersistConfig, createMigrate, getStoredState } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';

import type { RootState } from '@app/store';

Expand Down Expand Up @@ -76,7 +77,7 @@ const legacyPersistConfig: PersistConfig<RootState> = {
version: 1,
storage,
serialize: true,
whitelist: ['analytics', 'chains', 'software-keys', 'networks', 'onboarding', 'settings'],
whitelist: ['analytics', 'chains', 'keys', 'networks', 'onboarding', 'settings'],
};

async function migrateToUsingNoSerialization() {
Expand All @@ -96,25 +97,72 @@ async function migrateToUsingNoSerialization() {
return storage;
}

async function migrateToRenameKeysStoreModule(state: Promise<any>) {
const resolvedState = await Promise.resolve(state);

const newStore = JSON.parse(
JSON.stringify({
...resolvedState,
softwareKeys: resolvedState.keys,
ledger: {
...resolvedState.ledger,
},
})
);

// add stacks ledger keys to new place
if (resolvedState.keys.entities.default.type === 'ledger') {
newStore.ledger = { ...resolvedState.ledger, stacks: resolvedState.keys };
}

// add default bitcoin ledger state
if (!newStore.ledger.bitcoin) {
newStore.ledger.bitcoin = {
ids: [],
entities: {},
targetId: '',
};
}

// add default stacks ledger state
if (!newStore.ledger.stacks) {
newStore.ledger.stacks = {
ids: [],
entities: {},
targetId: '',
};
}

// remove old keys store
Reflect.deleteProperty(newStore, 'keys');

return newStore;
}

interface UntypedDeserializeOption {
deserialize?: boolean;
}
export const persistConfig: PersistConfig<RootState> & UntypedDeserializeOption = {
key: 'root',
stateReconciler: autoMergeLevel2,
version: 1,
storage,
serialize: false,
migrate: createMigrate({
0: async () => {
return migrateToUsingNoSerialization();
},
1: async (state: any) => {
return migrateToRenameKeysStoreModule(state);
},
debug: true,
} as any),
deserialize: false,
whitelist: [
'analytics',
'chains',
'ordinals',
'software-keys',
'softwareKeys',
'ledger',
'networks',
'onboarding',
Expand Down
4 changes: 2 additions & 2 deletions tests/page-object-models/onboarding.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const TEST_ACCOUNT_SECRET_KEY = process.env.TEST_ACCOUNT_SECRET_KEY ?? '';
export const testSoftwareAccountDefaultWalletState = {
analytics: { hasStxDeposits: { '1': true, '2147483648': true } },
chains: { stx: { default: { highestAccountIndex: 1, currentAccountIndex: 0 } } },
'software-keys': {
softwareKeys: {
ids: ['default'],
entities: {
default: {
Expand Down Expand Up @@ -53,7 +53,7 @@ const testLedgerAccountDefaultWalletState = {
_persist: { rehydrated: true, version: 1 },
analytics: { hasStxDeposits: { '1': false, '2147483648': true } },
chains: { stx: { default: { currentAccountIndex: 0, highestAccountIndex: 0 } } },
'software-keys': {
softwareKeys: {
entities: {},
ids: [],
},
Expand Down
10 changes: 5 additions & 5 deletions tests/specs/onboarding/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ test.describe('Onboarding an existing user', () => {
);

// Deleting values that are known to differ at random
delete (walletState as any)['software-keys'].entities.default.encryptedSecretKey;
delete (walletState as any)['software-keys'].entities.default.salt;
delete (testSoftwareAccountDefaultWalletState as any)['software-keys'].entities.default
delete (walletState as any).softwareKeys.entities.default.encryptedSecretKey;
delete (walletState as any).softwareKeys.entities.default.salt;
delete (testSoftwareAccountDefaultWalletState as any).softwareKeys.entities.default
.encryptedSecretKey;
delete (testSoftwareAccountDefaultWalletState as any)['software-keys'].entities.default.salt;
delete (testSoftwareAccountDefaultWalletState as any).softwareKeys.entities.default.salt;

test.expect(walletState).toEqual(testSoftwareAccountDefaultWalletState);
});
Expand All @@ -42,7 +42,7 @@ test.describe('Onboarding an existing user', () => {
// enter some invalid key
const invalidKey = 'some incorrect data';
await onboardingPage.signInMnemonicKey(invalidKey);
const signInButton = await onboardingPage.page.getByTestId(OnboardingSelectors.SignInBtn);
const signInButton = onboardingPage.page.getByTestId(OnboardingSelectors.SignInBtn);
const error = onboardingPage.page.getByText('Words 1 and 2 are incorrect or misspelled');
await test.expect(error).toBeVisible();
await test.expect(signInButton).toBeDisabled();
Expand Down
11 changes: 6 additions & 5 deletions tests/specs/store-migrations/store-migrations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ test.describe('Store migrations', () => {
await globalPage.page.waitForTimeout(1000);
});

test.describe('Migration 0 --> 1', () => {
test.describe('Migration 0 --> 2', () => {
const previousSerializedState =
'{"analytics":"{\\"hasStxDeposits\\":{\\"1\\":true,\\"2147483648\\":true}}","chains":"{\\"stx\\":{\\"default\\":{\\"highestAccountIndex\\":16,\\"currentAccountIndex\\":0}}}","software-keys":"{\\"ids\\":[\\"default\\"],\\"entities\\":{\\"default\\":{\\"type\\":\\"software\\",\\"id\\":\\"default\\",\\"salt\\":\\"c4cccf33166051f7704cd877a2f03f93\\",\\"encryptedSecretKey\\":\\"b7f516798e7160eca15c50b62e588698937f8ecf3930efc42baa690ddc0c7a51b74e3e4b129859274ed272652bc47651c6b6effbddf4d72a3eb9d2ea657b64a833c9bdccb562e45d94f0cc1366154072f12d35290566a99a6f952cd234ca9259\\"}}}","networks":"{\\"ids\\":[],\\"entities\\":{},\\"currentNetworkId\\":\\"mainnet\\"}","onboarding":"{\\"hideSteps\\":true,\\"stepsStatus\\":{\\"Back up secret key\\":1,\\"Add some funds\\":0,\\"Explore apps\\":0,\\"Buy an NFT\\":0}}","settings":"{\\"userSelectedTheme\\":\\"system\\",\\"hasAllowedAnalytics\\":false,\\"dismissedMessages\\":[]}","_persist":"{\\"version\\":1,\\"rehydrated\\":true}"}';
'{"analytics":"{\\"hasStxDeposits\\":{\\"1\\":true,\\"2147483648\\":true}}","chains":"{\\"stx\\":{\\"default\\":{\\"highestAccountIndex\\":16,\\"currentAccountIndex\\":0}}}","keys":"{\\"ids\\":[\\"default\\"],\\"entities\\":{\\"default\\":{\\"type\\":\\"software\\",\\"id\\":\\"default\\",\\"salt\\":\\"c4cccf33166051f7704cd877a2f03f93\\",\\"encryptedSecretKey\\":\\"b7f516798e7160eca15c50b62e588698937f8ecf3930efc42baa690ddc0c7a51b74e3e4b129859274ed272652bc47651c6b6effbddf4d72a3eb9d2ea657b64a833c9bdccb562e45d94f0cc1366154072f12d35290566a99a6f952cd234ca9259\\"}}}","networks":"{\\"ids\\":[],\\"entities\\":{},\\"currentNetworkId\\":\\"mainnet\\"}","onboarding":"{\\"hideSteps\\":true,\\"stepsStatus\\":{\\"Back up secret key\\":1,\\"Add some funds\\":0,\\"Explore apps\\":0,\\"Buy an NFT\\":0}}","settings":"{\\"userSelectedTheme\\":\\"system\\",\\"hasAllowedAnalytics\\":false,\\"dismissedMessages\\":[]}","_persist":"{\\"version\\":1,\\"rehydrated\\":true}"}';

test('that the app detects old store format', async ({ extensionId, globalPage }) => {
const { page } = globalPage;
Expand Down Expand Up @@ -43,17 +43,18 @@ test.describe('Store migrations', () => {
chrome.storage.local.get(['persist:root'], state => resolve(state['persist:root']))
)
);

// Assert that old values are present in unserialized format
test
.expect(result['software-keys'].entities['default']?.encryptedSecretKey)
.expect(result.softwareKeys.entities['default']?.encryptedSecretKey)
.toEqual(
'b7f516798e7160eca15c50b62e588698937f8ecf3930efc42baa690ddc0c7a51b74e3e4b129859274ed272652bc47651c6b6effbddf4d72a3eb9d2ea657b64a833c9bdccb562e45d94f0cc1366154072f12d35290566a99a6f952cd234ca9259'
);

test
.expect(result['software-keys'].entities['default']?.salt)
.expect(result.softwareKeys.entities['default']?.salt)
.toEqual('c4cccf33166051f7704cd877a2f03f93');

test.expect(result.ledger.stacks).toBeDefined();
});
});
});

0 comments on commit cf653c1

Please sign in to comment.