diff --git a/config/mainnet/config.json b/config/mainnet/config.json index 5c24fc71..8f9d40b7 100644 --- a/config/mainnet/config.json +++ b/config/mainnet/config.json @@ -8,7 +8,7 @@ "modes": ["ipc", "ws"], "port": 7887, "host": "127.0.0.1", - "allowedMethods": [] + "allowedMethods": ["generator", "system", "random"] }, "genesis": { "block": { diff --git a/config/testnet/config.json b/config/testnet/config.json index f71ef768..4560946b 100644 --- a/config/testnet/config.json +++ b/config/testnet/config.json @@ -8,7 +8,7 @@ "modes": ["ipc", "ws"], "port": 7887, "host": "127.0.0.1", - "allowedMethods": [] + "allowedMethods": ["generator", "system", "random"] }, "genesis": { "block": { diff --git a/docs/migration.md b/docs/migration.md index a17ef317..5d40f5e1 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -82,11 +82,11 @@ FLAGS -d, --lisk-core-v3-data-path=lisk-core-v3-data-path Path where the lisk-core v3.x instance is running. Current home directory will be considered the default if not provided. -h, --help Shows CLI help. -o, --output=output File path to write the genesis block json. If not provided, it will default to cwd/genesis_block.json. + -p, --page-size Maximum number of blocks to be iterated at once for computation. Default to 100000. -s, --snapshot-height=snapshot-height (Required) The height at which the re-genesis block will be generated. Can be specified with the SNAPSHOT_HEIGHT as well. -v, --version Shows the CLI version. --auto-migrate-config Migrate user configuration automatically. Default to false. --auto-start-lisk-core-v4 Start lisk-core v4 automatically. Default to false. - --snapshot-time-gap=snapshot-time-gap The number of seconds elapsed between the block at height HEIGHT_SNAPSHOT and the snapshot block. EXAMPLES lisk-migrator --snapshot-height 20931763 --lisk-core-path /path/to/data-dir diff --git a/package.json b/package.json index 61dfab65..45f3b5cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lisk-migrator", - "version": "2.0.0-rc.0", + "version": "2.0.0-rc.1", "description": "A command-line tool for migrating the blockchain state to the latest protocol after a hard fork", "author": "Lisk Foundation , lightcurve GmbH ", "license": "Apache-2.0", @@ -34,10 +34,10 @@ "test:ci": "jest --coverage=true --coverage-reporters=json --verbose", "test:watch": "npm test -- --watch", "copy-static-files": "copyfiles -u 1 src/**/*.sql ./dist", - "prebuild": "if test -d dist; then rm -r dist; fi; rm -f tsconfig.tsbuildinfo; rm -f npm-shrinkwrap.json", - "build": "tsc && npm run copy-static-files", - "prepack": "oclif-dev manifest && oclif-dev readme --multi --dir=docs/commands && git add README.md docs/commands && npm shrinkwrap", - "prepublishOnly": "yarn && npm run lint && npm run build" + "prebuild": "if test -d dist; then rm -r dist; fi; rm -f tsconfig.tsbuildinfo", + "build": "tsc && yarn copy-static-files", + "prepack": "oclif-dev manifest && oclif-dev readme --multi --dir=docs/commands", + "prepublishOnly": "yarn && yarn lint && yarn build" }, "oclif": { "commands": "./dist/commands", @@ -64,6 +64,7 @@ "@liskhq/lisk-codec": "0.4.0-rc.1", "@liskhq/lisk-cryptography": "4.0.0-rc.1", "@liskhq/lisk-db": "0.3.10", + "@liskhq/lisk-utils": "0.4.0-rc.0", "@liskhq/lisk-validator": "0.8.0-rc.1", "@oclif/command": "1.8.21", "@oclif/config": "1.14.0", diff --git a/src/assets/pos.ts b/src/assets/pos.ts index 6b64176b..8cfd2758 100644 --- a/src/assets/pos.ts +++ b/src/assets/pos.ts @@ -73,6 +73,7 @@ export const formatInt = (num: number | bigint): string => { export const getValidatorKeys = async ( accounts: Account[], db: Database, + pageSize: number, ): Promise> => { const delegateSet = new Set(); for (const account of accounts) { @@ -81,9 +82,8 @@ export const getValidatorKeys = async ( } } - const PAGE_SIZE = 100000; - const blockPublicKeySet = await getBlockPublicKeySet(db, PAGE_SIZE); - const txPublicKeySet = await getTransactionPublicKeySet(db, PAGE_SIZE); + const blockPublicKeySet = await getBlockPublicKeySet(db, pageSize); + const txPublicKeySet = await getTransactionPublicKeySet(db, pageSize); const keys: Record = {}; for (const key of blockPublicKeySet) { diff --git a/src/client.ts b/src/client.ts index 3f6643ec..f3dce12a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -15,10 +15,13 @@ import { createIPCClient, APIClient } from '@liskhq/lisk-api-client'; const clientCache: Record = {}; -export const getAPIClient = async (liskCorePath: string): Promise => { +export const getAPIClient = async ( + liskCorePath: string, + isForceInstantiate?: boolean, +): Promise => { let _client = clientCache[liskCorePath]; - if (!_client) { + if (!_client || isForceInstantiate) { _client = await createIPCClient(liskCorePath); clientCache[liskCorePath] = _client; } diff --git a/src/createAsset.ts b/src/createAsset.ts index 8bf930f0..3adfda21 100644 --- a/src/createAsset.ts +++ b/src/createAsset.ts @@ -75,7 +75,11 @@ export class CreateAsset { this._db = db; } - public init = async (snapshotHeight: number, tokenID: string): Promise => { + public init = async ( + snapshotHeight: number, + tokenID: string, + pageSize: number, + ): Promise => { const authSubstoreEntries: AuthStoreEntryBuffer[] = []; const userSubstoreEntries: UserSubstoreEntryBuffer[] = []; const supplySubstoreEntries: SupplySubstoreEntry[] = []; @@ -124,7 +128,7 @@ export class CreateAsset { ); // Get all validator keys for PoS module - const validatorKeys = await getValidatorKeys(accounts, this._db); + const validatorKeys = await getValidatorKeys(accounts, this._db, pageSize); for (const account of accounts) { // genesis asset for auth module diff --git a/src/events.ts b/src/events.ts index eb0b3435..9213c04b 100644 --- a/src/events.ts +++ b/src/events.ts @@ -30,18 +30,20 @@ export const captureForgingStatusAtSnapshotHeight = ( const newBlock = client.block.decode(Buffer.from(encodedBlock, 'hex')) as Block; if (newBlock.header.height === snapshotHeight) { const forgingStatus = await client.invoke('app:getForgingStatus'); - const forgingStatusJsonFilepath = resolve(outputDir, 'forgingStatus.json'); - try { - await write(forgingStatusJsonFilepath, JSON.stringify(forgingStatus, null, 2)); - _this.log(`Finished exporting forging status to ${forgingStatusJsonFilepath}.`); - } catch (error) { - _this.log( - `Unable to save the node Forging Status information to the disk, please find it below instead:\n${JSON.stringify( - forgingStatus, - null, - 2, - )}`, - ); + if (forgingStatus.length) { + const forgingStatusJsonFilepath = resolve(outputDir, 'forgingStatus.json'); + try { + await write(forgingStatusJsonFilepath, JSON.stringify(forgingStatus, null, 2)); + _this.log(`\nFinished exporting forging status to ${forgingStatusJsonFilepath}.`); + } catch (error) { + _this.log( + `\nUnable to save the node Forging Status information to the disk, please find it below instead:\n${JSON.stringify( + forgingStatus, + null, + 2, + )}`, + ); + } } } }); diff --git a/src/index.ts b/src/index.ts index 7f626584..85b935c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,7 @@ */ import util from 'util'; import * as fs from 'fs-extra'; -import { join } from 'path'; +import { join, resolve } from 'path'; import { ApplicationConfig, PartialApplicationConfig } from 'lisk-framework'; import { Database } from '@liskhq/lisk-db'; import * as semver from 'semver'; @@ -26,7 +26,6 @@ import { SNAPSHOT_DIR, MIN_SUPPORTED_LISK_CORE_VERSION, DEFAULT_LISK_CORE_PATH, - LEGACY_DB_PATH, } from './constants'; import { getAPIClient } from './client'; import { @@ -44,11 +43,12 @@ import { setPrevSnapshotBlockHeightByNetID, } from './utils/chain'; import { captureForgingStatusAtSnapshotHeight } from './events'; -import { createGenesisBlock, writeGenesisAssets } from './utils/genesis_block'; +import { copyGenesisBlock, createGenesisBlock, writeGenesisAssets } from './utils/genesis_block'; import { CreateAsset } from './createAsset'; import { ApplicationConfigV3, NetworkConfigLocal, NodeInfo } from './types'; import { installLiskCore, startLiskCore } from './utils/node'; -import { copyDir, resolveAbsolutePath } from './utils/fs'; +import { resolveAbsolutePath } from './utils/fs'; +import { execAsync } from './utils/process'; let configCoreV4: PartialApplicationConfig; class LiskMigrator extends Command { @@ -69,12 +69,12 @@ class LiskMigrator extends Command { char: 'd', required: false, description: - 'Path where the lisk-core v3.x instance is running. The current home directory will be considered the default directory if not otherwise provided.', + 'Path where the Lisk Core v3.x instance is running. When not supplied, defaults to the default data directory for Lisk Core.', }), config: flagsParser.string({ char: 'c', required: false, - description: 'Custom configuration file path.', + description: 'Custom configuration file path for Lisk Core v3.x.', }), 'snapshot-height': flagsParser.integer({ char: 's', @@ -95,6 +95,13 @@ class LiskMigrator extends Command { description: 'Start lisk core v4 automatically. Default to false.', default: false, }), + 'page-size': flagsParser.integer({ + char: 'p', + required: false, + default: 100000, + description: + 'Maximum number of blocks to be iterated at once for computation. Default to 100000.', + }), }; public async run(): Promise { @@ -108,6 +115,7 @@ class LiskMigrator extends Command { const customConfigPath = flags.config; const autoMigrateUserConfig = flags['auto-migrate-config'] ?? false; const autoStartLiskCoreV4 = flags['auto-start-lisk-core-v4']; + const pageSize = Number(flags['page-size']); const client = await getAPIClient(liskCoreV3DataPath); const nodeInfo = (await client.node.getNodeInfo()) as NodeInfo; @@ -156,7 +164,7 @@ class LiskMigrator extends Command { `Lisk Migrator utility is not compatible for lisk-core version ${liskCoreVersion.version}. The minimum compatible version is: ${MIN_SUPPORTED_LISK_CORE_VERSION}.`, ); } - cli.action.stop(`${liskCoreVersion.version} detected`); + cli.action.stop(`${appVersion} detected`); // User specified custom config file const configV3: ApplicationConfigV3 = customConfigPath @@ -184,7 +192,7 @@ class LiskMigrator extends Command { cli.action.start('Creating genesis assets'); const createAsset = new CreateAsset(db); const tokenID = getTokenIDLsk(); - const genesisAssets = await createAsset.init(snapshotHeight, tokenID); + const genesisAssets = await createAsset.init(snapshotHeight, tokenID, pageSize); cli.action.stop(); // Create an app instance for creating genesis block @@ -244,19 +252,34 @@ class LiskMigrator extends Command { configCoreV4 = defaultConfigV4; } - cli.action.start(`Creating legacy.db at ${LEGACY_DB_PATH}`); - await copyDir(snapshotDirPath, resolveAbsolutePath(LEGACY_DB_PATH)); - this.log(`Legacy database has been created at ${LEGACY_DB_PATH}`); + cli.action.start('Copying genesis block to the Lisk Core executable directory'); + const liskCoreExecPath = await execAsync('which lisk-core'); + const liskCoreV4ConfigPath = resolve( + liskCoreExecPath, + '../..', + `lib/node_modules/lisk-core/config/${networkConstant.name}`, + ); + + await copyGenesisBlock( + `${outputDir}/genesis_block.blob`, + `${liskCoreV4ConfigPath}/genesis_block.blob`, + ); + this.log(`Genesis block has been copied to: ${liskCoreV4ConfigPath}.`); cli.action.stop(); // Ask user to manually stop Lisk Core v3 and continue - const isLiskCoreV3Stopped = await cli.confirm(` - Please stop Lisk Core v3 to continue. Type 'yes' and press Enter when ready. [yes/no]`); + const isLiskCoreV3Stopped = await cli.confirm( + "Please stop Lisk Core v3 to continue. Type 'yes' and press Enter when ready. [yes/no]", + ); if (isLiskCoreV3Stopped) { - const isUserConfirmed = await cli.confirm(` - Start Lisk Core with the following configuration? [yes/no] \n - ${util.inspect(configCoreV4, false, 3)}`); + const isUserConfirmed = await cli.confirm( + `Start Lisk Core with the following configuration? [yes/no] \n${util.inspect( + configCoreV4, + false, + 3, + )}`, + ); if (isUserConfirmed) { cli.action.start('Starting lisk-core v4'); @@ -280,8 +303,9 @@ class LiskMigrator extends Command { } } else { this.log( - `Please copy ${snapshotDirPath} directory to the Lisk Core V4 data directory in order to access legacy blockchain information`, + `Please copy ${snapshotDirPath} directory to the Lisk Core V4 data directory in order to access legacy blockchain information.`, ); + this.log('Please copy genesis block to the Lisk Core V4 network directory.'); } } catch (error) { this.error(error as string); diff --git a/src/utils/config.ts b/src/utils/config.ts index 3891d7a9..c86c70b2 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -20,6 +20,7 @@ import { join, resolve } from 'path'; import { validator } from '@liskhq/lisk-validator'; import { ApplicationConfig, applicationConfigSchema } from 'lisk-framework'; +import { objects } from '@liskhq/lisk-utils'; import { ApplicationConfigV3, LoggerConfig } from '../types'; import { DEFAULT_VERSION, @@ -134,17 +135,6 @@ export const migrateUserConfig = async ( cli.action.stop(); } - // TODO: Verify if needed - if (configV3.backup?.height) { - cli.action.start( - `Setting config property 'system.backup.height' to: ${configV3.backup.height}.`, - ); - configV4.system.backup = { - height: Math.max(snapshotHeight + 1, configV3.backup.height + 1), - }; - cli.action.stop(); - } - if (configV3.transactionPool) { if (configV3.transactionPool.maxTransactions) { cli.action.start( @@ -267,7 +257,8 @@ export const migrateUserConfig = async ( export const validateConfig = async (config: ApplicationConfig): Promise => { try { - (await validator.validate(applicationConfigSchema, config)) as unknown; + const mergedConfig = objects.mergeDeep({}, applicationConfigSchema.default, config); + (await validator.validate(applicationConfigSchema, mergedConfig)) as unknown; return true; } catch (error) { return false; diff --git a/src/utils/fs.ts b/src/utils/fs.ts index 7d89dd4e..33dcf114 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -77,3 +77,13 @@ export const write = async (filePath: string, content: string): Promise => + new Promise((resolve, reject) => { + fs.copyFile(src, dest, err => { + if (err) { + return reject(err); + } + return resolve(true); + }); + }); diff --git a/src/utils/genesis_block.ts b/src/utils/genesis_block.ts index b3cd3974..6ae6e9f4 100644 --- a/src/utils/genesis_block.ts +++ b/src/utils/genesis_block.ts @@ -18,6 +18,7 @@ import { Block as BlockVersion3 } from '@liskhq/lisk-chain'; import { SNAPSHOT_TIME_GAP } from '../constants'; import { GenesisAssetEntry } from '../types'; import { execAsync } from './process'; +import { copyFile } from './fs'; (BigInt.prototype as any).toJSON = function () { return this.toString(); @@ -72,3 +73,8 @@ export const writeGenesisAssets = async ( JSON.stringify({ assets: genesisAssets }, null, '\t'), ); }; + +export const copyGenesisBlock = async ( + currGenesisBlockFilepath: string, + liskCoreV4ConfigPath: string, +): Promise => copyFile(currGenesisBlockFilepath, liskCoreV4ConfigPath); diff --git a/src/utils/node.ts b/src/utils/node.ts index 12292d5c..b3a62fca 100644 --- a/src/utils/node.ts +++ b/src/utils/node.ts @@ -11,8 +11,8 @@ * * Removal or modification of this copyright notice is prohibited. */ -import { resolve } from 'path'; import * as fs from 'fs-extra'; +import * as path from 'path'; import { homedir } from 'os'; import { Command } from '@oclif/command'; import { existsSync, renameSync } from 'fs-extra'; @@ -22,12 +22,13 @@ import { execAsync } from './process'; import { isPortAvailable } from './network'; import { Port } from '../types'; import { getAPIClient } from '../client'; -import { DEFAULT_PORT_P2P, DEFAULT_PORT_RPC } from '../constants'; +import { DEFAULT_PORT_P2P, DEFAULT_PORT_RPC, LEGACY_DB_PATH, SNAPSHOT_DIR } from '../constants'; +import { copyDir, exists, resolveAbsolutePath } from './fs'; const INSTALL_LISK_CORE_COMMAND = 'npm i -g lisk-core@^4.0.0-rc.1'; const INSTALL_PM2_COMMAND = 'npm i -g pm2'; const PM2_FILE_NAME = 'pm2.migrator.config.json'; -const START_PM2_COMMAND = `pm2 start ${PM2_FILE_NAME}`; +const PM2_COMMAND_START = `pm2 start ${PM2_FILE_NAME}`; const DEFAULT_LISK_DATA_DIR = `${homedir()}/.lisk/lisk-core`; const LISK_V3_BACKUP_DATA_DIR = `${homedir()}/.lisk/lisk-core-v3`; @@ -38,7 +39,7 @@ export const installPM2 = async (): Promise => execAsync(INSTALL_PM2_COM export const isLiskCoreV3Running = async (liskCorePath: string): Promise => { try { - const client = await getAPIClient(liskCorePath); + const client = await getAPIClient(liskCorePath, true); const nodeInfo = await client.node.getNodeInfo(); return !!nodeInfo; } catch (_) { @@ -54,13 +55,27 @@ const backupDefaultDirectoryIfExists = async (_this: Command) => { } }; +const copyLegacyDB = async (_this: Command) => { + _this.log(`Copying the v3.x snapshot to legacy.db at ${LEGACY_DB_PATH}`); + await copyDir( + path.resolve(LISK_V3_BACKUP_DATA_DIR, SNAPSHOT_DIR), + resolveAbsolutePath(LEGACY_DB_PATH), + ); + _this.log(`Legacy database for Lisk Core v4 has been created at ${LEGACY_DB_PATH}`); +}; + +const getFinalConfigPath = async (outputDir: string, network: string) => + (await exists(`${outputDir}/config.json`)) + ? outputDir + : path.resolve(__dirname, '../..', 'config', network); + export const startLiskCore = async ( _this: Command, liskCoreV3DataPath: string, _config: PartialApplicationConfig, network: string, outputDir: string, -): Promise => { +): Promise => { const isCoreV3Running = await isLiskCoreV3Running(liskCoreV3DataPath); if (isCoreV3Running) throw new Error('Lisk Core v3 is still running.'); @@ -75,41 +90,26 @@ export const startLiskCore = async ( } await backupDefaultDirectoryIfExists(_this); + await copyLegacyDB(_this); _this.log('Installing pm2...'); await installPM2(); _this.log('Finished installing pm2.'); - const customConfigFilepath = resolve(outputDir, 'custom_config.json'); - - fs.writeFileSync( - customConfigFilepath, - JSON.stringify( - { - ..._config, - genesis: { - ..._config.genesis, - block: { - fromFile: `${outputDir}/genesis_block.blob`, - }, - }, - }, - null, - '\t', - ), - ); - + _this.log(`Creating PM2 config at ${process.cwd()}/${PM2_FILE_NAME}`); + const configPath = await getFinalConfigPath(outputDir, network); fs.writeFileSync( PM2_FILE_NAME, JSON.stringify( { - name: 'lisk-core', - script: `lisk-core start --network ${network} --config ${customConfigFilepath}`, + name: 'lisk-core-v4', + script: `lisk-core start --network ${network} --config ${configPath}/config.json`, }, null, '\t', ), ); + _this.log(`Successfully created the PM2 config at ${process.cwd()}/${PM2_FILE_NAME}`); - return execAsync(START_PM2_COMMAND); + _this.log(await execAsync(PM2_COMMAND_START)); }; diff --git a/test/unit/assets/pos.spec.ts b/test/unit/assets/pos.spec.ts index 732bc3b4..08f98491 100644 --- a/test/unit/assets/pos.spec.ts +++ b/test/unit/assets/pos.spec.ts @@ -55,7 +55,7 @@ describe('Build assets/pos', () => { let blockIDs: string[]; let delegates: VoteWeightsWrapper; const snapshotHeight = 10815; - const prevSnapshotBlockHeight = 5000; + const pageSize = 1000; beforeAll(async () => { db = new Database('testDB'); @@ -174,12 +174,7 @@ describe('Build assets/pos', () => { const { getValidatorKeys } = require('../../../src/assets/pos'); - const validatorKeys = await getValidatorKeys( - accounts, - snapshotHeight, - prevSnapshotBlockHeight, - db, - ); + const validatorKeys = await getValidatorKeys(accounts, db, pageSize); const validator = (await createValidatorsArrayEntry( accounts[0], @@ -261,9 +256,7 @@ describe('Build assets/pos', () => { const { getValidatorKeys } = require('../../../src/assets/pos'); - await expect( - getValidatorKeys(accounts, snapshotHeight, prevSnapshotBlockHeight, db), - ).rejects.toThrow(); + await expect(getValidatorKeys(accounts, db, pageSize)).rejects.toThrow(); }); it('should create PoS module asset', async () => { @@ -289,12 +282,7 @@ describe('Build assets/pos', () => { const { getValidatorKeys } = require('../../../src/assets/pos'); - const validatorKeys = await getValidatorKeys( - accounts, - snapshotHeight, - prevSnapshotBlockHeight, - db, - ); + const validatorKeys = await getValidatorKeys(accounts, db, pageSize); const validator = (await createValidatorsArrayEntry( accounts[0], diff --git a/test/unit/createAsset.spec.ts b/test/unit/createAsset.spec.ts index de55001f..b09330d4 100644 --- a/test/unit/createAsset.spec.ts +++ b/test/unit/createAsset.spec.ts @@ -69,6 +69,7 @@ describe('Build assets/legacy', () => { const snapshotHeight = 10815; const prevSnapshotBlockHeight = 5000; const tokenID = '0400000000000000'; + const pageSize = 1000; interface Accounts { [key: string]: { @@ -231,7 +232,7 @@ describe('Build assets/legacy', () => { .calledWith(Buffer.from(`${DB_KEY_CHAIN_STATE}:${CHAIN_STATE_DELEGATE_VOTE_WEIGHTS}`)) .mockResolvedValue(encodedVoteWeights as never); - const response = await createAsset.init(snapshotHeight, tokenID); + const response = await createAsset.init(snapshotHeight, tokenID, pageSize); const moduleList = [ MODULE_NAME_LEGACY, @@ -257,7 +258,7 @@ describe('Build assets/legacy', () => { }) .mockReturnValue(undefined); - await expect(createAsset.init(snapshotHeight, tokenID)).rejects.toThrow(); + await expect(createAsset.init(snapshotHeight, tokenID, pageSize)).rejects.toThrow(); }); it('should throw error when block stream is undefined', async () => { @@ -268,7 +269,7 @@ describe('Build assets/legacy', () => { }) .mockReturnValue(undefined); - await expect(createAsset.init(snapshotHeight, tokenID)).rejects.toThrow(); + await expect(createAsset.init(snapshotHeight, tokenID, pageSize)).rejects.toThrow(); }); it('should throw error when creating stream with invalid file path', async () => { @@ -285,7 +286,7 @@ describe('Build assets/legacy', () => { }) .mockReturnValue(createReadStream('test.txt') as never); - await expect(createAsset.init(snapshotHeight, tokenID)).rejects.toThrow(); + await expect(createAsset.init(snapshotHeight, tokenID, pageSize)).rejects.toThrow(); }); }); }); diff --git a/test/unit/utils/config.spec.ts b/test/unit/utils/config.spec.ts index c0aa6f16..6e92807e 100644 --- a/test/unit/utils/config.spec.ts +++ b/test/unit/utils/config.spec.ts @@ -51,8 +51,9 @@ describe('Migrate user configuration', () => { }); it('should return false when user configuration is invalid', async () => { - const { system, ...invalidConfig } = configV4; - const isValidConfig = await validateConfig((invalidConfig as unknown) as ApplicationConfig); + const isValidConfig = await validateConfig(({ + invalid: 'invalid', + } as unknown) as ApplicationConfig); expect(isValidConfig).toBe(false); }); }); diff --git a/test/unit/utils/fs.spec.ts b/test/unit/utils/fs.spec.ts index b7478c83..be6d3cc2 100644 --- a/test/unit/utils/fs.spec.ts +++ b/test/unit/utils/fs.spec.ts @@ -22,6 +22,7 @@ import { resolveAbsolutePath, copyDir, write, + copyFile, } from '../../../src/utils/fs'; import { configV3 } from '../fixtures/config'; @@ -109,3 +110,19 @@ describe('Test write method', () => { await expect(write('', '')).rejects.toThrow(); }); }); + +describe('Test copyFile method', () => { + it('should copy file', async () => { + const srcPath = join(__dirname, '../fixtures/blockchain.db.tar.gz'); + const destPath = `${testDir}/blockchain.db.tar.gz`; + expect(await exists(destPath)).toBe(false); + + await copyFile(srcPath, destPath); + + expect(await exists(destPath)).toBe(true); + }); + + it('should throw when called with empty string', async () => { + await expect(copyFile('', '')).rejects.toThrow(); + }); +}); diff --git a/yarn.lock b/yarn.lock index 47aea436..0f24fab6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -805,6 +805,13 @@ "@liskhq/lisk-cryptography" "^4.0.0-rc.1" "@liskhq/lisk-utils" "^0.4.0-rc.0" +"@liskhq/lisk-utils@0.4.0-rc.0", "@liskhq/lisk-utils@^0.4.0-rc.0": + version "0.4.0-rc.0" + resolved "https://registry.yarnpkg.com/@liskhq/lisk-utils/-/lisk-utils-0.4.0-rc.0.tgz#6337e1d7e8ca766bbc7ea07408eaf34b2e51bd93" + integrity sha512-oMTRwkWWBNqZABXQTFxvo6L/vyAeYka9MlNGNZE7ObaAO8JjxHRukQDZAX36+NcrvREsp1yNQF8YW1xFs+XIvg== + dependencies: + lodash.clonedeep "4.5.0" + "@liskhq/lisk-utils@^0.3.0-rc.0": version "0.3.0-rc.0" resolved "https://registry.yarnpkg.com/@liskhq/lisk-utils/-/lisk-utils-0.3.0-rc.0.tgz#64af69f4f629fcd14dd5e56fcbb51160c6698ce2" @@ -812,13 +819,6 @@ dependencies: lodash.clonedeep "4.5.0" -"@liskhq/lisk-utils@^0.4.0-rc.0": - version "0.4.0-rc.0" - resolved "https://registry.yarnpkg.com/@liskhq/lisk-utils/-/lisk-utils-0.4.0-rc.0.tgz#6337e1d7e8ca766bbc7ea07408eaf34b2e51bd93" - integrity sha512-oMTRwkWWBNqZABXQTFxvo6L/vyAeYka9MlNGNZE7ObaAO8JjxHRukQDZAX36+NcrvREsp1yNQF8YW1xFs+XIvg== - dependencies: - lodash.clonedeep "4.5.0" - "@liskhq/lisk-validator@0.8.0-rc.1", "@liskhq/lisk-validator@^0.8.0-rc.1": version "0.8.0-rc.1" resolved "https://registry.yarnpkg.com/@liskhq/lisk-validator/-/lisk-validator-0.8.0-rc.1.tgz#f723bd1667e61c4c5bec680343342c1f8191ff67"