diff --git a/README.md b/README.md index b5ffddaf..0f0c2708 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,35 @@ Where: *Refer integration test of mosaic-create command to understand end to end flow.* +## Subgraph deployment +Subgraph command can be used to deploy mosaic subgraph. Subgraph by [thegraph](https://thegraph.com) protocol is used to index transactions and events by mosaic smart contract. + +#### Prerequisite: Below commands assumes the blockchain node and graph node is already running. You can use `mosaic start` command to start a node and graph node. + +##### Subgraph deployment for mosaic gateways: +Below command deploys subgraph of mosaic gateways. + +```bash +./mosaic subgraph +``` +**where:** +1. origin-chain-identifier: Origin chain identifier like ropsten, goerli, dev-origin +2. auxiliary-chain-identifier: Auxiliary chain ID like 1405, 1406, 1407 or 1000(dev-auxiliary). +3. chainType: Either`origin` or `auxiliary` chain. +4. admin-graph-rpc: RPC endpoint of graph node. +5. graph-ipfs: IPFS endpoint used by graph node. + +Optionally `--mosaic-config` option can be used to pass mosaic config otherwise command will search on default path. + +#### Subgraph deployment for any EIP20 gateways: +Below command deploys subgraph of any eip20gateway. +```bash +./mosaic subgraph --gateway-config +``` +**where:** +1. gateway-config: Path of gateway config. + +Optionally `gateway-address` option can be passed which will search gateway config on default path. ## Chain Verifier Chain verifier makes sure that newly created chain is being setup correctly. diff --git a/src/Config/GatewayAddresses.ts b/src/Config/GatewayAddresses.ts new file mode 100644 index 00000000..86f18110 --- /dev/null +++ b/src/Config/GatewayAddresses.ts @@ -0,0 +1,94 @@ +import MosaicConfig from './MosaicConfig'; +import GatewayConfig from './GatewayConfig'; + +/** + * This class represents set of addresses specific to a gateway pair. + */ +export default class GatewayAddresses { + public readonly stakePoolAddress: string; + + public readonly eip20GatewayAddress: string; + + public readonly anchorAddress: string; + + public readonly coAnchorAddress: string; + + public readonly eip20CoGatewayAddress: string; + + public readonly redeemPoolAddress: string; + + /** + * Constructor + * @param stakePoolAddress StakePool address. + * @param eip20GatewayAddress eip20Gateway address. + * @param anchorAddress anchor address. + * @param coAnchorAddress coanchor address. + * @param eip20CoGatewayAddress cogateway address. + * @param redeemPoolAddress redeem pool address. + */ + private constructor( + stakePoolAddress: string, + eip20GatewayAddress: string, + anchorAddress: string, + coAnchorAddress: string, + eip20CoGatewayAddress: string, + redeemPoolAddress: string, + ) { + this.stakePoolAddress = stakePoolAddress; + this.eip20GatewayAddress = eip20GatewayAddress; + this.anchorAddress = anchorAddress; + this.coAnchorAddress = coAnchorAddress; + this.eip20CoGatewayAddress = eip20CoGatewayAddress; + this.redeemPoolAddress = redeemPoolAddress; + } + + /** + * Create Gateway address instance based on mosaic config. + * @param mosaicConfig Mosaic config object. + * @param auxiliaryChain aux chain identifier. + */ + public static fromMosaicConfig( + mosaicConfig: MosaicConfig, + auxiliaryChain: string, + ): GatewayAddresses { + const auxiliaryContractAddresses = mosaicConfig.auxiliaryChains[auxiliaryChain] + .contractAddresses.auxiliary; + const originContractAddresses = mosaicConfig.auxiliaryChains[auxiliaryChain] + .contractAddresses.origin; + return new GatewayAddresses( + mosaicConfig.originChain.contractAddresses.stakePoolAddress, + originContractAddresses.eip20GatewayAddress, + originContractAddresses.anchorAddress, + auxiliaryContractAddresses.anchorAddress, + auxiliaryContractAddresses.eip20CoGatewayAddress, + auxiliaryContractAddresses.redeemPoolAddress, + ); + } + + /** + * Creates gateway address instance from gateway config. + * @param gatewayConfig GatewayConfig instance. + */ + public static fromGatewayConfig( + gatewayConfig: GatewayConfig, + ): GatewayAddresses { + const { auxChainId } = gatewayConfig; + const stakePoolAddress = gatewayConfig.originContracts.stakePoolAddress + || gatewayConfig.mosaicConfig.originChain.contractAddresses.stakePoolAddress; + + const auxiliaryChain = gatewayConfig.mosaicConfig.auxiliaryChains[auxChainId]; + const auxiliaryContracts = auxiliaryChain.contractAddresses.auxiliary; + const redeemPool = gatewayConfig.auxiliaryContracts.redeemPoolAddress + || auxiliaryContracts.redeemPoolAddress; + + const originContracts = auxiliaryChain.contractAddresses.origin; + return new GatewayAddresses( + stakePoolAddress, + gatewayConfig.originContracts.eip20GatewayAddress, + originContracts.anchorAddress, + auxiliaryContracts.anchorAddress, + gatewayConfig.auxiliaryContracts.eip20CoGatewayAddress, + redeemPool, + ); + } +} diff --git a/src/Config/GatewayConfig.ts b/src/Config/GatewayConfig.ts index 2935ca17..bd651080 100644 --- a/src/Config/GatewayConfig.ts +++ b/src/Config/GatewayConfig.ts @@ -7,6 +7,7 @@ import { } from '../Exception'; import FileSystem from '../FileSystem '; import Directory from '../Directory'; +import Logger from "../Logger"; /* eslint-disable @typescript-eslint/no-var-requires */ const schema = require('./GatewayConfig.schema.json'); @@ -95,6 +96,7 @@ export default class GatewayConfig { public static fromChain(originChain: string, auxChainId: number, gatewayAddress: string): GatewayConfig { const filePath = Directory.getGatewayConfigPath(originChain, auxChainId, gatewayAddress); + Logger.info(`filepath for gateway config ${filePath}`); if (GatewayConfig.exists(filePath)) { const configObject = GatewayConfig.readConfigFromFile(filePath); return new GatewayConfig(configObject); diff --git a/src/Directory.ts b/src/Directory.ts index fd5ba3f9..99d50e8f 100644 --- a/src/Directory.ts +++ b/src/Directory.ts @@ -78,18 +78,15 @@ export default class Directory { * * @param originChain * @param auxiliaryChain - * @param chainClient * @return */ public static getOriginSubGraphProjectDirSuffix( originChain: string, auxiliaryChain: string, - chainClient: string ): string { return path.join( originChain, - `origin-${chainClient}`, - 'subgraph', + 'origin-subgraph', auxiliaryChain, ); } @@ -175,12 +172,12 @@ export default class Directory { * @return Path of gateway config file. */ public static getGatewayConfigPath(originChain: string, auxChainId: number, gatewayAddress: string): - string { + string { return path.join( Directory.getDefaultMosaicDataDir, originChain, auxChainId.toString(), - gatewayAddress.toLowerCase()+'.json', + `${gatewayAddress.toLowerCase()}.json`, ); } } diff --git a/src/Graph/SubGraph.ts b/src/Graph/SubGraph.ts index 92bc4bdd..12d31d94 100644 --- a/src/Graph/SubGraph.ts +++ b/src/Graph/SubGraph.ts @@ -3,10 +3,13 @@ import * as mustache from 'mustache'; import Logger from '../Logger'; import Shell from '../Shell'; import Directory from '../Directory'; -import MosaicConfig from '../Config/MosaicConfig'; -import GraphDescription from './GraphDescription'; import FileSystem from '../FileSystem '; +import GatewayAddresses from '../Config/GatewayAddresses'; +export enum SubGraphType { + ORIGIN = 'origin', + AUXILIARY = 'auxiliary', +} /** * Represents a sub graph. */ @@ -20,42 +23,38 @@ export default class SubGraph { /** To be used to determine which code is deployed. For example origin/auxiliary */ private readonly subGraphType: string; - /** Graph description to be used for this sub graph */ - private readonly graphDescription: GraphDescription; + /** Graph node rpc admin endpoint */ + private readonly graphRPCAdminEndPoint: string; - /** - * enum defining origin sub graph type. - * @returns The prefix. - */ - public static get originSubGraphType(): string { - return 'origin'; - } + /** Graph node IPFS endpoint */ + private readonly graphIPFSEndPoint: string; - /** - * enum defining auxiliary sub graph type. - * @returns The prefix. - */ - public static get auxiliarySubGraphType(): string { - return 'auxiliary'; - } + /** Gateway pair addresses */ + private readonly gatewayAddresses: GatewayAddresses; /** * Constructor. - * @param {string} originChain - * @param {string} auxiliaryChain - * @param {string} subGraphType - * @param {GraphDescription} graphDescription + * @param originChain Origin chain identifier. + * @param auxiliaryChain Auxiliary chain identifier. + * @param subGraphType Subgraph type + * @param graphRPCAdminEndPoint Graph node rpc admin endpoint. + * @param graphIPFSEndPoint Graph node IPFS endpoint. + * @param gatewayAddresses Gateway pair addresses. */ - constructor( + public constructor( originChain: string, auxiliaryChain: string, subGraphType: string, - graphDescription: GraphDescription, + graphRPCAdminEndPoint: string, + graphIPFSEndPoint: string, + gatewayAddresses: GatewayAddresses, ) { this.originChain = originChain; this.auxiliaryChain = auxiliaryChain; this.subGraphType = subGraphType; - this.graphDescription = graphDescription; + this.graphRPCAdminEndPoint = graphRPCAdminEndPoint; + this.graphIPFSEndPoint = graphIPFSEndPoint; + this.gatewayAddresses = gatewayAddresses; } /** @@ -63,11 +62,6 @@ export default class SubGraph { * @return */ public deploy(): {success: boolean; message: string} { - if (FileSystem.pathExistsSync(this.getSubGraphProjectDir)) { - // if subGraphProjectDir we would assume sub graph deployment was already complete - this.logInfo('Sub graph already exists. Skipping deployment'); - return { success: true, message: 'Sub graph already exists. Skipping deployment' }; - } this.copyCodeToTempDir(); this.installNodeModules(); this.writeSubGraphConfigFile(); @@ -77,24 +71,10 @@ export default class SubGraph { return createLocalResponse; } const deployLocalResponse = this.deployLocal(); - if (deployLocalResponse.success) { - this.copyToSubGraphProjectDir(); - } this.deleteCodeFromTempDir(); return deployLocalResponse; } - /** - * Directory in which we would persist code which was used for sub graph deployment. - * @return - */ - private get getSubGraphProjectDir(): string { - return path.join( - this.graphDescription.mosaicDir, - this.getSubGraphProjectDirSuffix, - ); - } - /** * Directory in which we would keep auto generated graph related code temporarily. * @return {string} @@ -111,11 +91,11 @@ export default class SubGraph { * @return {string} */ private get getSubGraphProjectDirSuffix(): string { - if (this.subGraphType === SubGraph.originSubGraphType) { + if (this.subGraphType === SubGraphType.ORIGIN + ) { return Directory.getOriginSubGraphProjectDirSuffix( this.originChain, this.auxiliaryChain, - this.graphDescription.ethereumClient ); } return Directory.getAuxiliarySubGraphProjectDirSuffix(this.originChain, this.auxiliaryChain); @@ -148,7 +128,8 @@ export default class SubGraph { private createLocal(): {success: boolean; message: string} { this.logInfo('attempting to create local graph'); try { - this.executeGraphCommand(`create --node http://localhost:${this.graphDescription.rpcAdminPort}/ ${this.name}`); + this.tryRemovingSubgraph(); + this.executeGraphCommand(`create --node ${this.graphRPCAdminEndPoint}/ ${this.name}`); return { success: true, message: '' }; } catch (ex) { const message = this.extractMessageFromError(ex); @@ -157,6 +138,17 @@ export default class SubGraph { } } + /** + * This method tries to remove the subgraph if already deployed. + */ + private tryRemovingSubgraph() { + try { + this.executeGraphCommand(`remove --node ${this.graphRPCAdminEndPoint}/ ${this.name}`); + } catch (e) { + this.logInfo('No subgraph exists, deploying for the first time.'); + } + } + /** * Copy auto generated code to a temp dir. */ @@ -173,7 +165,7 @@ export default class SubGraph { * Returns values for all template variables which need to be replaced in subgraph.yaml. */ private templateVariables(): object { - if (this.subGraphType === SubGraph.originSubGraphType) { + if (this.subGraphType === SubGraphType.ORIGIN) { return this.originChainTemplateVariables(); } return this.auxiliaryChainTemplateVariables(); @@ -184,36 +176,35 @@ export default class SubGraph { * @returns The prefix. */ private get name(): string { - if (this.subGraphType === SubGraph.originSubGraphType) { - return `mosaic/origin-${this.auxiliaryChain}`; + if (this.subGraphType === SubGraphType.ORIGIN) { + return `mosaic/origin-${this.gatewayAddresses.eip20GatewayAddress.substr(2, 25)}`; } - return `mosaic/auxiliary-${this.auxiliaryChain}`; + return `mosaic/auxiliary-${this.gatewayAddresses.eip20CoGatewayAddress.substr(2, 22)}`; } /** - * Returns values for all template variables which need to be replaced in subgraph.yaml for origin subGraphType + * Returns values for all template variables which need to be replaced in + * subgraph.yaml for origin subGraphType. */ private originChainTemplateVariables(): object { - const mosaicConfig: MosaicConfig = MosaicConfig.fromChain(this.originChain); return { projectRoot: Directory.projectRoot, - stakePoolAddress: mosaicConfig.originChain.contractAddresses.stakePoolAddress, - eip20GatewayAddress: mosaicConfig.auxiliaryChains[this.auxiliaryChain].contractAddresses.origin.eip20GatewayAddress, - anchorAddress: mosaicConfig.auxiliaryChains[this.auxiliaryChain].contractAddresses.origin.anchorAddress, + stakePoolAddress: this.gatewayAddresses.stakePoolAddress, + eip20GatewayAddress: this.gatewayAddresses.eip20GatewayAddress, + anchorAddress: this.gatewayAddresses.anchorAddress, }; } /** - * Returns values for all template variables which need to be replaced in subgraph.yaml for auxiliary subGraphType + * Returns values for all template variables which need to be replaced in + * subgraph.yaml for auxiliary subGraphType. */ private auxiliaryChainTemplateVariables(): object { - const mosaicConfig: MosaicConfig = MosaicConfig.fromChain(this.originChain); - const auxiliaryContractAddresses = mosaicConfig.auxiliaryChains[this.auxiliaryChain].contractAddresses.auxiliary; return { projectRoot: Directory.projectRoot, - anchorAddress: auxiliaryContractAddresses.anchorAddress, - eip20CoGatewayAddress: auxiliaryContractAddresses.eip20CoGatewayAddress, - redeemPoolAddress: auxiliaryContractAddresses.redeemPoolAddress, + anchorAddress: this.gatewayAddresses.coAnchorAddress, + eip20CoGatewayAddress: this.gatewayAddresses.eip20CoGatewayAddress, + redeemPoolAddress: this.gatewayAddresses.redeemPoolAddress, }; } @@ -225,14 +216,14 @@ export default class SubGraph { this.logInfo('attempting to deploy local graph'); try { this.executeGraphCommand( - `deploy --node http://localhost:${this.graphDescription.rpcAdminPort}/ --ipfs http://localhost:${this.graphDescription.ipfsPort} ${this.name}`, + `deploy --node ${this.graphRPCAdminEndPoint}/ --ipfs ${this.graphIPFSEndPoint} ${this.name}`, ); return { success: true, message: '' }; } catch (ex) { const message = this.extractMessageFromError(ex); this.logInfo(`deploy local graph failed with: ${message}`); this.logInfo('removing local graph'); - this.executeGraphCommand(`remove --node http://localhost:${this.graphDescription.rpcAdminPort}/ ${this.name}`); + this.executeGraphCommand(`remove --node ${this.graphRPCAdminEndPoint}/ ${this.name}`); return { success: false, message }; } } @@ -253,23 +244,10 @@ export default class SubGraph { FileSystem.removeSync(this.getTempGraphInstallationDir); } - /** - * Persist auto generated code. - */ - private copyToSubGraphProjectDir(): void { - this.logInfo('persisting auto generated graph code'); - const subGraphProjectDir = this.getSubGraphProjectDir; - FileSystem.ensureDirSync(subGraphProjectDir); - FileSystem.copySync( - this.getTempGraphInstallationDir, - subGraphProjectDir, - ); - } /** * Extract message from error object. * @param error - * @return */ private extractMessageFromError(error: Error): string { const jsonErrorObject = JSON.parse(JSON.stringify(error)); diff --git a/src/Graph/SubGraphDeployer.ts b/src/Graph/SubGraphDeployer.ts deleted file mode 100644 index d1a27e40..00000000 --- a/src/Graph/SubGraphDeployer.ts +++ /dev/null @@ -1,76 +0,0 @@ -import GraphDescription from './GraphDescription'; -import SubGraph from './SubGraph'; -import MosaicConfig from '../Config/MosaicConfig'; -import Logger from '../Logger'; - -/** - * Has logic to determine list of sub graphs and deploy them (if required) - */ -export default class SubGraphDeployer { - /** This chain identifier identifies the origin chain. For example ropsten. */ - private readonly originChain: string; - - /** This chain identifier identifies the aux chain. For example 1407. */ - private readonly auxiliaryChain: string; - - /** graph description to be used for starting graph node. */ - private readonly graphDescription: GraphDescription; - - /** - * - * @param {GraphDescription} graphDescription - * @param {String} originChain : origin chain identifier - * @param {String} [auxiliaryChain] : auxiliary chain identifier (to be passed only if called for an auxiliary chain) - */ - public constructor(graphDescription: GraphDescription, originChain: string, auxiliaryChain: string) { - this.graphDescription = graphDescription; - this.originChain = originChain; - this.auxiliaryChain = auxiliaryChain; - } - - /** - * Start graph node - * @return {Promise} - */ - public deploy(): void { - if (this.auxiliaryChain) { - return this.deployAuxiliarySubGraph(); - } - return this.deployOriginSubGraphs(); - } - - /** - * deploy sub graphs with SubGraphType=origin for all auxiliary chains - * @return {Promise} - */ - private deployOriginSubGraphs(): void { - const subGraphType = SubGraph.originSubGraphType; - const mosaicConfig: MosaicConfig = MosaicConfig.fromChain(this.originChain); - for (const auxiliaryChain of Object.keys(mosaicConfig.auxiliaryChains)) { - Logger.info(`Starting Sub Graph Deployment for originChain: ${this.originChain} auxiliaryChain: ${auxiliaryChain} subGraphType: ${subGraphType}`); - const subGraph = new SubGraph( - this.originChain, - auxiliaryChain, - subGraphType, - this.graphDescription, - ); - subGraph.deploy(); - } - } - - /** - * - * @return {Promise} - */ - private deployAuxiliarySubGraph(): void { - const subGraphType = SubGraph.auxiliarySubGraphType; - Logger.info(`Starting Sub Graph Deployment for originChain: ${this.originChain} auxiliaryChain: ${this.auxiliaryChain} subGraphType: ${subGraphType}`); - const subGraph = new SubGraph( - this.originChain, - this.auxiliaryChain, - subGraphType, - this.graphDescription, - ); - subGraph.deploy(); - } -} diff --git a/src/Node/GethNode.ts b/src/Node/GethNode.ts index ad30487e..a86ff85f 100644 --- a/src/Node/GethNode.ts +++ b/src/Node/GethNode.ts @@ -5,7 +5,7 @@ import Shell from '../Shell'; import Directory from '../Directory'; import ChainInfo from './ChainInfo'; -const DEV_CHAIN_DOCKER = 'mosaicdao/dev-chains'; +const DEV_CHAIN_DOCKER = 'mosaicdao/dev-chains:1.0.0'; /** * Represents a geth node that runs in a docker container. */ diff --git a/src/bin/mosaic-setup-redeem-pool.ts b/src/bin/mosaic-setup-redeem-pool.ts index de717039..bed1468c 100644 --- a/src/bin/mosaic-setup-redeem-pool.ts +++ b/src/bin/mosaic-setup-redeem-pool.ts @@ -23,7 +23,7 @@ mosaic.action( organizationAdmin, ); } catch (error) { - Logger.error('error while executing mosaic libraries', {error: error.toString()}); + Logger.error('error while executing mosaic setup redeem pool', { error: error.toString() }); process.exit(1); } diff --git a/src/bin/mosaic-setup-stake-pool.ts b/src/bin/mosaic-setup-stake-pool.ts index afcddff2..acd03871 100755 --- a/src/bin/mosaic-setup-stake-pool.ts +++ b/src/bin/mosaic-setup-stake-pool.ts @@ -17,7 +17,7 @@ mosaic.action( try { await deployStakePool(chain, originWebsocket, deployer, organizationOwner, organizationAdmin); } catch (error) { - Logger.error('error while executing mosaic libraries', { error: error.toString() }); + Logger.error('error while executing mosaic setup stake pool', { error: error.toString() }); process.exit(1); } diff --git a/src/bin/mosaic-start.ts b/src/bin/mosaic-start.ts index 35afba1f..829bbb84 100755 --- a/src/bin/mosaic-start.ts +++ b/src/bin/mosaic-start.ts @@ -6,7 +6,6 @@ import Node from '../Node/Node'; import NodeOptions from './NodeOptions'; import GraphOptions from './GraphOptions'; import GraphDescription from '../Graph/GraphDescription'; -import SubGraphDeployer from '../Graph/SubGraphDeployer'; import Graph from '../Graph/Graph'; import NodeDescription from '../Node/NodeDescription'; import DevChainOptions from './DevChainOptions'; @@ -65,7 +64,7 @@ mosaic .option('-u,--unlock ', 'a comma separated list of accounts that get unlocked in the node; you must use this together with --password') .option('-s,--password ', 'the path to the password file on your machine; you must use this together with --unlock') .option('-g,--withoutGraphNode', 'boolean flag which decides if graph node should be started') - .action((chain: string, options) => { + .action(async (chain: string, options) => { try { let chainInput = chain; let optionInput = Object.assign({}, options); @@ -114,20 +113,7 @@ mosaic graphDescription.ethereumRpcPort = rpcPort; graphDescription.ethereumClient = nodeDescription.client; - new Graph(graphDescription).start().then(() => { - let subGraphDeployer; - // options.origin passed only in case of starting an auxiliary chain - if (optionInput.origin) { - subGraphDeployer = new SubGraphDeployer( - graphDescription, - optionInput.origin, - chainInput, - ); - } else { - subGraphDeployer = new SubGraphDeployer(graphDescription, chainInput, null); - } - return subGraphDeployer.deploy(); - }); + await (new Graph(graphDescription).start()); } } catch (e) { Logger.error(`Error starting node: ${e} `); diff --git a/src/bin/mosaic-subgraph.ts b/src/bin/mosaic-subgraph.ts new file mode 100755 index 00000000..a5be873d --- /dev/null +++ b/src/bin/mosaic-subgraph.ts @@ -0,0 +1,85 @@ +#!/usr/bin/env node + +import * as commander from 'commander'; + +import Logger from '../Logger'; +import MosaicConfig from '../Config/MosaicConfig'; +import SubGraph, { SubGraphType } from '../Graph/SubGraph'; +import GatewayAddresses from '../Config/GatewayAddresses'; +import GatewayConfig from '../Config/GatewayConfig'; + +const mosaic = commander + .arguments(' '); + +mosaic.option('-m,--mosaic-config ', 'Mosaic config absolute path.'); +mosaic.option('-t,--gateway-config ', 'Gateway config absolute path.'); +mosaic.option('-g,--gateway-address ', 'gateway address of origin.'); +mosaic.action( + async ( + originChain: string, + auxiliaryChain: string, + subgraphType: SubGraphType, + graphAdminRPC: string, + graphIPFS: string, + options, + ) => { + try { + let gatewayAddresses: GatewayAddresses; + let gatewayConfig: GatewayConfig; + let mosaicConfig: MosaicConfig; + + if (options.gatewayConfig) { + gatewayConfig = GatewayConfig.fromFile(options.gatewayConfig); + } else if (options.gatewayAddress) { + gatewayConfig = GatewayConfig.fromChain( + originChain, + parseInt(auxiliaryChain), + options.gatewayAddress, + ); + } + + if (options.mosaicConfig) { + mosaicConfig = MosaicConfig.fromFile(options.mosaicConfig); + } else if (MosaicConfig.exists(originChain)) { + mosaicConfig = MosaicConfig.fromChain(originChain); + } + + if (gatewayConfig) { + if (parseInt(auxiliaryChain) !== gatewayConfig.auxChainId) { + Logger.error(`Auxiliary chain id in gateway config is ${gatewayConfig.auxChainId} but value passed is ${auxiliaryChain}`); + process.exit(1); + } + gatewayAddresses = GatewayAddresses.fromGatewayConfig(gatewayConfig); + } else if (mosaicConfig) { + if (mosaicConfig.originChain.chain !== originChain) { + Logger.error(`Origin chain id in mosaic config is ${mosaicConfig.originChain.chain} but received argument is ${originChain}`); + process.exit(1); + } + gatewayAddresses = GatewayAddresses.fromMosaicConfig( + mosaicConfig, + auxiliaryChain.toString(), + ); + } + + if (!gatewayAddresses) { + Logger.error('Mosaic config or gateway config not found . Use --mosaic-config or --gateway-config option to provide path.'); + process.exit(1); + } + + new SubGraph( + originChain, + auxiliaryChain.toString(), + subgraphType, + graphAdminRPC, + graphIPFS, + gatewayAddresses, + ).deploy(); + } catch (error) { + Logger.error('error while executing mosaic subgraph command', { error: error.toString() }); + process.exit(1); + } + + process.exit(0); + }, +) + .parse(process.argv); diff --git a/src/bin/mosaic.ts b/src/bin/mosaic.ts index 19fbce7c..5eb416fb 100755 --- a/src/bin/mosaic.ts +++ b/src/bin/mosaic.ts @@ -13,4 +13,5 @@ mosaic .command('verify-chain ', 'Verifies an auxiliary chain.') .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') .parse(process.argv); diff --git a/tests/Graph/SubGraphDeployment/auxiliary-verifier.ts b/tests/Graph/SubGraphDeployment/auxiliary-verifier.ts index 286d3eac..ec1395ee 100644 --- a/tests/Graph/SubGraphDeployment/auxiliary-verifier.ts +++ b/tests/Graph/SubGraphDeployment/auxiliary-verifier.ts @@ -8,21 +8,23 @@ import Logger from '../../../src/Logger'; import WebSocket = require('ws'); const mosaic = commander - .arguments(' '); + .arguments(' '); mosaic.action( async ( - auxiliaryChainIdentifier: string, graphWsPort: string, + cogatewayAddress: string, ) => { - const subGraphName = `mosaic/auxiliary-${auxiliaryChainIdentifier}`; + const subGraphName = `mosaic/auxiliary-${cogatewayAddress.substr(2, 22)}`; + Logger.info(`subgraph name for verification ${subGraphName}`); + Logger.info(`graph ws port ${graphWsPort}`); const wsEndPoint = `ws://127.0.0.1:${graphWsPort}/subgraphs/name/${subGraphName}`; // Creates subscription client const subscriptionClient = new SubscriptionClient(wsEndPoint, { - reconnect: true, - }, - WebSocket); + reconnect: true, + }, + WebSocket); subscriptionClient.onConnected(() => { Logger.info(`Connected to sub graph: ${subGraphName}`); subscriptionClient.close(); @@ -35,4 +37,4 @@ mosaic.action( }); }, ) - .parse(process.argv); \ No newline at end of file + .parse(process.argv); diff --git a/tests/Graph/SubGraphDeployment/origin-verifier.ts b/tests/Graph/SubGraphDeployment/origin-verifier.ts index 4ff2e558..7eafacc9 100644 --- a/tests/Graph/SubGraphDeployment/origin-verifier.ts +++ b/tests/Graph/SubGraphDeployment/origin-verifier.ts @@ -8,21 +8,23 @@ import Logger from '../../../src/Logger'; import WebSocket = require('ws'); const mosaic = commander - .arguments(' '); + .arguments(' { - const subGraphName = `mosaic/origin-${auxiliaryChainIdentifier}`; + const subGraphName = `mosaic/origin-${gatewayAddresses.substr(2, 25)}` + Logger.info(`subgraph name for verification ${subGraphName}`); + Logger.info(`graph ws port ${graphWsPort}`); const wsEndPoint = `ws://127.0.0.1:${graphWsPort}/subgraphs/name/${subGraphName}`; // Creates subscription client const subscriptionClient = new SubscriptionClient(wsEndPoint, { - reconnect: true, - }, - WebSocket); + reconnect: true, + }, + WebSocket); subscriptionClient.onConnected(() => { Logger.info(`Connected to sub graph: ${subGraphName}`); subscriptionClient.close(); diff --git a/tests/smoke.sh b/tests/smoke.sh index 9942858b..5f26cd4d 100755 --- a/tests/smoke.sh +++ b/tests/smoke.sh @@ -42,8 +42,34 @@ function stop_nodes { stop_node ropsten stop_node 1407 stop_node 1406 + stop_node dev-origin + stop_node dev-auxiliary } +# Deploy subgraph +# $1 origin chain identifier +# $2 aux chain identifier +# $3 chain {origin, auxiliary} +# $4 graph admin rpc port +# $5 graph IPFS port +function deploy_subgraph { + info "Deploying origin subraph." + try_silent "./mosaic subgraph $1 $2 $3 http://localhost:$4 http://localhost:$5" +} + +# Deploy subgraph +# $1 origin chain identifier +# $2 aux chain identifier +# $3 chain {origin, auxiliary} +# $4 graph admin rpc port +# $5 graph IPFS port +# gateway config +function deploy_subgraph_gateway_config { + info "Deploying origin subraph." + try_silent "./mosaic subgraph $1 $2 $3 http://localhost:$4 http://localhost:$5 --gateway-config ~/.mosaic/$1/$2/$( echo "$6" | tr -s '[:upper:]' '[:lower:]' ).json" +} + + # Tries a command without output. Errors if the command does not execute successfully. function try_silent { eval $1 2>&1 || error "$2" @@ -99,35 +125,66 @@ function rpc_node_try { } function rpc_origin_sub_graph_try { - info "Checking RPC connection to origin sub graph at port $2 on node for $1." - try_silent "./node_modules/.bin/ts-node tests/Graph/SubGraphDeployment/origin-verifier.ts $1 $2" "Origin sub graph at port $2 was expected to be deployed on $1, but wasn't." + info "Checking RPC connection to origin sub graph at port $1 on node for $2." + try_silent "./node_modules/.bin/ts-node tests/Graph/SubGraphDeployment/origin-verifier.ts $1 $2" "Origin sub graph at port $1 was expected to be deployed on $2, but wasn't." } function rpc_auxiliary_sub_graph_try { info "Checking RPC connection to auxiliary sub graph for $1 chain on node." - try_silent "./node_modules/.bin/ts-node tests/Graph/SubGraphDeployment/auxiliary-verifier.ts $1 6$1" "Auxiliary sub graph was expected to be deployed, but wasn't." + try_silent "./node_modules/.bin/ts-node tests/Graph/SubGraphDeployment/auxiliary-verifier.ts 6$1 $2" "Auxiliary sub graph was expected to be deployed, but wasn't." } # Making sure the mosaic command exists (we are in the right directory). try_silent "ls mosaic" "Script must be run from the mosaic chains root directory so that the required node modules are available." info "Starting node one by one and verifying if all services for them are running." +# 1406 config +GRAPH_ADMIN_RPC_1406=9426 +GRAPH_IPFS_1406=6407 +OST_COGATEWAY_ADDRESS_1406=0x02cffaa1e06c28021fff6b36d9e418a97b3de2fc + +# 1407 config +GRAPH_ADMIN_RPC_1407=9427 +GRAPH_IPFS_1407=6408 +OST_COGATEWAY_ADDRESS_1407=0xf690624171fe06d02d2f4250bff17fe3b682ebd1 + +# ropsten config +GRAPH_ADMIN_RPC_ROPSTEN=8023 +GRAPH_IPFS_ROPSTEN=5004 +GRAPH_WS_PORT_ROPSTEN=60003 +OST_GATEWAY_ADDRESS_ROPSTEN_1406=0x04df90efbedf393361cdf498234af818da14f562 +OST_GATEWAY_ADDRESS_ROPSTEN_1407=0x31c8870c76390c5eb0d425799b5bd214a2600438 + +# Dev chain config +GRAPH_ADMIN_RPC_DEV_ORIGIN=9535 +GRAPH_IPFS_DEV_ORIGIN=6516 +GRAPH_WS_PORT_DEV_ORIGIN=61515 +OST_GATEWAY_ADDRESS_DEV_ORIGIN_WETH=0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe +OST_CO_GATEWAY_ADDRESS_DEV_ORIGIN_WETH=0xc6fF898ceBf631eFb58eEc7187E4c1f70AE8d943 + +DEV_AUXILIARY_CHAIN_ID=1000 +GRAPH_ADMIN_RPC_DEV_AUXILIARY=9020 +GRAPH_IPFS_DEV_AUXILIARY=6001 start_auxiliary_node 1406 grep_try 1406 geth rpc_node_try 1406 -rpc_auxiliary_sub_graph_try 1406 +deploy_subgraph ropsten 1406 auxiliary $GRAPH_ADMIN_RPC_1406 $GRAPH_IPFS_1406 +rpc_auxiliary_sub_graph_try 1406 $OST_COGATEWAY_ADDRESS_1406 start_auxiliary_node 1407 grep_try 1407 geth rpc_node_try 1407 -rpc_auxiliary_sub_graph_try 1407 +deploy_subgraph ropsten 1407 auxiliary $GRAPH_ADMIN_RPC_1407 $GRAPH_IPFS_1407 +rpc_auxiliary_sub_graph_try 1407 $OST_COGATEWAY_ADDRESS_1407 start_origin_node ropsten geth grep_try ropsten geth rpc_node_try "0003" # Given like this as it is used for the port in `rpc_node_try`. -rpc_origin_sub_graph_try 1406 60003 -rpc_origin_sub_graph_try 1407 60003 +deploy_subgraph ropsten 1406 origin $GRAPH_ADMIN_RPC_ROPSTEN $GRAPH_IPFS_ROPSTEN +deploy_subgraph ropsten 1407 origin $GRAPH_ADMIN_RPC_ROPSTEN $GRAPH_IPFS_ROPSTEN +rpc_origin_sub_graph_try $GRAPH_WS_PORT_ROPSTEN $OST_GATEWAY_ADDRESS_ROPSTEN_1406 +rpc_origin_sub_graph_try $GRAPH_WS_PORT_ROPSTEN $OST_GATEWAY_ADDRESS_ROPSTEN_1407 # Stop and start some nodes and make sure they are or are not running. stop_node ropsten @@ -145,5 +202,12 @@ grep_fail ropsten geth start_origin_node ropsten parity grep_try ropsten parity +# Deploy subgraph with gateway config +start_origin_node dev-origin geth +start_auxiliary_node dev-auxiliary geth +deploy_subgraph_gateway_config dev-origin $DEV_AUXILIARY_CHAIN_ID origin $GRAPH_ADMIN_RPC_DEV_ORIGIN $GRAPH_IPFS_DEV_ORIGIN $OST_GATEWAY_ADDRESS_DEV_ORIGIN_WETH +deploy_subgraph_gateway_config dev-origin $DEV_AUXILIARY_CHAIN_ID auxiliary $GRAPH_ADMIN_RPC_DEV_AUXILIARY $GRAPH_IPFS_DEV_AUXILIARY $OST_GATEWAY_ADDRESS_DEV_ORIGIN_WETH +rpc_origin_sub_graph_try $GRAPH_WS_PORT_DEV_ORIGIN $OST_GATEWAY_ADDRESS_DEV_ORIGIN_WETH +rpc_auxiliary_sub_graph_try $DEV_AUXILIARY_CHAIN_ID $OST_CO_GATEWAY_ADDRESS_DEV_ORIGIN_WETH # When done, stop all nodes. stop_nodes