diff --git a/src/Directory.ts b/src/Directory.ts index 99d50e8f..2ca5d3a2 100644 --- a/src/Directory.ts +++ b/src/Directory.ts @@ -44,8 +44,7 @@ export default class Directory { } return path.join( - Directory.projectRoot, - 'chains', + Directory.getProjectChainsDirectory, originChain, auxiliaryChainId, ); @@ -162,6 +161,16 @@ export default class Directory { ); } + /** + * This method return project chains directory. + */ + public static get getProjectChainsDirectory(): string { + return path.join( + Directory.projectRoot, + 'chains', + ); + } + /** * Returns the full path of GatewayConfig for a given origin, auxiliary and gatewayAddress. * diff --git a/src/NewChain/AuxiliaryChainInteract.ts b/src/NewChain/AuxiliaryChainInteract.ts index baeffa97..76065b39 100644 --- a/src/NewChain/AuxiliaryChainInteract.ts +++ b/src/NewChain/AuxiliaryChainInteract.ts @@ -66,10 +66,10 @@ export default class AuxiliaryChainInteract { constructor( private initConfig: InitConfig, private chainId: string, - private originChainId: string, + private originChain: string, private nodeDescription: NodeDescription, ) { - this.chainDir = path.join(nodeDescription.mosaicDir, originChainId, this.chainId); + this.chainDir = path.join(nodeDescription.mosaicDir, originChain, this.chainId); } /** @@ -163,6 +163,8 @@ export default class AuxiliaryChainInteract { * lock for the origin stake. * @param proofData The proof data of the origin stake. Will be used to proof the stake against an * available origin state root on auxiliary. + * @param originChainId Chain ID of origin chain is used to set remote chain Id in anchor. + * It's required because originChain in the constructor can be a string like ropsten. */ public async initializeContracts( originOstGatewayAddress: string, @@ -171,6 +173,7 @@ export default class AuxiliaryChainInteract { stakeMessageNonce: string, hashLockSecret: string, proofData: Proof, + originChainId?: string, ): Promise<{ anchorOrganization: ContractInteract.Organization; anchor: ContractInteract.Anchor; @@ -194,6 +197,7 @@ export default class AuxiliaryChainInteract { originOstGatewayAddress, originHeight, originStateRoot, + originChainId, ); await this.transferAllOstIntoOstPrime(ostPrime.address); @@ -482,6 +486,7 @@ export default class AuxiliaryChainInteract { originOstGatewayAddress: string, originHeight: string, originStateRoot: string, + originChainId?: string, ): Promise<{ anchorOrganization: ContractInteract.Organization; anchor: ContractInteract.Anchor; @@ -503,7 +508,7 @@ export default class AuxiliaryChainInteract { this.anchorOrganizationDeploymentNonce, ); const anchor = await this.deployAnchor( - this.originChainId, + originChainId || this.originChain, originHeight, originStateRoot, anchorOrganization.address, @@ -652,7 +657,7 @@ export default class AuxiliaryChainInteract { * to connect. */ private copyStateToChainsDir(): void { - fs.ensureDirSync(Directory.getProjectUtilityChainDir(this.originChainId, this.chainId)); + fs.ensureDirSync(Directory.getProjectUtilityChainDir(this.originChain, this.chainId)); this.copy('geth'); this.copy('genesis.json'); @@ -664,7 +669,7 @@ export default class AuxiliaryChainInteract { private copy(file: string): void { const source: string = path.join(this.chainDir, file); const destination: string = path.join( - Directory.getProjectUtilityChainDir(this.originChainId, this.chainId), + Directory.getProjectUtilityChainDir(this.originChain, this.chainId), file, ); this.logInfo('copying chains state to utility chains directory', { source, destination }); diff --git a/src/NewChain/ChainVerifier.ts b/src/NewChain/ChainVerifier.ts index 7650ba4b..168feb06 100644 --- a/src/NewChain/ChainVerifier.ts +++ b/src/NewChain/ChainVerifier.ts @@ -5,6 +5,7 @@ import MosaicConfig, { ContractAddresses } from '../Config/MosaicConfig'; import Logger from '../Logger'; import Web3 = require('web3'); +import BN = require('bn.js'); /** * Chain verifier does verification of newly created auxiliary chains. @@ -340,7 +341,8 @@ export default class ChainVerifier { ); const remoteChainId = await anchorInstance.methods.getRemoteChainId().call(); - if (remoteChainId !== this.mosaicConfig.originChain.chain) { + const originChainId = await this.originWeb3.eth.net.getId(); + if (!new BN(remoteChainId).eq(new BN(originChainId))) { const errMsg = 'AuxiliaryAnchor: Invalid remoteChainId!!!'; Logger.error(errMsg); throw new Error(errMsg); diff --git a/src/NewChain/Initialization.ts b/src/NewChain/Initialization.ts index b679c90f..a61904ce 100644 --- a/src/NewChain/Initialization.ts +++ b/src/NewChain/Initialization.ts @@ -145,7 +145,7 @@ export default class Initialization { ); Logger.info('Origin contracts deployed'); const originContracts = auxiliaryChain.contractAddresses.origin; - originContracts.baseTokenAddress = Utils.toChecksumAddress(mosaicConfig.originChain.contractAddresses.valueTokenAddress) + originContracts.baseTokenAddress = Utils.toChecksumAddress(mosaicConfig.originChain.contractAddresses.valueTokenAddress); originContracts.anchorOrganizationAddress = Utils.toChecksumAddress(originAnchorOrganization.address); originContracts.anchorAddress = Utils.toChecksumAddress(originAnchor.address); originContracts.gatewayOrganizationAddress = Utils.toChecksumAddress(ostGatewayOrganization.address); @@ -179,6 +179,8 @@ export default class Initialization { Logger.info('Generated Proof for Stake & mint'); Logger.info('Deploying auxiliary contract.'); + // Origin chain Id is used to set remote chain while deploying anchor. + const originChainId = (await (originChainInteract.getWeb3().eth.net.getId())).toString(); const { anchorOrganization: auxiliaryAnchorOrganization, anchor: auxiliaryAnchor, @@ -195,6 +197,7 @@ export default class Initialization { stakeMessageNonce, hashLockSecret, proofData, + originChainId, ); await Initialization.setCoAnchors( diff --git a/src/Node/ChainInfo.ts b/src/Node/ChainInfo.ts index 5b47c273..38b3b6e1 100644 --- a/src/Node/ChainInfo.ts +++ b/src/Node/ChainInfo.ts @@ -5,7 +5,6 @@ export const PARITY_CLIENT = 'parity'; * Builds node based on the given chain id. */ export default class ChainInfo { - /** * array of supported origin chains. */ @@ -13,7 +12,7 @@ export default class ChainInfo { return [ 'ethereum', 'ropsten', - 'goerli' + 'goerli', ]; } @@ -76,7 +75,10 @@ export default class ChainInfo { * @param chain Chain name. */ public static isDevChain(chain: string): boolean { - return (ChainInfo.devChainInfo[chain] !== undefined); + const setOfChain = new Set( + [...Object.keys(ChainInfo.devChainInfo), + ...Object.values(ChainInfo.devChainInfo)]); + return setOfChain.has(chain); } /** diff --git a/src/Node/GethNode.ts b/src/Node/GethNode.ts index a86ff85f..256743ba 100644 --- a/src/Node/GethNode.ts +++ b/src/Node/GethNode.ts @@ -228,10 +228,10 @@ export default class GethNode extends Node { return [ '--testnet', '--bootnodes', - 'enode://a60baadd908740e1fed9690ec399db6cbec47244acecd845a3585ec560f89d9ab96400004412b4dbf59c4e56758824e606ded5be97376ffc012a62869877f9af@155.138.211.79:30303,' + - 'enode://3869e363263a54cd930960d485338a7ef1b5b6cd61a4484c81b31f48a2b68200783472a2e7f89c31a86f087e377050720a91cfa82903bd8d45456b6a5e0ffe5f@54.149.176.240:30303,' + - 'enode://24cabc9618a4bd4ef3ccfb124b885ddfc352b87bd9f30c4f98f4791b6e81d58824f2c8b451bbdbac25a1b6311b9e12e50775ee49cdb1847c3132b4abfa7842c2@54.39.102.3:30302,' + - 'enode://eecaf5852a9f0973d20fd9cb20b480ab0e47fe4a53a2395394e8fe618e8c9e5cb058fd749bf8f0b8483d7dc14c2948e18433490f7dd6182455e3f046d2225a8c@52.221.19.47:30303' + 'enode://a60baadd908740e1fed9690ec399db6cbec47244acecd845a3585ec560f89d9ab96400004412b4dbf59c4e56758824e606ded5be97376ffc012a62869877f9af@155.138.211.79:30303,' + + 'enode://3869e363263a54cd930960d485338a7ef1b5b6cd61a4484c81b31f48a2b68200783472a2e7f89c31a86f087e377050720a91cfa82903bd8d45456b6a5e0ffe5f@54.149.176.240:30303,' + + 'enode://24cabc9618a4bd4ef3ccfb124b885ddfc352b87bd9f30c4f98f4791b6e81d58824f2c8b451bbdbac25a1b6311b9e12e50775ee49cdb1847c3132b4abfa7842c2@54.39.102.3:30302,' + + 'enode://eecaf5852a9f0973d20fd9cb20b480ab0e47fe4a53a2395394e8fe618e8c9e5cb058fd749bf8f0b8483d7dc14c2948e18433490f7dd6182455e3f046d2225a8c@52.221.19.47:30303', ]; case 'ethereum': return ['--networkid', '1']; diff --git a/src/bin/Validator.ts b/src/bin/Validator.ts new file mode 100644 index 00000000..1e6f7c26 --- /dev/null +++ b/src/bin/Validator.ts @@ -0,0 +1,76 @@ +import * as web3Utils from 'web3-utils'; +import * as fs from 'fs'; +import * as path from 'path'; +import ChainInfo from '../Node/ChainInfo'; +import Directory from '../Directory'; +import MosaicConfig from '../Config/MosaicConfig'; + +import Web3 = require('web3'); + +/** + * This class contains methods to validate commandline arguments. + */ +export default class Validator { + /** + * This method validates an ethereum address. + * @param value Ethereum address. + */ + public static isValidAddress(value: string): boolean { + return web3Utils.isAddress(value); + } + + /** + * This method validates an origin chain. + * @param value Chain identifier. + */ + public static isValidOriginChain(value: string): boolean { + return !!ChainInfo.publicOriginChainNameToIdMap[value] || ChainInfo.isDevChain(value); + } + + /** + * This method validates an aux chain. + * @param auxChain Chain identifier. + * @param originChainId Origin chain identifier. + */ + public static isValidAuxChain(auxChain: string, originChainId: string): boolean { + if (MosaicConfig.exists(originChainId)) { + const mosaicConfig = MosaicConfig.fromChain(originChainId); + if (mosaicConfig.auxiliaryChains[auxChain]) { + return true; + } + } + if (ChainInfo.isDevChain(auxChain)) { + return true; + } + let validAuxChain = false; + const chainDir = Directory.getProjectChainsDirectory; + const originChainDirectories = Validator.getDirectories(chainDir); + originChainDirectories.forEach((originChain) => { + const auxDirectory = Directory.getProjectUtilityChainDir(originChain, auxChain); + if (fs.existsSync(auxDirectory)) { + validAuxChain = true; + } + }); + return validAuxChain; + } + + public static async isValidWeb3EndPoint(web3EndPoint: string): Promise { + const web3 = new Web3(web3EndPoint); + // https://web3js.readthedocs.io/en/v1.2.0/web3-net.html#islistening + return web3.eth.net.isListening(); + } + + /** + * This function returns directories inside a folder. + * @param folderPath Path of folder. + */ + private static getDirectories(folderPath) { + return fs.readdirSync(folderPath) + .map(file => ({ + fullPath: path.join(folderPath, file), + name: file, + })) + .filter(dir => fs.statSync(dir.fullPath).isDirectory()) + .map(dir => dir.name); + } +} diff --git a/src/bin/mosaic-attach.ts b/src/bin/mosaic-attach.ts index 9feb446d..135be479 100755 --- a/src/bin/mosaic-attach.ts +++ b/src/bin/mosaic-attach.ts @@ -5,16 +5,12 @@ import Shell from '../Shell'; import Node from '../Node/Node'; import NodeFactory from '../Node/NodeFactory'; import NodeDescription from '../Node/NodeDescription'; -import DevChainOptions from './DevChainOptions'; mosaic .arguments('') + // Chain can't be validated as origin chain id is not received for aux chain. .action((chain: string) => { - let chainInput = chain; - if (DevChainOptions.isDevChain(chain)) { - chainInput = DevChainOptions.getDevChainParams(chain).chain; - } - const node: Node = NodeFactory.create(new NodeDescription(chainInput)); + const node: Node = NodeFactory.create(new NodeDescription(chain)); const args = [ 'run', diff --git a/src/bin/mosaic-create.ts b/src/bin/mosaic-create.ts index a9507c5d..8a328656 100755 --- a/src/bin/mosaic-create.ts +++ b/src/bin/mosaic-create.ts @@ -6,6 +6,7 @@ import Initialization from '../NewChain/Initialization'; import NodeOptions from './NodeOptions'; import NodeDescription from '../Node/NodeDescription'; import Directory from '../Directory'; +import Validator from './Validator'; let mosaic = commander .arguments(' '); @@ -17,6 +18,10 @@ mosaic.action( passwordFile: string, options, ) => { + const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); + if (!isValidWeb3Connection) { + Logger.error('Could not connect to origin node with web3'); + } const nodeOptions: NodeOptions = NodeOptions.parseOptions(options, newChainId); if (nodeOptions.originChain === '') { Logger.error('Unknown origin, please provide --origin'); diff --git a/src/bin/mosaic-libraries.ts b/src/bin/mosaic-libraries.ts index 1b0616dc..6a85509b 100755 --- a/src/bin/mosaic-libraries.ts +++ b/src/bin/mosaic-libraries.ts @@ -7,6 +7,7 @@ import OriginChainInteract from '../NewChain/OriginChainInteract'; import MosaicConfig from '../Config/MosaicConfig'; import PublishMosaicConfig from '../Config/PublishMosaicConfig'; import Utils from '../Utils'; +import Validator from './Validator'; import Web3 = require('web3'); @@ -18,6 +19,19 @@ mosaic.action( originWebsocket: string, deployer: string, ) => { + const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); + if (!isValidWeb3Connection) { + Logger.error('Could not connect to origin node with web3'); + } + + if (!Validator.isValidOriginChain(chain)) { + Logger.error(`Invalid origin chain identifier: ${chain}`); + process.exit(1); + } + if (!Validator.isValidAddress(deployer)) { + Logger.error(`Invalid deployer address: ${deployer}`); + process.exit(1); + } try { // Publishes mosaic configs for existing chains PublishMosaicConfig.tryPublish(chain); diff --git a/src/bin/mosaic-logs.ts b/src/bin/mosaic-logs.ts index 64b34c2a..1f153198 100755 --- a/src/bin/mosaic-logs.ts +++ b/src/bin/mosaic-logs.ts @@ -5,16 +5,12 @@ import Shell from '../Shell'; import Node from '../Node/Node'; import NodeDescription from '../Node/NodeDescription'; import NodeFactory from '../Node/NodeFactory'; -import DevChainOptions from './DevChainOptions'; mosaic .arguments('') .action((chain: string) => { - let chainInput = chain; - if (DevChainOptions.isDevChain(chain)) { - chainInput = DevChainOptions.getDevChainParams(chain).chain; - } - const node: Node = NodeFactory.create(new NodeDescription(chainInput)); + // Chain can't be validated as origin chain id is not received for aux chain. + const node: Node = NodeFactory.create(new NodeDescription(chain)); const args = [ 'logs', '-f', diff --git a/src/bin/mosaic-setup-redeem-pool.ts b/src/bin/mosaic-setup-redeem-pool.ts index bed1468c..9a6aeddb 100644 --- a/src/bin/mosaic-setup-redeem-pool.ts +++ b/src/bin/mosaic-setup-redeem-pool.ts @@ -1,6 +1,9 @@ import * as commander from 'commander'; import Logger from '../Logger'; import setupRedeemPool from '../lib/RedeemPool'; +import Validator from './Validator'; + +import Web3 = require('web3'); const mosaic = commander .arguments(' '); @@ -13,6 +16,35 @@ mosaic.action( organizationOwner: string, organizationAdmin: string, ) => { + const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(auxChainWeb3EndPoint); + if (!isValidWeb3Connection) { + Logger.error('Could not connect to aux node with web3'); + } + + if (!Validator.isValidOriginChain(originChain)) { + Logger.error(`Invalid origin chain identifier: ${originChain}`); + process.exit(1); + } + + if (!Validator.isValidAuxChain(auxiliaryChain, originChain)) { + Logger.error(`Invalid aux chain identifier: ${auxiliaryChain}`); + process.exit(1); + } + + if (!Validator.isValidAddress(deployer)) { + Logger.error(`Invalid deployer address: ${deployer}`); + process.exit(1); + } + if (!Validator.isValidAddress(organizationOwner)) { + Logger.error(`Invalid organization owner address: ${organizationOwner}`); + process.exit(1); + } + + if (!Validator.isValidAddress(organizationAdmin)) { + Logger.error(`Invalid organization admin address: ${organizationAdmin}`); + process.exit(1); + } + try { await setupRedeemPool( originChain, diff --git a/src/bin/mosaic-setup-stake-pool.ts b/src/bin/mosaic-setup-stake-pool.ts index acd03871..d7fbffe6 100755 --- a/src/bin/mosaic-setup-stake-pool.ts +++ b/src/bin/mosaic-setup-stake-pool.ts @@ -3,6 +3,7 @@ import * as commander from 'commander'; import Logger from '../Logger'; import deployStakePool from '../lib/StakePool'; +import Validator from './Validator'; const mosaic = commander .arguments(' '); @@ -14,6 +15,28 @@ mosaic.action( organizationOwner: string, organizationAdmin: string, ) => { + const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); + if (!isValidWeb3Connection) { + Logger.error('Could not connect to origin node with web3'); + } + if (!Validator.isValidOriginChain(chain)) { + Logger.error(`Invalid origin chain identifier: ${chain}`); + process.exit(1); + } + + if (!Validator.isValidAddress(deployer)) { + Logger.error(`Invalid deployer address: ${deployer}`); + process.exit(1); + } + if (!Validator.isValidAddress(organizationOwner)) { + Logger.error(`Invalid organization owner address: ${organizationOwner}`); + process.exit(1); + } + + if (!Validator.isValidAddress(organizationAdmin)) { + Logger.error(`Invalid organization admin address: ${organizationAdmin}`); + process.exit(1); + } try { await deployStakePool(chain, originWebsocket, deployer, organizationOwner, organizationAdmin); } catch (error) { diff --git a/src/bin/mosaic-start.ts b/src/bin/mosaic-start.ts index 829bbb84..a08c3adc 100755 --- a/src/bin/mosaic-start.ts +++ b/src/bin/mosaic-start.ts @@ -11,6 +11,7 @@ import NodeDescription from '../Node/NodeDescription'; import DevChainOptions from './DevChainOptions'; import Logger from '../Logger'; import { default as ChainInfo, GETH_CLIENT, PARITY_CLIENT } from '../Node/ChainInfo'; +import Validator from './Validator'; let mosaic = commander .arguments(''); @@ -88,6 +89,22 @@ mosaic password, originChain, } = NodeOptions.parseOptions(optionInput, chainInput); + + if (originChain && originChain.length > 0) { + if (!Validator.isValidOriginChain(originChain)) { + Logger.error(`Invalid origin chain identifier: ${originChain}`); + process.exit(1); + } + + if (!Validator.isValidAuxChain(chain, originChain)) { + Logger.error(`Invalid aux chain identifier: ${chain}`); + process.exit(1); + } + } else if (!Validator.isValidOriginChain(chain)) { + Logger.error(`Invalid origin chain identifier: ${chain}`); + process.exit(1); + } + const nodeDescription: NodeDescription = { chain: chainInput, mosaicDir, diff --git a/src/bin/mosaic-stop.ts b/src/bin/mosaic-stop.ts index ccb9ff49..983d3934 100755 --- a/src/bin/mosaic-stop.ts +++ b/src/bin/mosaic-stop.ts @@ -6,19 +6,15 @@ import NodeDescription from '../Node/NodeDescription'; import Node from '../Node/Node'; import GraphDescription from '../Graph/GraphDescription'; import Graph from '../Graph/Graph'; -import DevChainOptions from './DevChainOptions'; mosaic .arguments('') .action((chains: string[]) => { + // Chain can't be validated as origin chain id is not received for aux chain. for (const chain of chains) { - let chainInput = chain; - if (DevChainOptions.isDevChain(chain)) { - chainInput = DevChainOptions.getDevChainParams(chain).chain; - } - const node: Node = NodeFactory.create(new NodeDescription(chainInput)); + const node: Node = NodeFactory.create(new NodeDescription(chain)); node.stop(); - const graph: Graph = new Graph(new GraphDescription(chainInput)); + const graph: Graph = new Graph(new GraphDescription(chain)); graph.stop(); } }) diff --git a/src/bin/mosaic-verify-chain.ts b/src/bin/mosaic-verify-chain.ts index 70dded37..d5a7be4e 100644 --- a/src/bin/mosaic-verify-chain.ts +++ b/src/bin/mosaic-verify-chain.ts @@ -5,6 +5,7 @@ import * as commander from 'commander'; import Logger from '../Logger'; import ChainVerifier from '../NewChain/ChainVerifier'; import NodeOptions from './NodeOptions'; +import Validator from './Validator'; let mosaic = commander .arguments(' '); @@ -16,6 +17,25 @@ mosaic.action( originChainIdentifier: string, auxiliaryChainIdentifier: string, ) => { + const isValidOriginWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); + if (!isValidOriginWeb3Connection) { + Logger.error('Could not connect to origin node with web3'); + } + + const isValidAuxWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); + if (!isValidAuxWeb3Connection) { + Logger.error('Could not connect to aux node with web3'); + } + + if (!Validator.isValidOriginChain(originChainIdentifier)) { + Logger.error(`Invalid origin chain identifier: ${originChainIdentifier}`); + process.exit(1); + } + + if (!Validator.isValidAuxChain(auxiliaryChainIdentifier, originChainIdentifier)) { + Logger.error(`Invalid aux chain identifier: ${auxiliaryChainIdentifier}`); + process.exit(1); + } try { const chainVerifier = new ChainVerifier( originWebsocket, diff --git a/src/bin/mosaic.ts b/src/bin/mosaic.ts index 5eb416fb..2bb399f3 100755 --- a/src/bin/mosaic.ts +++ b/src/bin/mosaic.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node import * as mosaic from 'commander'; +import Logger from '../Logger'; mosaic .command('start ', 'start container that runs a given chain') @@ -14,4 +15,20 @@ mosaic .command('setup-redeem-pool ', 'Deploys redeem pool contract.') .command('setup-stake-pool ', 'Deploys stake pool contract.') .command('subgraph ', 'Deploys mosaic subgraph on a graph node') + .on('command:*', (command) => { + // First argument is the command name. + const firstCommand = command[0]; + // Check if command is implemented. + const searchedCommand = mosaic.commands.find(c => c._name === firstCommand); + if (!searchedCommand) { + Logger.error('Invalid command: %s\nSee --help for a list of available commands.', mosaic.args.join(' ')); + process.exit(1); + } + const requiredArgsCount = searchedCommand._args.filter(cmd => cmd.required).length; + // Check for required arguments. + if ((command.length - 1) < requiredArgsCount) { + Logger.error('Missing required argument: See --help for details.'); + process.exit(1); + } + }) .parse(process.argv); diff --git a/src/lib/SubGraph.ts b/src/lib/SubGraph.ts index 71145ad9..bb5e2b38 100644 --- a/src/lib/SubGraph.ts +++ b/src/lib/SubGraph.ts @@ -2,6 +2,7 @@ import GatewayAddresses from '../Config/GatewayAddresses'; import GatewayConfig from '../Config/GatewayConfig'; import MosaicConfig from '../Config/MosaicConfig'; import SubGraph, { SubGraphType } from '../Graph/SubGraph'; +import Validator from '../bin/Validator'; const deploySubGraph = ( originChain: string, @@ -13,6 +14,17 @@ const deploySubGraph = ( gatewayAddress?: string, gatewayConfigPath?: string, ) => { + if (!Validator.isValidOriginChain(originChain)) { + throw new Error(`Invalid origin chain identifier: ${originChain}`); + } + + if (!Validator.isValidAuxChain(auxiliaryChain, originChain)) { + throw new Error(`Invalid aux chain identifier: ${auxiliaryChain}`); + } + if (gatewayAddress && !Validator.isValidAddress(gatewayAddress)) { + throw new Error(`Invalid deployer address: ${gatewayAddress}`); + } + let gatewayAddresses: GatewayAddresses; let gatewayConfig: GatewayConfig; let mosaicConfig: MosaicConfig; diff --git a/test_integration/mosaic-create/mosaic-create.test.ts b/test_integration/mosaic-create/mosaic-create.test.ts index 6529dc0a..d7c717a2 100644 --- a/test_integration/mosaic-create/mosaic-create.test.ts +++ b/test_integration/mosaic-create/mosaic-create.test.ts @@ -18,10 +18,10 @@ describe('Mosaic create', () => { const auxChainId = 500; const passwordFile = './test_integration/mosaic-create/integration_test_password.txt'; let originWeb3; - let originChainId; + const originChainId = 'dev-origin'; let originDeployerAddress: string; let auxiliaryWeb3; - let auxiliaryEndpoint = 'http://localhost:40500'; + const auxiliaryEndpoint = 'http://localhost:40500'; let beneficiary; let stakeAmount; @@ -31,7 +31,6 @@ describe('Mosaic create', () => { it('Start origin chain', async () => { await Utils.startOriginChain(originPort); originWeb3 = new Web3(originWeb3RPCEndPoint); - originChainId = await originWeb3.eth.net.getId(); originDeployerAddress = (await originWeb3.eth.getAccounts())[0]; }); diff --git a/tests/ChainInfo/isDevChain.test.ts b/tests/ChainInfo/isDevChain.test.ts new file mode 100644 index 00000000..69e978aa --- /dev/null +++ b/tests/ChainInfo/isDevChain.test.ts @@ -0,0 +1,38 @@ +import { assert } from 'chai'; +import ChainInfo from '../../src/Node/ChainInfo'; + +describe('ChainInfo.isDevChain()', () => { + it('should return true for valid dev chain', async () => { + assert.isTrue( + ChainInfo.isDevChain('dev-origin'), + 'dev-origin must be a valid dev chain', + ); + + assert.isTrue( + ChainInfo.isDevChain('dev-auxiliary'), + 'dev-auxiliary must be a valid dev chain', + ); + + assert.isTrue( + ChainInfo.isDevChain('1000'), + '1000 must be a valid dev chain', + ); + + assert.isTrue( + ChainInfo.isDevChain('1515'), + '1515 must be a valid dev chain', + ); + }); + + it('should return false for invalid dev chain', async () => { + assert.isNotTrue( + ChainInfo.isDevChain('ropsten'), + 'ropsten must be an invalid dev chain', + ); + + assert.isNotTrue( + ChainInfo.isDevChain('1406'), + '1406 must be an invalid dev chain', + ); + }); +}); diff --git a/tests/Directory.test.ts b/tests/Directory.test.ts index a6c76810..1f4f6a22 100644 --- a/tests/Directory.test.ts +++ b/tests/Directory.test.ts @@ -36,7 +36,7 @@ describe('Directory.sanitize()', () => { path.join( Directory.projectRoot, 'some', - 'path' + 'path', ), 'Does not properly replace `.` with the project root.', ); @@ -46,7 +46,7 @@ describe('Directory.sanitize()', () => { path.join( Directory.projectRoot, 'other', - 'path' + 'path', ), 'Does not properly add project root before a relative path.', ); @@ -69,3 +69,18 @@ describe('Directory.sanitize()', () => { ); }); }); + +describe('Directory.getProjectChainsDirectory()', () => { + it('should return project chain directory', () => { + const projectChainsDirectory = Directory.getProjectChainsDirectory; + + assert.strictEqual( + projectChainsDirectory, + path.join( + Directory.projectRoot, + 'chains', + ), + 'Expected project chain directory doesnot match', + ); + }); +}); diff --git a/tests/Validator/isValidAddress.test.ts b/tests/Validator/isValidAddress.test.ts new file mode 100644 index 00000000..f69f8c25 --- /dev/null +++ b/tests/Validator/isValidAddress.test.ts @@ -0,0 +1,17 @@ +import 'mocha'; +import { assert } from 'chai'; +import Validator from '../../src/bin/Validator'; + +describe('Validation.isValidAddress', () => { + it('should return true for valid address', () => { + assert.isTrue( + Validator.isValidAddress('0x0000000000000000000000000000000000000001'), 'Must return true for 0x0000000000000000000000000000000000000001', + ); + }); + + it('should return false for valid address', () => { + assert.isNotTrue( + Validator.isValidAddress('0x000000001'), 'Must return true for 0x000000001', + ); + }); +}); diff --git a/tests/Validator/isValidAuxiliaryChain.test.ts b/tests/Validator/isValidAuxiliaryChain.test.ts new file mode 100644 index 00000000..3f6a78c0 --- /dev/null +++ b/tests/Validator/isValidAuxiliaryChain.test.ts @@ -0,0 +1,38 @@ +import 'mocha'; +import {assert} from 'chai'; +import Validator from '../../src/bin/Validator'; + +describe('Validation.isValidAuxiliaryChain', () => { + it('should return true for valid auxiliary chain', () => { + let auxChain = '1405'; + assert.isTrue( + Validator.isValidAuxChain(auxChain, 'goerli'), `Must return true for ${auxChain} `, + ); + auxChain = '1406'; + assert.isTrue( + Validator.isValidAuxChain(auxChain, 'ropsten'), `Must return true for ${auxChain} `, + ); + auxChain = '1407'; + assert.isTrue( + Validator.isValidAuxChain(auxChain, 'ropsten'), `Must return true for ${auxChain} `, + ); + }); + + it('should return true for dev auxiliary chain', () => { + let auxChain = 'dev-auxiliary'; + assert.isTrue( + Validator.isValidAuxChain(auxChain, 'dev-origin'), `Must return true for ${auxChain} `, + ); + auxChain = '1000'; + assert.isTrue( + Validator.isValidAuxChain(auxChain, 'dev-origin'), `Must return true for ${auxChain} `, + ); + }); + + it('should return false for invalid aux chain', () => { + const originChain = '2000'; + assert.isNotTrue( + Validator.isValidAuxChain(originChain, 'invalid'), `Must return false for ${originChain} `, + ); + }); +}); diff --git a/tests/Validator/isValidOriginChain.test.ts b/tests/Validator/isValidOriginChain.test.ts new file mode 100644 index 00000000..9e0b3c9e --- /dev/null +++ b/tests/Validator/isValidOriginChain.test.ts @@ -0,0 +1,38 @@ +import 'mocha'; +import {assert} from 'chai'; +import Validator from '../../src/bin/Validator'; + +describe('Validation.isValidOriginChain', () => { + it('should return true for valid origin chain', () => { + let originChain = 'ropsten'; + assert.isTrue( + Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, + ); + originChain = 'ethereum'; + assert.isTrue( + Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, + ); + originChain = 'goerli'; + assert.isTrue( + Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, + ); + }); + + it('should return true for dev origin chain', () => { + let originChain = 'dev-origin'; + assert.isTrue( + Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, + ); + originChain = '1515'; + assert.isTrue( + Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, + ); + }); + + it('should return false for invalid origin chain', () => { + const originChain = '1405'; + assert.isNotTrue( + Validator.isValidOriginChain(originChain), `Must return false for ${originChain} `, + ); + }); +}); diff --git a/tests/lib/subgraph/DeploySubgraph.test.ts b/tests/lib/subgraph/DeploySubgraph.test.ts index e1af6958..c750e021 100644 --- a/tests/lib/subgraph/DeploySubgraph.test.ts +++ b/tests/lib/subgraph/DeploySubgraph.test.ts @@ -7,6 +7,7 @@ import SubGraph, { SubGraphType } from '../../../src/Graph/SubGraph'; import MosaicConfig from '../../../src/Config/MosaicConfig'; import Directory from '../../../src/Directory'; import SpyAssert from '../../test_utils/SpyAssert'; +import Validator from '../../../src/bin/Validator'; import someMosaicConfig = require('../../Config/testdata/mosaic.json'); @@ -24,6 +25,18 @@ describe('Subgraph.deploySubgraph', () => { gatewayAddress: '0x0000000000000000000000000000000000000001', gatewayConfigPath: '~/.mosaic/ropsten/1405/0x0000000000000000000000000000000000000001,json', }; + + sinon.replace( + Validator, + 'isValidOriginChain', + sinon.fake.returns(true), + ); + + sinon.replace( + Validator, + 'isValidAuxChain', + sinon.fake.returns(true), + ); }); afterEach(() => { @@ -155,7 +168,7 @@ describe('Subgraph.deploySubgraph', () => { it('should fail if gateway config does not exist for mentioned gateway address', () => { input.mosaicConfigPath = undefined; - input.gatewayAddress = '0x110000000000000000000000000000000000001'; + input.gatewayAddress = '0x1100000000000000000000000000000000000011'; input.gatewayConfigPath = undefined; assert.throws( @@ -343,4 +356,62 @@ describe('Subgraph.deploySubgraph', () => { 'Deploy subgraph must be called once', ); }); + + it('should fail for an invalid originChain', () => { + sinon.restore(); + const validOriginChainSpy = sinon.replace( + Validator, + 'isValidOriginChain', + sinon.fake.returns(false), + ); + + assert.throws( + () => deploySubGraph( + input.originChain, + input.auxiliaryChain, + input.subgraphType, + input.graphAdminRPC, + input.graphIPFS, + input.mosaicConfigPath, + input.gatewayAddress, + input.gatewayConfigPath, + ), + `Invalid origin chain identifier: ${input.originChain}`, + ); + + SpyAssert.assert( + validOriginChainSpy, + 1, + [[input.originChain]], + ); + }); + + it('should fail for an invalid aux chain', () => { + sinon.restore(); + const validAuxChainSpy = sinon.replace( + Validator, + 'isValidAuxChain', + sinon.fake.returns(false), + ); + + assert.throws( + () => deploySubGraph( + input.originChain, + input.auxiliaryChain, + input.subgraphType, + input.graphAdminRPC, + input.graphIPFS, + input.mosaicConfigPath, + input.gatewayAddress, + input.gatewayConfigPath, + ), + `Invalid aux chain identifier: ${input.auxiliaryChain}`, + ); + + SpyAssert.assert( + validAuxChainSpy, + 1, + [[input.auxiliaryChain, input.originChain]], + ); + }); });