1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 03:12:42 +02:00

Use unflattened state for Sentry (#20428)

The unflattened background state is now attached to any Sentry errors
from the background process. This provides a clearer picture of the
state of the wallet, and unblocks further improvements to Sentry state
which will come in later PRs.
This commit is contained in:
Mark Stacey 2023-08-16 15:21:18 -02:30 committed by Dan Miller
parent d610aad2fd
commit 1ad47c660b
4 changed files with 138 additions and 100 deletions

View File

@ -876,14 +876,14 @@ browser.runtime.onInstalled.addListener(({ reason }) => {
function setupSentryGetStateGlobal(store) { function setupSentryGetStateGlobal(store) {
global.stateHooks.getSentryState = function () { global.stateHooks.getSentryState = function () {
const backgroundState = store.getState(); const backgroundState = store.memStore.getState();
const maskedBackgroundState = maskObject( const maskedBackgroundState = maskObject(
backgroundState, backgroundState,
SENTRY_BACKGROUND_STATE, SENTRY_BACKGROUND_STATE,
); );
return { return {
browser: window.navigator.userAgent, browser: window.navigator.userAgent,
store: { metamask: maskedBackgroundState }, store: maskedBackgroundState,
version: platform.getVersion(), version: platform.getVersion(),
}; };
}; };

View File

@ -27,60 +27,103 @@ export const ERROR_URL_ALLOWLIST = {
// sent to Sentry These properties have some potential to be useful for // sent to Sentry These properties have some potential to be useful for
// debugging, and they do not contain any identifiable information. // debugging, and they do not contain any identifiable information.
export const SENTRY_BACKGROUND_STATE = { export const SENTRY_BACKGROUND_STATE = {
alertEnabledness: true, AccountTracker: {
completedOnboarding: true, currentBlockGasLimit: true,
connectedStatusPopoverHasBeenShown: true, },
conversionDate: true, AlertController: {
conversionRate: true, alertEnabledness: true,
currentAppVersion: true, },
currentBlockGasLimit: true, AppMetadataController: {
currentCurrency: true, currentAppVersion: true,
currentLocale: true, previousAppVersion: true,
currentMigrationVersion: true, previousMigrationVersion: true,
customNonceValue: true, currentMigrationVersion: true,
defaultHomeActiveTabName: true, },
desktopEnabled: true, AppStateController: {
featureFlags: true, connectedStatusPopoverHasBeenShown: true,
firstTimeFlowType: true, defaultHomeActiveTabName: true,
forgottenPassword: true, },
incomingTxLastFetchedBlockByChainId: true, CurrencyController: {
ipfsGateway: true, conversionDate: true,
isAccountMenuOpen: true, conversionRate: true,
isInitialized: true, currentCurrency: true,
isUnlocked: true, nativeCurrency: true,
metaMetricsId: true, },
nativeCurrency: true, DecryptMessageController: {
networkId: true, unapprovedDecryptMsgCount: true,
networkStatus: true, },
nextNonce: true, DesktopController: {
participateInMetaMetrics: true, desktopEnabled: true,
preferences: true, },
previousAppVersion: true, EncryptionPublicKeyController: {
previousMigrationVersion: true, unapprovedEncryptionPublicKeyMsgCount: true,
providerConfig: { },
nickname: true, IncomingTransactionsController: {
ticker: true, incomingTxLastFetchedBlockByChainId: true,
type: true, },
KeyringController: {
isUnlocked: true,
},
MetaMetricsController: {
metaMetricsId: true,
participateInMetaMetrics: true,
},
NetworkController: {
networkId: true,
networkStatus: true,
providerConfig: {
nickname: true,
ticker: true,
type: true,
},
},
OnboardingController: {
completedOnboarding: true,
firstTimeFlowType: true,
seedPhraseBackedUp: true,
},
PreferencesController: {
currentLocale: true,
featureFlags: true,
forgottenPassword: true,
ipfsGateway: true,
preferences: true,
useBlockie: true,
useNonceField: true,
usePhishDetect: true,
},
SignatureController: {
unapprovedMsgCount: true,
unapprovedPersonalMsgCount: true,
unapprovedTypedMessagesCount: true,
}, },
seedPhraseBackedUp: true,
unapprovedDecryptMsgCount: true,
unapprovedEncryptionPublicKeyMsgCount: true,
unapprovedMsgCount: true,
unapprovedPersonalMsgCount: true,
unapprovedTypedMessagesCount: true,
useBlockie: true,
useNonceField: true,
usePhishDetect: true,
welcomeScreenSeen: true,
}; };
const flattenedBackgroundStateMask = Object.values(
SENTRY_BACKGROUND_STATE,
).reduce((partialBackgroundState, controllerState) => {
return {
...partialBackgroundState,
...controllerState,
};
}, {});
// This describes the subset of Redux state attached to errors sent to Sentry // This describes the subset of Redux state attached to errors sent to Sentry
// These properties have some potential to be useful for debugging, and they do // These properties have some potential to be useful for debugging, and they do
// not contain any identifiable information. // not contain any identifiable information.
export const SENTRY_UI_STATE = { export const SENTRY_UI_STATE = {
gas: true, gas: true,
history: true, history: true,
metamask: SENTRY_BACKGROUND_STATE, metamask: {
...flattenedBackgroundStateMask,
// This property comes from the background but isn't in controller state
isInitialized: true,
// These properties are in the `metamask` slice but not in the background state
customNonceValue: true,
isAccountMenuOpen: true,
nextNonce: true,
welcomeScreenSeen: true,
},
unconnectedAccount: true, unconnectedAccount: true,
}; };

View File

@ -7,7 +7,8 @@ const { format } = require('prettier');
const { convertToHexValue, withFixtures } = require('../helpers'); const { convertToHexValue, withFixtures } = require('../helpers');
const FixtureBuilder = require('../fixture-builder'); const FixtureBuilder = require('../fixture-builder');
const dateFields = ['metamask.conversionDate']; const backgroundDateFields = ['CurrencyController.conversionDate'];
const uiDateFields = ['metamask.conversionDate'];
/** /**
* Transform date properties to value types, to ensure that state is * Transform date properties to value types, to ensure that state is
@ -15,8 +16,23 @@ const dateFields = ['metamask.conversionDate'];
* *
* @param {unknown} data - The data to transform * @param {unknown} data - The data to transform
*/ */
function transformDates(data) { function transformBackgroundDates(data) {
for (const field of dateFields) { for (const field of backgroundDateFields) {
if (has(data, field)) {
set(data, field, typeof get(data, field));
}
}
return data;
}
/**
* Transform date properties to value types, to ensure that state is
* consistent between test runs.
*
* @param {unknown} data - The data to transform
*/
function transformUiDates(data) {
for (const field of uiDateFields) {
if (has(data, field)) { if (has(data, field)) {
set(data, field, typeof get(data, field)); set(data, field, typeof get(data, field));
} }
@ -33,12 +49,10 @@ function transformDates(data) {
* @param {boolean} [args.update] - Whether to update the snapshot if it doesn't match. * @param {boolean} [args.update] - Whether to update the snapshot if it doesn't match.
*/ */
async function matchesSnapshot({ async function matchesSnapshot({
data: unprocessedData, data,
snapshot, snapshot,
update = process.env.UPDATE_SNAPSHOTS === 'true', update = process.env.UPDATE_SNAPSHOTS === 'true',
}) { }) {
const data = transformDates(unprocessedData);
const snapshotPath = resolve(__dirname, `./state-snapshots/${snapshot}.json`); const snapshotPath = resolve(__dirname, `./state-snapshots/${snapshot}.json`);
const rawSnapshotData = await fs.readFile(snapshotPath, { const rawSnapshotData = await fs.readFile(snapshotPath, {
encoding: 'utf-8', encoding: 'utf-8',
@ -243,7 +257,7 @@ describe('Sentry errors', function () {
const mockJsonBody = JSON.parse(mockTextBody[2]); const mockJsonBody = JSON.parse(mockTextBody[2]);
const appState = mockJsonBody?.extra?.appState; const appState = mockJsonBody?.extra?.appState;
await matchesSnapshot({ await matchesSnapshot({
data: appState, data: transformBackgroundDates(appState),
snapshot: 'errors-before-init-opt-in-background-state', snapshot: 'errors-before-init-opt-in-background-state',
}); });
}, },
@ -328,7 +342,7 @@ describe('Sentry errors', function () {
const mockJsonBody = JSON.parse(mockTextBody[2]); const mockJsonBody = JSON.parse(mockTextBody[2]);
const appState = mockJsonBody?.extra?.appState; const appState = mockJsonBody?.extra?.appState;
await matchesSnapshot({ await matchesSnapshot({
data: appState, data: transformUiDates(appState),
snapshot: 'errors-before-init-opt-in-ui-state', snapshot: 'errors-before-init-opt-in-ui-state',
}); });
}, },
@ -436,7 +450,8 @@ describe('Sentry errors', function () {
const mockJsonBody = JSON.parse(mockTextBody[2]); const mockJsonBody = JSON.parse(mockTextBody[2]);
const { level, extra } = mockJsonBody; const { level, extra } = mockJsonBody;
const [{ type, value }] = mockJsonBody.exception.values; const [{ type, value }] = mockJsonBody.exception.values;
const { participateInMetaMetrics } = extra.appState.store.metamask; const { participateInMetaMetrics } =
extra.appState.store.MetaMetricsController;
// Verify request // Verify request
assert.equal(type, 'TestError'); assert.equal(type, 'TestError');
assert.equal(value, 'Test Error'); assert.equal(value, 'Test Error');
@ -494,7 +509,7 @@ describe('Sentry errors', function () {
'Invalid version state', 'Invalid version state',
); );
await matchesSnapshot({ await matchesSnapshot({
data: appState.store, data: transformBackgroundDates(appState.store),
snapshot: 'errors-after-init-opt-in-background-state', snapshot: 'errors-after-init-opt-in-background-state',
}); });
}, },
@ -588,7 +603,7 @@ describe('Sentry errors', function () {
'Invalid version state', 'Invalid version state',
); );
await matchesSnapshot({ await matchesSnapshot({
data: appState.store, data: transformUiDates(appState.store),
snapshot: 'errors-after-init-opt-in-ui-state', snapshot: 'errors-after-init-opt-in-ui-state',
}); });
}, },

View File

@ -1,52 +1,32 @@
{ {
"metamask": { "AccountTracker": { "currentBlockGasLimit": "0x1c9c380" },
"isInitialized": true, "AppStateController": {
"connectedStatusPopoverHasBeenShown": true, "connectedStatusPopoverHasBeenShown": true,
"defaultHomeActiveTabName": null, "defaultHomeActiveTabName": null
"currentAppVersion": "10.34.4", },
"previousAppVersion": "", "CurrencyController": {
"previousMigrationVersion": 0, "conversionDate": "number",
"currentMigrationVersion": 94, "conversionRate": 1700,
"nativeCurrency": "ETH",
"currentCurrency": "usd"
},
"DecryptMessageController": { "unapprovedDecryptMsgCount": 0 },
"EncryptionPublicKeyController": {
"unapprovedEncryptionPublicKeyMsgCount": 0
},
"MetaMetricsController": {
"participateInMetaMetrics": true,
"metaMetricsId": "fake-metrics-id"
},
"NetworkController": {
"networkId": "1337", "networkId": "1337",
"providerConfig": { "providerConfig": {
"nickname": "Localhost 8545", "nickname": "Localhost 8545",
"ticker": "ETH", "ticker": "ETH",
"type": "rpc" "type": "rpc"
}, }
"isUnlocked": false, },
"useBlockie": false, "SignatureController": {
"useNonceField": false,
"usePhishDetect": true,
"featureFlags": { "showIncomingTransactions": true },
"currentLocale": "en",
"forgottenPassword": false,
"preferences": {
"hideZeroBalanceTokens": false,
"showFiatInTestnets": false,
"showTestNetworks": false,
"useNativeCurrencyAsPrimaryCurrency": true
},
"ipfsGateway": "dweb.link",
"participateInMetaMetrics": true,
"metaMetricsId": "fake-metrics-id",
"conversionDate": "number",
"conversionRate": 1700,
"nativeCurrency": "ETH",
"currentCurrency": "usd",
"alertEnabledness": { "unconnectedAccount": true, "web3ShimUsage": true },
"seedPhraseBackedUp": true,
"firstTimeFlowType": "import",
"completedOnboarding": true,
"incomingTxLastFetchedBlockByChainId": {
"0x1": null,
"0xe708": null,
"0x5": null,
"0xaa36a7": null,
"0xe704": null
},
"currentBlockGasLimit": "0x1c9c380",
"unapprovedDecryptMsgCount": 0,
"unapprovedEncryptionPublicKeyMsgCount": 0,
"unapprovedMsgCount": 0, "unapprovedMsgCount": 0,
"unapprovedPersonalMsgCount": 0, "unapprovedPersonalMsgCount": 0,
"unapprovedTypedMessagesCount": 0 "unapprovedTypedMessagesCount": 0