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

feat: throw/warn on mismatch of client version and supported version #1287

Merged
merged 23 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
26dd6f0
feat: throw/warn on node and supported version mismatch
nedsalk Sep 22, 2023
62f3b01
refactor: move to method
nedsalk Sep 22, 2023
0997b65
refactor: rename method
nedsalk Sep 22, 2023
4059711
refactor: rename variables
nedsalk Sep 22, 2023
0425061
refactor: rename method
nedsalk Sep 22, 2023
0867de4
chore: changeset
nedsalk Sep 22, 2023
a62f27e
Merge branch 'master' into ns/feat/check-node-version-in-provider
nedsalk Sep 22, 2023
0281648
Merge branch 'master' into ns/feat/check-node-version-in-provider
nedsalk Sep 22, 2023
05967bf
refactor: move semver logic into versions package
nedsalk Sep 25, 2023
1b98639
Merge remote-tracking branch 'origin/master' into ns/feat/check-node-…
nedsalk Sep 26, 2023
4d13e3d
refactor: use isMajorSupported pattern
nedsalk Sep 26, 2023
87cf8ea
test: rename
nedsalk Sep 26, 2023
c3c1f84
Update packages/versions/src/lib/checkFuelCoreVersionCompatibility.ts
nedsalk Sep 27, 2023
25ecd96
Update packages/versions/src/lib/checkFuelCoreVersionCompatibility.te…
nedsalk Sep 27, 2023
64113b9
fix: changes to property name
nedsalk Sep 27, 2023
52bf4cc
Merge remote-tracking branch 'origin/master' into ns/feat/check-node-…
nedsalk Sep 27, 2023
f3819e9
fix: merge issues
nedsalk Sep 27, 2023
1d58531
Update packages/versions/src/lib/checkFuelCoreVersionCompatibility.te…
nedsalk Sep 27, 2023
6f38fa9
Merge branch 'master' into ns/feat/check-node-version-in-provider
nedsalk Sep 28, 2023
b386577
Merge branch 'master' into ns/feat/check-node-version-in-provider
nedsalk Sep 28, 2023
80f2b31
checkpoint
nedsalk Sep 29, 2023
c69983a
Merge remote-tracking branch 'origin/master' into ns/feat/check-node-…
nedsalk Oct 3, 2023
e1b789a
test: fix mock assumptions
nedsalk Oct 3, 2023
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
6 changes: 6 additions & 0 deletions .changeset/fresh-mails-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/errors": minor
"@fuel-ts/providers": minor
---

Check mismatch of fuel client version and supported version: throw on major/minor mismatch, warn on patch mismatch
1 change: 1 addition & 0 deletions packages/errors/src/error-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export enum ErrorCode {
// chain
LATEST_BLOCK_UNAVAILABLE = 'latest-block-unavailable',
ERROR_BUILDING_BLOCK_EXPLORER_URL = 'error-building-block-explorer-url',
UNSUPPORTED_FUEL_CLIENT_VERSION = 'unsupported-fuel-client-version',

// docs
VITEPRESS_PLUGIN_ERROR = 'vitepress-plugin-error',
Expand Down
1 change: 1 addition & 0 deletions packages/providers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@fuel-ts/interfaces": "workspace:*",
"@fuel-ts/math": "workspace:*",
"@fuel-ts/transactions": "workspace:*",
"@fuel-ts/versions": "workspace:*",
"graphql": "^16.6.0",
"graphql-request": "^5.0.0",
"graphql-tag": "^2.12.6",
Expand Down
23 changes: 23 additions & 0 deletions packages/providers/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
InputMessageCoder,
TransactionCoder,
} from '@fuel-ts/transactions';
import { checkFuelCoreVersionCompatibility } from '@fuel-ts/versions';
import { print } from 'graphql';
import { GraphQLClient } from 'graphql-request';
import type { Client } from 'graphql-sse';
Expand Down Expand Up @@ -346,12 +347,34 @@ export default class Provider {
const chain = await this.fetchChain();
const nodeInfo = await this.fetchNode();

Provider.ensureClientVersionIsSupported(nodeInfo);

return {
chain,
nodeInfo,
};
}

private static ensureClientVersionIsSupported(nodeInfo: NodeInfo) {
const { isMajorSupported, isMinorSupported, isPatchSupported, supportedVersion } =
checkFuelCoreVersionCompatibility(nodeInfo.nodeVersion);

if (!isMajorSupported || !isMinorSupported) {
throw new FuelError(
FuelError.CODES.UNSUPPORTED_FUEL_CLIENT_VERSION,
`Fuel client version: ${nodeInfo.nodeVersion}, Supported version: ${supportedVersion}`
);
}

if (!isPatchSupported) {
// eslint-disable-next-line no-console
console.warn(
FuelError.CODES.UNSUPPORTED_FUEL_CLIENT_VERSION,
`The patch versions of the client and sdk differ. Fuel client version: ${nodeInfo.nodeVersion}, Supported version: ${supportedVersion}`
);
}
}

/**
* Create GraphQL client and set operations.
*
Expand Down
79 changes: 79 additions & 0 deletions packages/providers/test/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { expectToThrowFuelError, safeExec } from '@fuel-ts/errors/test-utils';
import { BN, bn } from '@fuel-ts/math';
import type { Receipt } from '@fuel-ts/transactions';
import { InputType, ReceiptType, TransactionType } from '@fuel-ts/transactions';
import * as fuelTsVersionsMod from '@fuel-ts/versions';
import { versions } from '@fuel-ts/versions';

import type { FetchRequestOptions } from '../src/provider';
import Provider from '../src/provider';
Expand All @@ -20,6 +22,12 @@ import { fromTai64ToUnix, fromUnixToTai64 } from '../src/utils';

import { messageProofResponse, messageStatusResponse } from './fixtures';

// https://stackoverflow.com/a/72885576
jest.mock('@fuel-ts/versions', () => ({
__esModule: true,
...jest.requireActual('@fuel-ts/versions'),
}));

afterEach(() => {
jest.restoreAllMocks();
});
Expand Down Expand Up @@ -812,4 +820,75 @@ describe('Provider', () => {
)
);
});

it('throws on difference between major client version and supported major version', async () => {
const { FUEL_CORE } = versions;
const [major, minor, patch] = FUEL_CORE.split('.');
const majorMismatch = major === '0' ? 1 : parseInt(patch, 10) - 1;

const mock = {
isMajorSupported: false,
isMinorSupported: true,
isPatchSupported: true,
supportedVersion: `${majorMismatch}.${minor}.${patch}`,
};

if (mock.supportedVersion === FUEL_CORE) throw new Error();

const spy = jest.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility');
spy.mockImplementationOnce(() => mock);

await expectToThrowFuelError(() => Provider.create(FUEL_NETWORK_URL), {
code: ErrorCode.UNSUPPORTED_FUEL_CLIENT_VERSION,
message: `Fuel client version: ${FUEL_CORE}, Supported version: ${mock.supportedVersion}`,
});
});

it('throws on difference between minor client version and supported minor version', async () => {
const { FUEL_CORE } = versions;
const [major, minor, patch] = FUEL_CORE.split('.');
const minorMismatch = minor === '0' ? 1 : parseInt(patch, 10) - 1;

const mock = {
isMajorSupported: true,
isMinorSupported: false,
isPatchSupported: true,
supportedVersion: `${major}.${minorMismatch}.${patch}`,
};

if (mock.supportedVersion === FUEL_CORE) throw new Error();

const spy = jest.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility');
spy.mockImplementationOnce(() => mock);

await expectToThrowFuelError(() => Provider.create(FUEL_NETWORK_URL), {
code: ErrorCode.UNSUPPORTED_FUEL_CLIENT_VERSION,
message: `Fuel client version: ${FUEL_CORE}, Supported version: ${mock.supportedVersion}`,
});
});

it('warns on difference between patch client version and supported patch version', async () => {
const { FUEL_CORE } = versions;
const [major, minor, patch] = FUEL_CORE.split('.');

const patchMismatch = patch === '0' ? 1 : parseInt(patch, 10) - 1;
const mock = {
isMajorSupported: true,
isMinorSupported: true,
isPatchSupported: false,
supportedVersion: `${major}.${minor}.${patchMismatch}`,
};
if (mock.supportedVersion === FUEL_CORE) throw new Error();

const spy = jest.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility');
spy.mockImplementation(() => mock);

const warnSpy = jest.spyOn(global.console, 'warn');
await Provider.create(FUEL_NETWORK_URL);

expect(warnSpy).toHaveBeenCalledWith(
ErrorCode.UNSUPPORTED_FUEL_CLIENT_VERSION,
`The patch versions of the client and sdk differ. Fuel client version: ${FUEL_CORE}, Supported version: ${mock.supportedVersion}`
);
});
});
1 change: 1 addition & 0 deletions packages/versions/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import * as indexMod from './index';
describe('index.js', () => {
test('should export versions constant', () => {
expect(indexMod.versions).toBeTruthy();
expect(indexMod.checkFuelCoreVersionCompatibility).toBeTruthy();
});
});
1 change: 1 addition & 0 deletions packages/versions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@
import { getSupportedVersions } from './lib/getSupportedVersions';

export const versions = getSupportedVersions();
export * from './lib/checkFuelCoreVersionCompatibility';
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { checkFuelCoreVersionCompatibility } from './checkFuelCoreVersionCompatibility';
import * as getSupportedVersionsMod from './getSupportedVersions';

describe('getDifferenceToUserFuelCoreVersion', () => {
afterAll(() => jest.restoreAllMocks());

it('should validate all possible version mismatches', () => {
const supportedVersion = '0.1.2';

jest.spyOn(getSupportedVersionsMod, 'getSupportedVersions').mockImplementation(() => ({
FUELS: '1', // not under test
FORC: '1', // not under test
FUEL_CORE: supportedVersion,
}));

expect(checkFuelCoreVersionCompatibility('1.1.2')).toEqual({
isMajorSupported: false,
isMinorSupported: true,
isPatchSupported: true,
supportedVersion,
});

expect(checkFuelCoreVersionCompatibility('1.2.2')).toEqual({
isMajorSupported: false,
isMinorSupported: false,
isPatchSupported: true,
supportedVersion,
});

expect(checkFuelCoreVersionCompatibility('1.1.3')).toEqual({
isMajorSupported: false,
isMinorSupported: true,
isPatchSupported: false,
supportedVersion,
});

expect(checkFuelCoreVersionCompatibility('0.2.2')).toEqual({
isMajorSupported: true,
isMinorSupported: false,
isPatchSupported: true,
supportedVersion,
});

expect(checkFuelCoreVersionCompatibility('0.2.3')).toEqual({
isMajorSupported: true,
isMinorSupported: false,
isPatchSupported: false,
supportedVersion,
});

expect(checkFuelCoreVersionCompatibility('0.1.3')).toEqual({
isMajorSupported: true,
isMinorSupported: true,
isPatchSupported: false,
supportedVersion,
});

expect(checkFuelCoreVersionCompatibility('0.1.2')).toEqual({
isMajorSupported: true,
isMinorSupported: true,
isPatchSupported: true,
supportedVersion,
});
});
});
22 changes: 22 additions & 0 deletions packages/versions/src/lib/checkFuelCoreVersionCompatibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import semver from 'semver';

import { getSupportedVersions } from './getSupportedVersions';

export function checkFuelCoreVersionCompatibility(networkVersion: string) {
const { FUEL_CORE: supportedVersion } = getSupportedVersions();

const networkMajor = semver.major(networkVersion);
const networkMinor = semver.minor(networkVersion);
const networkPatch = semver.patch(networkVersion);

const supportedMajor = semver.major(supportedVersion);
const supportedMinor = semver.minor(supportedVersion);
const supportedPatch = semver.patch(supportedVersion);

return {
supportedVersion,
isMajorSupported: networkMajor === supportedMajor,
isMinorSupported: networkMinor === supportedMinor,
isPatchSupported: networkPatch === supportedPatch,
};
}
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading