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: migrate from decommissioned ipfs gateway to new one #11985

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/components/Base/RemoteImage/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: jest
.fn()
.mockImplementation(() => 'https://gateway.pinata.cloud/ipfs/'),
.mockImplementation(() => 'https://dweb.link/ipfs/'),
}));

jest.mock('../../../components/hooks/useIpfsGateway', () => jest.fn());
Expand Down
4 changes: 2 additions & 2 deletions app/components/UI/AssetIcon/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const mockInitialState = {
...backgroundState,
PreferencesController: {
featureFlags: {},
ipfsGateway: 'https://gateway.pinata.cloud/ipfs/',
ipfsGateway: 'https://dweb.link/ipfs/',
lostIdentities: {},
selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3',
useTokenDetection: true,
Expand All @@ -25,7 +25,7 @@ const mockInitialState = {
_W: {
featureFlags: {},
frequentRpcList: [],
ipfsGateway: 'https://gateway.pinata.cloud/ipfs/',
ipfsGateway: 'https://dweb.link/ipfs/',
lostIdentities: {},
selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3',
useTokenDetection: true,
Expand Down
4 changes: 2 additions & 2 deletions app/components/UI/Swaps/components/TokenIcon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('TokenIcon component', () => {
const icon = shallow(
<TokenIcon
symbol="DAI"
icon="https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
icon="https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
/>,
);
expect(icon).toMatchSnapshot();
Expand All @@ -27,7 +27,7 @@ describe('TokenIcon component', () => {
<TokenIcon
medium
symbol="DAI"
icon="https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
icon="https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
/>,
);
expect(iconMedium).toMatchSnapshot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ describe('TokenSelectButton component', () => {
<TokenSelectButton
label="Select a token"
symbol="DAI"
icon="https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
icon="https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
/>,
);
expect(icon).toMatchSnapshot();
const onPress = shallow(
<TokenSelectButton
label="Select a token"
symbol="DAI"
icon="https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
icon="https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
onPress={dummyHandler}
/>,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ exports[`TokenIcon component should Render correctly 4`] = `
onError={[Function]}
source={
{
"uri": "https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ",
"uri": "https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ",
}
}
style={
Expand Down Expand Up @@ -146,7 +146,7 @@ exports[`TokenIcon component should Render correctly 8`] = `
onError={[Function]}
source={
{
"uri": "https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ",
"uri": "https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ",
}
}
style={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ exports[`TokenSelectButton component should Render correctly 4`] = `
}
>
<TokenIcon
icon="https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
icon="https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
symbol="DAI"
/>
</View>
Expand All @@ -95,7 +95,7 @@ exports[`TokenSelectButton component should Render correctly 5`] = `
}
>
<TokenIcon
icon="https://gateway.pinata.cloud/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
icon="https://dweb.link/ipfs/QmNYVMm3iC7HEoxfvxsZbRoapdjDHj9EREFac4BPeVphSJ"
symbol="DAI"
/>
</View>
Expand Down
2 changes: 1 addition & 1 deletion app/components/Views/confirmations/Send/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const initialState: DeepPartial<RootState> = {
},
PreferencesController: {
featureFlags: {},
ipfsGateway: 'https://gateway.pinata.cloud/ipfs/',
ipfsGateway: 'https://dweb.link/ipfs/',
lostIdentities: {},
selectedAddress: MOCK_ADDRESS_2,
useTokenDetection: true,
Expand Down
2 changes: 1 addition & 1 deletion app/constants/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const RPC = NetworkType.rpc;
export const NO_RPC_BLOCK_EXPLORER = 'NO_BLOCK_EXPLORER';
export const PRIVATENETWORK = 'PRIVATENETWORK';
export const DEFAULT_MAINNET_CUSTOM_NAME = 'Ethereum Main Custom';
export const IPFS_DEFAULT_GATEWAY_URL = 'https://gateway.pinata.cloud/ipfs/';
export const IPFS_DEFAULT_GATEWAY_URL = 'https://dweb.link/ipfs/';

/**
* @enum {string}
Expand Down
2 changes: 1 addition & 1 deletion app/core/AppConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default {
DEFAULT_SEARCH_ENGINE: 'DuckDuckGo',
TX_CHECK_BACKGROUND_FREQUENCY: 30000,
IPFS_OVERRIDE_PARAM: 'mm_override',
IPFS_DEFAULT_GATEWAY_URL: 'https://gateway.pinata.cloud/ipfs/',
IPFS_DEFAULT_GATEWAY_URL: 'https://dweb.link/ipfs/',
IPNS_DEFAULT_GATEWAY_URL: 'https://gateway.pinata.cloud/ipns/',
SWARM_DEFAULT_GATEWAY_URL: 'https://swarm-gateways.net/bzz:/',
supportedTLDs: ['eth', 'xyz', 'test'],
Expand Down
119 changes: 119 additions & 0 deletions app/store/migrations/056.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import migrate from './056';
import { merge } from 'lodash';
import { captureException } from '@sentry/react-native';
import initialRootState from '../../util/test/initial-root-state';
import mockedEngine from '../../core/__mocks__/MockedEngine';

jest.mock('@sentry/react-native', () => ({
captureException: jest.fn(),
}));
const mockedCaptureException = jest.mocked(captureException);

jest.mock('../../core/Engine', () => ({
init: () => mockedEngine.init(),
}));

describe('Migration #56', () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});

const invalidStates = [
{
state: null,
errorMessage: "FATAL ERROR: Migration 56: Invalid state error: 'object'",
scenario: 'state is invalid',
},
{
state: merge({}, initialRootState, {
engine: null,
}),
errorMessage:
"FATAL ERROR: Migration 56: Invalid engine state error: 'object'",
scenario: 'engine state is invalid',
},
{
state: merge({}, initialRootState, {
engine: {
backgroundState: null,
},
}),
errorMessage:
"FATAL ERROR: Migration 56: Invalid engine backgroundState error: 'object'",
scenario: 'backgroundState is invalid',
},
{
state: merge({}, initialRootState, {
engine: {
backgroundState: { PreferencesController: null },
},
}),
errorMessage:
"FATAL ERROR: Migration 56: Invalid PreferencesController state error: 'object'",
scenario: 'PreferencesController is invalid',
},
];

for (const { errorMessage, scenario, state } of invalidStates) {
it(`should capture exception if ${scenario}`, () => {
const newState = migrate(state);

expect(newState).toStrictEqual(state);
expect(mockedCaptureException).toHaveBeenCalledWith(expect.any(Error));
expect(mockedCaptureException.mock.calls[0][0].message).toBe(
errorMessage,
);
});
}

it('updates decomissioned ipfsGateway to new default', () => {
const oldState = {
engine: {
backgroundState: {
PreferencesController: {
ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/',
},
},
},
};

const expectedState = {
engine: {
backgroundState: {
PreferencesController: {
ipfsGateway: 'https://dweb.link/ipfs/',
},
},
},
};

const migratedState = migrate(oldState);
expect(migratedState).toStrictEqual(expectedState);
});

it('does not change ipfsGateway if not decomissioned', () => {
const oldState = {
engine: {
backgroundState: {
PreferencesController: {
ipfsGateway: 'https://ipfs.io/ipfs/',
},
},
},
};

const expectedState = {
engine: {
backgroundState: {
PreferencesController: {
ipfsGateway: 'https://ipfs.io/ipfs/',
},
},
},
};

const migratedState = migrate(oldState);
expect(migratedState).toStrictEqual(expectedState);
});
});
31 changes: 31 additions & 0 deletions app/store/migrations/056.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { captureException } from '@sentry/react-native';
import { isObject } from '@metamask/utils';
import { ensureValidState } from './util';

export default function migrate(state: unknown) {
if (!ensureValidState(state, 56)) {
// Increment the migration number as appropriate
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: I think we do not need this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just copied it from previous migration, looks useful reminder and not doing any arm...

return state;
}

const preferencesController =
state.engine.backgroundState.PreferencesController;

if (!isObject(preferencesController)) {
captureException(
new Error(
`FATAL ERROR: Migration 56: Invalid PreferencesController state error: '${typeof preferencesController}'`,
),
);
return state;
}

const decommisionedIpfsGateway = 'https://cloudflare-ipfs.com/ipfs/';
const newDefaultIpfsGateway = 'https://dweb.link/ipfs/';

if (decommisionedIpfsGateway === preferencesController?.ipfsGateway) {
preferencesController.ipfsGateway = newDefaultIpfsGateway;
}
// Return the modified state
Copy link
Contributor

@tommasini tommasini Oct 24, 2024

Choose a reason for hiding this comment

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

NIT: I think we can remove this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above

return state;
}
2 changes: 2 additions & 0 deletions app/store/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import migration52 from './052';
import migration53 from './053';
import migration54 from './054';
import migration55 from './055';
import migration56 from './056';

type MigrationFunction = (state: unknown) => unknown;
type AsyncMigrationFunction = (state: unknown) => Promise<unknown>;
Expand Down Expand Up @@ -124,6 +125,7 @@ export const migrationList: MigrationsList = {
53: migration53,
54: migration54,
55: migration55,
56: migration56,
};

// Enable both synchronous and asynchronous migrations
Expand Down
5 changes: 5 additions & 0 deletions app/util/ipfs-gateways.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@
"value": "https://gateway.pinata.cloud/ipfs/",
"key": 24,
"label": "https://gateway.pinata.cloud/ipfs/"
},
{
"value": "https://dweb.link/ipfs/",
"key": 30,
"label": "https://dweb.link/ipfs/"
}
]
2 changes: 1 addition & 1 deletion app/util/sentry/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ describe('captureSentryFeedback', () => {
name: 'Account 1',
},
},
ipfsGateway: 'https://gateway.pinata.cloud/ipfs/',
ipfsGateway: 'https://dweb.link/ipfs/',
isIpfsGatewayEnabled: true,
isMultiAccountBalancesEnabled: true,
lostIdentities: {},
Expand Down
2 changes: 1 addition & 1 deletion app/util/test/initial-background-state.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
"PreferencesController": {
"featureFlags": {},
"identities": {},
"ipfsGateway": "https://gateway.pinata.cloud/ipfs/",
"ipfsGateway": "https://dweb.link/ipfs/",
"lostIdentities": {},
"selectedAddress": "",
"useTokenDetection": true,
Expand Down
4 changes: 2 additions & 2 deletions e2e/fixtures/fixture-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ class FixtureBuilder {
importTime: 1684232000456,
},
},
ipfsGateway: 'https://gateway.pinata.cloud/ipfs/',
ipfsGateway: 'https://dweb.link/ipfs/',
lostIdentities: {},
selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3',
useTokenDetection: true,
Expand All @@ -298,7 +298,7 @@ class FixtureBuilder {
importTime: 1684232000456,
},
},
ipfsGateway: 'https://gateway.pinata.cloud/ipfs/',
ipfsGateway: 'https://dweb.link/ipfs/',
lostIdentities: {},
selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3',
useTokenDetection: true,
Expand Down
Loading