├── test_integration └── mosaic-create │ ├── integration_test_password.txt │ └── Utils.ts ├── tests ├── package │ ├── .gitignore │ └── index.ts ├── Validator │ ├── isValidAddress.test.ts │ ├── isValidOriginChain.test.ts │ └── isValidAuxiliaryChain.test.ts ├── Config │ ├── testdata │ │ └── 0xae02c7b1c324a8d94a564bc8d713df89eae441fe.json │ ├── fromFile.test.ts │ └── fromChain.test.ts ├── Utils │ └── toChecksumAddress.test.ts ├── test_utils │ ├── assert.ts │ └── SpyAssert.ts ├── ChainInfo │ └── isDevChain.test.ts ├── FileSystem │ └── resolveHomePath.test.ts ├── Graph │ └── SubGraphDeployment │ │ ├── origin-verifier.ts │ │ └── auxiliary-verifier.ts ├── package.sh ├── Node │ └── NodeFactory.test.ts ├── NewChain │ ├── OriginChainInteract │ │ └── SetCoanchorAddress.test.ts │ └── AuxiliaryChainInteract │ │ └── SetCoanchorAddress.test.ts └── Directory.test.ts ├── mosaic ├── chains ├── ethereum │ ├── 1414 │ │ ├── bootnodes │ │ └── genesis.json │ ├── bootnodes │ └── mosaic.json ├── goerli │ ├── 1405 │ │ ├── bootnodes │ │ └── gateway-0x6649c6FF3629aE875b91B6C1551139c9feaA2514 │ │ │ └── gateway-config.json │ └── bootnodes └── ropsten │ ├── 1406 │ ├── bootnodes │ └── genesis.json │ ├── 1407 │ ├── bootnodes │ └── genesis.json │ ├── bootnodes │ └── mosaic.json ├── src ├── NewChain │ └── Proof.ts ├── Logger.ts ├── bin │ ├── mosaic-list.ts │ ├── mosaic-logs.ts │ ├── DevChainOptions.ts │ ├── mosaic-stop.ts │ ├── mosaic-attach.ts │ ├── mosaic-create.ts │ ├── mosaic-verify-chain.ts │ ├── mosaic-setup-stake-pool.ts │ ├── mosaic.ts │ ├── mosaic-subgraph.ts │ ├── mosaic-setup-redeem-pool.ts │ ├── mosaic-libraries.ts │ ├── Validator.ts │ ├── GraphOptions.ts │ └── NodeOptions.ts ├── Integer.ts ├── Node │ ├── NodeDescription.ts │ ├── NodeFactory.ts │ ├── ParityNode.ts │ ├── ChainInfo.ts │ └── Node.ts ├── Graph │ ├── GraphDescription.ts │ └── docker-compose.yml ├── Exception.ts ├── Config │ ├── PublishMosaicConfig.ts │ ├── GatewayAddresses.ts │ ├── GatewayConfig.schema.json │ ├── InitConfig.ts │ └── GatewayConfig.ts ├── Utils.ts ├── Shell.ts ├── lib │ ├── StakePool.ts │ ├── RedeemPool.ts │ └── SubGraph.ts ├── FileSystem .ts └── Directory.ts ├── docker ├── Dockerfile ├── 0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe.json ├── readme.md └── startup.sh ├── package.sh ├── tsconfig.json ├── index.ts ├── graph ├── auxiliary │ ├── package.json │ ├── src │ │ ├── AnchorMapping.ts │ │ └── RedeemPoolMapping.ts │ ├── generated │ │ ├── AnchorSchema.ts │ │ └── Contract │ │ │ └── Anchor.ts │ ├── subgraph.yaml.mustache │ └── schema.graphql └── origin │ ├── package.json │ ├── src │ ├── AnchorMapping.ts │ └── OSTComposerMapping.ts │ ├── generated │ ├── AnchorSchema.ts │ └── Contract │ │ └── Anchor.ts │ ├── subgraph.yaml.mustache │ └── schema.graphql ├── tools ├── abi_downloader │ ├── run.sh │ └── index.ts └── whitelistworker.ts ├── .eslintrc.json ├── initialize ├── 500.json └── example.json ├── .gitignore ├── .travis.yml ├── package.json └── abi ├── 0.10 └── Anchor.json └── 0.12 └── Anchor.json /test_integration/mosaic-create/integration_test_password.txt: -------------------------------------------------------------------------------- 1 | 123 2 | 123 3 | -------------------------------------------------------------------------------- /tests/package/.gitignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | node_modules 4 | openst-mosaic-chains-*.tgz 5 | -------------------------------------------------------------------------------- /mosaic: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TS_NODE_FILES=true; TS_NODE_TRANSPILE_ONLY=true; node -r ts-node/register ./src/bin/mosaic.ts $@ 4 | -------------------------------------------------------------------------------- /tests/package/index.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { Node } from '@openst/mosaic-chains'; 3 | 4 | assert.strictEqual( 5 | Node.network, 6 | 'mosaic', 7 | ); 8 | -------------------------------------------------------------------------------- /chains/ethereum/1414/bootnodes: -------------------------------------------------------------------------------- 1 | enode://c677ea2e455828db3d2f7bbc232518cfe683475edae66ab46fcf27bc21efb991fb37776f815d679a449374e7363692aee3c89a70f6b8cd754abc95fa11a3c757@99.80.181.153:30301 -------------------------------------------------------------------------------- /chains/goerli/1405/bootnodes: -------------------------------------------------------------------------------- 1 | enode://9c871de33589c0d8a890a6d5157f08b81652b9acdc6307f6b756bc97e742fe89b13d18f4e42f49a5789786e028d3d5ab9a2c8b5e3a260ed201ad3533657839bf@3.92.64.15:31405 2 | -------------------------------------------------------------------------------- /chains/ropsten/1406/bootnodes: -------------------------------------------------------------------------------- 1 | enode://edaed9d130c1d4580a17ecf572500c2f30ef4bfc0fbee3764a1a23a837b8ee10a3698051c5339473132343e69729b243f565873b233031467599a17e08252de0@99.80.80.138:30301 -------------------------------------------------------------------------------- /chains/ropsten/1407/bootnodes: -------------------------------------------------------------------------------- 1 | enode://e60ff2d4ed05213b8ae32ca00fd7a0252d7fe8fea48911043c3ea3db8749130014e952ee22f5600e649eb8e25fbffb64e4e1f7feb915fd2d7d518b86973c24af@99.80.85.164:30301 -------------------------------------------------------------------------------- /src/NewChain/Proof.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Proof is a container for data that is required for a gateway merkle proof. 3 | */ 4 | export default class Proof { 5 | readonly blockNumber: number; 6 | 7 | readonly accountData: string; 8 | 9 | readonly accountProof: string; 10 | 11 | readonly storageProof: string; 12 | 13 | readonly stateRoot: string; 14 | } 15 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ethereum/client-go:v1.9.5 2 | ADD /docker/chain_data /chain_data 3 | ADD /docker/configs /configs 4 | ADD /docker/root /root 5 | ADD /docker/startup.sh / 6 | RUN apk update && apk upgrade && apk add bash && apk add bash-doc && apk add bash-completion 7 | RUN chmod +x ./startup.sh 8 | RUN export PATH=$PATH:/startup.sh 9 | ENTRYPOINT ["./startup.sh"] 10 | CMD [""] 11 | EXPOSE 8545 8546 12 | -------------------------------------------------------------------------------- /src/Logger.ts: -------------------------------------------------------------------------------- 1 | import * as winston from 'winston'; 2 | 3 | export default winston.createLogger({ 4 | level: 'info', 5 | format: winston.format.combine( 6 | winston.format.timestamp(), 7 | winston.format.simple(), 8 | ), 9 | defaultMeta: { service: 'mosaic-chains' }, 10 | transports: [new winston.transports.Console()], 11 | exceptionHandlers: [new winston.transports.Console()], 12 | }); 13 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf ./lib 4 | npm ci 5 | 6 | ./node_modules/.bin/tsc 7 | cp -r ./chains ./lib/ 8 | cp -r ./graph ./lib/ 9 | cp -r ./abi ./lib/ 10 | cp ./src/Graph/docker-compose.yml ./lib/src/Graph/docker-compose.yml 11 | cp ./src/Config/MosaicConfig.schema.json ./lib/src/Config/MosaicConfig.schema.json 12 | cp ./src/Config/GatewayConfig.schema.json ./lib/src/Config/GatewayConfig.schema.json 13 | -------------------------------------------------------------------------------- /chains/goerli/bootnodes: -------------------------------------------------------------------------------- 1 | enode://46add44b9f13965f7b9875ac6b85f016f341012d84f975377573800a863526f4da19ae2c620ec73d11591fa9510e992ecc03ad0751f53cc02f7c7ed6d55c7291@94.237.54.114:30313,enode://c766806a407488a5d7335bd29af7681616ef6a34cd7e35a0b5bd35bdbbf285aa76a53abbb9294ee5caa3e83e0e145667f50a099e0dccfa9cd91db793640dfb8b@54.80.223.88:30303,enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303 2 | -------------------------------------------------------------------------------- /src/bin/mosaic-list.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as mosaic from 'commander'; 4 | import Node from '../Node/Node'; 5 | import Shell from '../Shell'; 6 | 7 | mosaic 8 | .action(() => { 9 | const args = [ 10 | 'ps', 11 | '-a', 12 | '--filter', `name=${Node.prefix}`, 13 | '--format', 'table {{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.Image}}\t{{.ID}}', 14 | ]; 15 | Shell.executeDockerCommand(args); 16 | }) 17 | .parse(process.argv); 18 | -------------------------------------------------------------------------------- /chains/ethereum/bootnodes: -------------------------------------------------------------------------------- 1 | enode://fc73f1528df7b3d955e8e0b913d3481010d74c25242e4ced9eadbde5f57c2409827b0f516f26f66163f14d3c5bcc613c6252326015607ae388b361b640d31b87@54.242.22.54:30303,enode://fd691970b338f13fa4aa709ab0abb1889a3c51a7025e001a3c19ba7ab1dbc564becf18ecc011ffa72fa9bc338ff0d4ea05dd15461d3dee5ac89f4c5093508da2@5.102.239.168:33284,enode://b026d280d4b15180723b311bc4e63a8b3d207a8f3fcce458dfb9b181a9f69a9ada2a4f7ea34b5e6dca1130242373d6f046b87c620d3e3c5de746d75c6479fa9a@54.152.101.211:55628 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "outDir": "./lib", 6 | "sourceMap": true, 7 | "target": "es5", 8 | "types": [ 9 | "chai", 10 | "fs-extra", 11 | "node", 12 | "mocha", 13 | "web3" 14 | ], 15 | "resolveJsonModule": true 16 | }, 17 | "include": [ 18 | "index.ts", 19 | "src/**/*.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import Directory from './src/Directory'; 2 | import Logger from './src/Logger'; 3 | import Node from './src/Node/Node'; 4 | import NodeDescription from './src/Node/NodeDescription'; 5 | import NodeFactory from './src/Node/NodeFactory'; 6 | import Shell from './src/Shell'; 7 | import MosaicConfig from './src/Config/MosaicConfig'; 8 | import GatewayConfig from './src/Config/GatewayConfig'; 9 | 10 | export { 11 | Directory, 12 | Logger, 13 | Node, 14 | NodeDescription, 15 | NodeFactory, 16 | Shell, 17 | MosaicConfig, 18 | GatewayConfig 19 | }; 20 | -------------------------------------------------------------------------------- /src/Integer.ts: -------------------------------------------------------------------------------- 1 | import Logger from './Logger'; 2 | 3 | export default class Integer { 4 | /** 5 | * Convert a given decimal string to a number. Always radix 10. 6 | * @throws If the given input is not parseable into an int. 7 | */ 8 | public static parseString(input: string): number { 9 | const output = parseInt(input, 10); 10 | 11 | if (Number.isNaN(output)) { 12 | const message = 'could not convert input to integer'; 13 | Logger.error(message, { input }); 14 | throw new Error(message); 15 | } 16 | 17 | return output; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chains/ropsten/bootnodes: -------------------------------------------------------------------------------- 1 | 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 2 | -------------------------------------------------------------------------------- /graph/auxiliary/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ost-auxiliary-subgraph", 3 | "author": "OpenST Ltd.", 4 | "license": "LGPL-3.0-only", 5 | "scripts": { 6 | "codegen": "graph codegen", 7 | "build": "graph build", 8 | "create-local": "graph create --node http://localhost:8020/ openst/ost-anchor", 9 | "remove-local": "graph remove --node http://localhost:8020/ openst/ost-anchor", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 openst/ost-anchor" 11 | }, 12 | "dependencies": { 13 | "@graphprotocol/graph-cli": "0.12.0", 14 | "@graphprotocol/graph-ts": "0.11.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /graph/origin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ost-origin-subgraph", 3 | "author": "OpenST Ltd.", 4 | "license": "LGPL-3.0-only", 5 | "scripts": { 6 | "codegen": "graph codegen", 7 | "build": "graph build", 8 | "create-local": "graph create --node http://localhost:8020/ openst/ost-composer", 9 | "remove-local": "graph remove --node http://localhost:8020/ openst/ost-composer", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 openst/ost-composer" 11 | }, 12 | "dependencies": { 13 | "@graphprotocol/graph-cli": "0.12.0", 14 | "@graphprotocol/graph-ts": "0.11.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Validator/isValidAddress.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import { assert } from 'chai'; 3 | import Validator from '../../src/bin/Validator'; 4 | 5 | describe('Validation.isValidAddress', () => { 6 | it('should return true for valid address', () => { 7 | assert.isTrue( 8 | Validator.isValidAddress('0x0000000000000000000000000000000000000001'), 'Must return true for 0x0000000000000000000000000000000000000001', 9 | ); 10 | }); 11 | 12 | it('should return false for valid address', () => { 13 | assert.isNotTrue( 14 | Validator.isValidAddress('0x000000001'), 'Must return true for 0x000000001', 15 | ); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tools/abi_downloader/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | default_version='0.10.0' 3 | package='@openst/mosaic-contracts' 4 | default="the default value" 5 | read -p "Enter mosaic contracts version [default=$default_version] " version 6 | : ${version:=${default_version}} 7 | 8 | version_count=$(grep -o '\.' <<<"$version" | grep -c .) 9 | if ((version_count ==1)) || ((version_count ==2)) 10 | then 11 | mosaic_contract_package=${package}$"@"${version} 12 | echo ${mosaic_contract_package} 13 | npm install ${mosaic_contract_package} --no-save 14 | ts-node tools/abi_downloader/index.ts ${version} 15 | else 16 | echo "Enter version in format MAJOR.MINOR or MAJOR.MINOR.PATCH" 17 | exit 18 | fi 19 | -------------------------------------------------------------------------------- /graph/auxiliary/src/AnchorMapping.ts: -------------------------------------------------------------------------------- 1 | import { StateRootAvailable as StateRootAvailableEvent } from '../generated/Contract/Anchor'; 2 | import { StateRootAvailable } from '../generated/AnchorSchema'; 3 | 4 | export function handleStateRootAvailable(event: StateRootAvailableEvent): void { 5 | let entity = new StateRootAvailable( 6 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 7 | ) 8 | entity._blockHeight = event.params._blockHeight 9 | entity._stateRoot = event.params._stateRoot 10 | entity.blockNumber = event.block.number 11 | entity.blockHash = event.block.hash 12 | entity.contractAddress = event.address 13 | entity.uts = event.block.timestamp 14 | entity.save() 15 | } -------------------------------------------------------------------------------- /graph/origin/src/AnchorMapping.ts: -------------------------------------------------------------------------------- 1 | import { StateRootAvailable as StateRootAvailableEvent } from '../generated/Contract/Anchor'; 2 | import { StateRootAvailable } from '../generated/AnchorSchema'; 3 | 4 | export function handleStateRootAvailable(event: StateRootAvailableEvent): void { 5 | let entity = new StateRootAvailable( 6 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 7 | ) 8 | entity._blockHeight = event.params._blockHeight 9 | entity._stateRoot = event.params._stateRoot 10 | entity.blockNumber = event.block.number 11 | entity.blockHash = event.block.hash 12 | entity.contractAddress = event.address 13 | entity.uts = event.block.timestamp 14 | entity.save() 15 | } -------------------------------------------------------------------------------- /docker/0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe.json: -------------------------------------------------------------------------------- 1 | { 2 | "mosaicConfigFilePath":"~/.mosaic/dev-origin/mosaic.json", 3 | "auxChainId":1000, 4 | "originContracts":{ 5 | "baseTokenAddress":"0x9AC77F4c0ca4D0F2142D7a77175cf4F1295fb2d8", 6 | "valueTokenAddress":"0x8e183Fd2cd55C7C05bBf4FAC989740f69e559A6d", 7 | "gatewayOrganizationAddress":"0x3f99f42d226A0CD1C1Fcae1e8dC11b2f7a9DcE4B", 8 | "eip20GatewayAddress":"0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe" 9 | }, 10 | "auxiliaryContracts":{ 11 | "coGatewayOrganizationAddress":"0x2D586C7E220839a9284888B10aDF4823AcD6EdF3", 12 | "utilityTokenAddress":"0x62F8729C1C282C231a22252e90CE9735533D2518", 13 | "eip20CoGatewayAddress":"0xc6fF898ceBf631eFb58eEc7187E4c1f70AE8d943" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Node/NodeDescription.ts: -------------------------------------------------------------------------------- 1 | import Directory from '../Directory'; 2 | 3 | /** 4 | * Describes the properties of a node. 5 | */ 6 | export default class NodeDescription { 7 | public mosaicDir: string = Directory.getDefaultMosaicDataDir; 8 | 9 | public port: number = 30303; 10 | 11 | public rpcPort: number = 8545; 12 | 13 | public websocketPort: number = 8645; 14 | 15 | public keepAfterStop: boolean = false; 16 | 17 | public unlock: string = ''; 18 | 19 | public password: string = ''; 20 | 21 | public originChain: string = ''; 22 | 23 | public client: string = ''; 24 | 25 | public bootNodesFile?: string; 26 | 27 | public clefSigner?: string; 28 | 29 | public forceInit?: boolean; 30 | 31 | constructor(readonly chain: string) { } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Config/testdata/0xae02c7b1c324a8d94a564bc8d713df89eae441fe.json: -------------------------------------------------------------------------------- 1 | { 2 | "mosaicConfigFilePath":"./tests/Config/testdata/mosaic.json", 3 | "auxChainId":1000, 4 | "originContracts":{ 5 | "baseTokenAddress":"0x9ac77f4c0ca4d0f2142d7a77175cf4f1295fb2d8", 6 | "valueTokenAddress":"0x8e183Fd2cd55C7C05bBf4FAC989740f69e559A6d", 7 | "gatewayOrganizationAddress":"0x3f99f42d226A0CD1C1Fcae1e8dC11b2f7a9DcE4B", 8 | "eip20GatewayAddress":"0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe" 9 | }, 10 | "auxiliaryContracts":{ 11 | "coGatewayOrganizationAddress":"0x2D586C7E220839a9284888B10aDF4823AcD6EdF3", 12 | "utilityTokenAddress":"0x62F8729C1C282C231a22252e90CE9735533D2518", 13 | "eip20CoGatewayAddress":"0xc6fF898ceBf631eFb58eEc7187E4c1f70AE8d943" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/Utils/toChecksumAddress.test.ts: -------------------------------------------------------------------------------- 1 | import * as sinon from 'sinon'; 2 | import Web3 = require('web3'); 3 | 4 | import assert from '../test_utils/assert'; 5 | import SpyAssert from '../test_utils/SpyAssert'; 6 | import Utils from '../../src/Utils'; 7 | 8 | describe('Utils.toChecksumAddress()', () => { 9 | it('should be successfully', () => { 10 | let address = '0x123Ad'; 11 | 12 | const web3Spy = sinon.stub(Web3.utils, 'toChecksumAddress').returns(address); 13 | 14 | const actualAddress = Utils.toChecksumAddress(address); 15 | 16 | assert.strictEqual( 17 | actualAddress, 18 | address, 19 | 'Addresses are different', 20 | ); 21 | 22 | SpyAssert.assert(web3Spy, 1, [[address]]); 23 | 24 | sinon.restore(); 25 | }); 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /src/bin/mosaic-logs.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as mosaic from 'commander'; 4 | import Shell from '../Shell'; 5 | import Node from '../Node/Node'; 6 | import NodeDescription from '../Node/NodeDescription'; 7 | import NodeFactory from '../Node/NodeFactory'; 8 | import ChainInfo from '../Node/ChainInfo'; 9 | 10 | mosaic 11 | .arguments('') 12 | .action((chain: string) => { 13 | const chainIdentifier = ChainInfo.getChainParams(chain).chain; 14 | // Chain can't be validated as origin chain id is not received for aux chain. 15 | const node: Node = NodeFactory.create(new NodeDescription(chainIdentifier)); 16 | const args = [ 17 | 'logs', 18 | '-f', 19 | node.getContainerName(), 20 | ]; 21 | Shell.executeDockerCommand(args); 22 | }) 23 | .parse(process.argv); 24 | -------------------------------------------------------------------------------- /src/bin/DevChainOptions.ts: -------------------------------------------------------------------------------- 1 | import ChainInfo from '../Node/ChainInfo'; 2 | 3 | export const DEV_CHAIN_ROOT = 'dev-origin'; 4 | 5 | /** 6 | * For the ease of use of dev chains, the input provided to the mosaic commands 7 | * are origin or auxiliary. This class helps to identify if the indented chain 8 | * and provides the appropriate dev chain params. 9 | */ 10 | export default class DevChainOptions { 11 | /** 12 | * Identify it the input provided in the mosaic command is for dev chains. 13 | * @param chain Chain provided in the mosaic command. 14 | * @param options Options provided in the mosaic command. 15 | */ 16 | public static isDevChain(chain: string, options = { origin: '' }): boolean { 17 | return ChainInfo.isDevChain(chain) 18 | || ChainInfo.isDevOriginChain(options.origin); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Graph/GraphDescription.ts: -------------------------------------------------------------------------------- 1 | import Directory from '../Directory'; 2 | 3 | /** 4 | * Describes the properties of a graph. 5 | */ 6 | export default class GraphDescription { 7 | public mosaicDir: string = Directory.getDefaultMosaicDataDir; 8 | 9 | public ethereumRpcPort: number = 8545; 10 | 11 | public rpcPort: number = 8000; 12 | 13 | public websocketPort: number = 8001; 14 | 15 | public rpcAdminPort: number = 8020; 16 | 17 | public ipfsPort: number = 5001; 18 | 19 | public postgresPort: number = 5432; 20 | 21 | public postgresUser: string; 22 | 23 | public postgresPassword: string; 24 | 25 | public postgresDatabase: string; 26 | 27 | public keepAfterStop: boolean = false; 28 | 29 | public ethereumClient: string; 30 | 31 | public originChain?: string; 32 | 33 | constructor(readonly chain: string) { } 34 | } 35 | -------------------------------------------------------------------------------- /src/bin/mosaic-stop.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as mosaic from 'commander'; 4 | import NodeFactory from '../Node/NodeFactory'; 5 | import NodeDescription from '../Node/NodeDescription'; 6 | import Node from '../Node/Node'; 7 | import GraphDescription from '../Graph/GraphDescription'; 8 | import Graph from '../Graph/Graph'; 9 | import ChainInfo from '../Node/ChainInfo'; 10 | 11 | mosaic 12 | .arguments('') 13 | .action((chains: string[]) => { 14 | // Chain can't be validated as origin chain id is not received for aux chain. 15 | for (const chain of chains) { 16 | const chainIdentifier = ChainInfo.getChainParams(chain).chain; 17 | const node: Node = NodeFactory.create(new NodeDescription(chainIdentifier)); 18 | node.stop(); 19 | const graph: Graph = new Graph(new GraphDescription(chainIdentifier)); 20 | graph.stop(); 21 | } 22 | }) 23 | .parse(process.argv); 24 | -------------------------------------------------------------------------------- /tests/test_utils/assert.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019 OpenST Ltd. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // ---------------------------------------------------------------------------- 16 | 17 | import chai = require('chai'); 18 | import chaiAsPromised = require('chai-as-promised'); 19 | chai.use(chaiAsPromised); 20 | const { assert } = chai; 21 | 22 | export default assert; 23 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb-base", 4 | "plugin:@typescript-eslint/recommended" 5 | ], 6 | "parser": "@typescript-eslint/parser", 7 | "parserOptions": { 8 | "sourceType": "module", 9 | "ecmaFeatures": { 10 | "modules": true 11 | } 12 | }, 13 | "plugins": [ 14 | "@typescript-eslint", 15 | "json" 16 | ], 17 | "rules": { 18 | "@typescript-eslint/indent": ["error", 2], 19 | "import/no-extraneous-dependencies": "off", 20 | "no-underscore-dangle": "off", 21 | "strict": "off" 22 | }, 23 | "env": { 24 | "mocha": true, 25 | "node": true, 26 | "es6": true 27 | }, 28 | "globals": { 29 | "artifacts": false, 30 | "contract": false, 31 | "assert": false 32 | }, 33 | "settings": { 34 | "import/resolver": { 35 | "node": { 36 | "extensions": [".js", ".jsx", ".ts", ".tsx", ".d.ts"] 37 | } 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/Exception.ts: -------------------------------------------------------------------------------- 1 | export class InvalidMosaicConfigException extends Error { 2 | public constructor(message: string) { 3 | super(message); 4 | this.name = 'InvalidMosaicConfigException'; 5 | this.message = message; 6 | } 7 | } 8 | 9 | export class MosaicConfigNotFoundException extends Error { 10 | public constructor(message: string) { 11 | super(message); 12 | this.name = 'MosaicConfigNotFoundException'; 13 | this.message = message; 14 | } 15 | } 16 | 17 | export class InvalidGatewayConfigException extends Error { 18 | public constructor(message: string) { 19 | super(message); 20 | this.name = 'InvalidGatewayConfigException'; 21 | this.message = message; 22 | } 23 | } 24 | 25 | export class GatewayConfigNotFoundException extends Error { 26 | public constructor(message: string) { 27 | super(message); 28 | this.name = 'GatewayConfigNotFoundException'; 29 | this.message = message; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/ChainInfo/isDevChain.test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import ChainInfo from '../../src/Node/ChainInfo'; 3 | 4 | describe('ChainInfo.isDevChain()', () => { 5 | it('should return true for valid dev chain', async () => { 6 | assert.isTrue( 7 | ChainInfo.isDevChain('dev-origin'), 8 | 'dev-origin must be a valid dev chain', 9 | ); 10 | 11 | assert.isTrue( 12 | ChainInfo.isDevChain('dev-auxiliary'), 13 | 'dev-auxiliary must be a valid dev chain', 14 | ); 15 | 16 | assert.isTrue( 17 | ChainInfo.isDevChain('1000'), 18 | '1000 must be a valid dev chain', 19 | ); 20 | 21 | assert.isTrue( 22 | ChainInfo.isDevChain('1515'), 23 | '1515 must be a valid dev chain', 24 | ); 25 | }); 26 | 27 | it('should return false for invalid dev chain', async () => { 28 | assert.isNotTrue( 29 | ChainInfo.isDevChain('ropsten'), 30 | 'ropsten must be an invalid dev chain', 31 | ); 32 | 33 | assert.isNotTrue( 34 | ChainInfo.isDevChain('1406'), 35 | '1406 must be an invalid dev chain', 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/FileSystem/resolveHomePath.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import { assert } from 'chai'; 3 | import FileSystem from '../../src/FileSystem '; 4 | 5 | describe('FileSystem.resolveHomeDirectory()', () => { 6 | it('Should resolve home path', async () => { 7 | const unresolvedHomePath = '~/.mosaic/goerli/mosaic.json'; 8 | const filePath = FileSystem.resolveHomePath(unresolvedHomePath); 9 | const expectedFilePath = `${process.env.HOME}/.mosaic/goerli/mosaic.json`; 10 | assert.strictEqual( 11 | filePath, 12 | expectedFilePath, 13 | 'Expected resolved file path is not equal to actual file path', 14 | ); 15 | }); 16 | 17 | it('Should return correct path if path does not have tilde', async () => { 18 | const unresolvedHomePath = '/user/.mosaic/goerli/mosaic.json'; 19 | const filePath = FileSystem.resolveHomePath(unresolvedHomePath); 20 | const expectedFilePath = '/user/.mosaic/goerli/mosaic.json'; 21 | assert.strictEqual( 22 | filePath, 23 | expectedFilePath, 24 | 'Expected resolved file path is not equal to actual file path', 25 | ); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /initialize/500.json: -------------------------------------------------------------------------------- 1 | { 2 | "originTxOptions": { 3 | "from": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F", 4 | "gasPrice": 3000000000 5 | }, 6 | "originBounty": "10", 7 | "auxiliaryBounty": "10", 8 | "originStakeGasPrice": "0", 9 | "originStakeGasLimit": "10000000", 10 | "originStakeBlocksToWait": 3, 11 | "originStakeAmount": "100000000000000000000", 12 | "originOstAddress": "0x9AC77F4c0ca4D0F2142D7a77175cf4F1295fb2d8", 13 | "originBurnerAddress": "0x0000000000000000000000000000000000000000", 14 | "auxiliaryBurnerAddress": "0x0000000000000000000000000000000000000000", 15 | "originAnchorBufferSize": "100", 16 | "auxiliaryAnchorBufferSize": "200", 17 | "originAnchorOrganizationOwner": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F", 18 | "originAnchorOrganizationAdmin": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F", 19 | "auxiliaryAnchorOrganizationOwner": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F", 20 | "auxiliaryAnchorOrganizationAdmin": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F", 21 | "originGatewayOrganizationOwner": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F", 22 | "auxiliaryCoGatewayAndOstPrimeOrganizationOwner": "0x40Ebe1ce3428b9CbAddBc20aF27F1c4780b7D49F" 23 | } 24 | -------------------------------------------------------------------------------- /initialize/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "originTxOptions": { 3 | "from": "0x7b829edc5d894bdc00a8d1717c90e9f369f41a18", 4 | "gasPrice": 3000000000 5 | }, 6 | "originBounty": "10", 7 | "auxiliaryBounty": "10", 8 | "originStakeGasPrice": "0", 9 | "originStakeGasLimit": "10000000", 10 | "originStakeBlocksToWait": 3, 11 | "originStakeAmount": "100", 12 | "originOstAddress": "0x325f05a75999347b7d8461BaEf274afAE0B8AE1c", 13 | "originBurnerAddress": "0x0000000000000000000000000000000000000000", 14 | "auxiliaryBurnerAddress": "0x0000000000000000000000000000000000000000", 15 | "originAnchorBufferSize": "100", 16 | "auxiliaryAnchorBufferSize": "200", 17 | "originAnchorOrganizationOwner": "0x4bdc7ED91924d23B8F8cD4f3AEA854a2555e506D", 18 | "originAnchorOrganizationAdmin": "0x4bdc7ED91924d23B8F8cD4f3AEA854a2555e506D", 19 | "auxiliaryAnchorOrganizationOwner": "0x68a1A34c1344bb8241cF72193AE0f3e6374856de", 20 | "auxiliaryAnchorOrganizationAdmin": "0xe94A4f27c9486fDc03A55Eb9B1018361bA9Ac2A4", 21 | "originGatewayOrganizationOwner": "0x4bdc7ED91924d23B8F8cD4f3AEA854a2555e506D", 22 | "auxiliaryCoGatewayAndOstPrimeOrganizationOwner": "0xcB8F6c8eBAD6F81fa0A3B956057551a259601A58" 23 | } 24 | -------------------------------------------------------------------------------- /chains/goerli/1405/gateway-0x6649c6FF3629aE875b91B6C1551139c9feaA2514/gateway-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mosaicConfigFilePath":"~/.mosaic/goerli/mosaic.json", 3 | "auxChainId":1405, 4 | "originContracts":{ 5 | "baseTokenAddress":"0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", 6 | "valueTokenAddress":"0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", 7 | "gatewayOrganizationAddress":"0x77DDDB66b12b2279A83D61fF443F1d8D16352C59", 8 | "eip20GatewayAddress":"0x6649c6FF3629aE875b91B6C1551139c9feaA2514", 9 | "gatewayLibAddress":"0x0f08341505C01b7bc05C95A99aB7427eD4C3195F", 10 | "messageBusAddress":"0xcaB4728ea030A04A183928884a6dcD83857d96Ba", 11 | "merklePatriciaLibAddress":"0x83aA52516805Ff323CA1033a12c6bF133d9AC17e" 12 | }, 13 | "auxiliaryContracts":{ 14 | "coGatewayOrganizationAddress":"0x47Ef953C4372457754d3471Ca2BB1b5d6AD8F245", 15 | "eip20CoGatewayAddress":"0x962e1404D6957d8fF56F8fE79a3fC2B670C3d578", 16 | "utilityTokenAddress":"0xBB5676d85d28DA039F982C07E4217fB0FDB2c2ef", 17 | "gatewayLibAddress":"0xa718f65c96a177d8967E8f6D67A7872b26771495", 18 | "messageBusAddress":"0x8F3578462BaF62e48Ad02820f4b7cd9788B200B6", 19 | "merklePatriciaLibAddress":"0x4473FBF3390e49e79b27D229b9BF230DAD78414e" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/bin/mosaic-attach.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as mosaic from 'commander'; 4 | import Shell from '../Shell'; 5 | import Node from '../Node/Node'; 6 | import NodeFactory from '../Node/NodeFactory'; 7 | import NodeDescription from '../Node/NodeDescription'; 8 | import ChainInfo from '../Node/ChainInfo'; 9 | 10 | mosaic 11 | .arguments('') 12 | // Chain can't be validated as origin chain id is not received for aux chain. 13 | .action((chain: string) => { 14 | const chainIdentifier = ChainInfo.getChainParams(chain).chain; 15 | const node: Node = NodeFactory.create(new NodeDescription(chainIdentifier)); 16 | 17 | const args = [ 18 | 'run', 19 | '--network', `${Node.network}`, 20 | '-it', 21 | '--rm', 22 | 'ethereum/client-go', 23 | 'attach', 24 | // Docker automatically resolves the container name to the IP if the container is spawned 25 | // within the same network (see `--network` above). 26 | // The port is always `8545` as that is always the same port for the nodes running *inside* 27 | // the containers. 28 | `http://${node.getContainerName()}:8545`, 29 | ]; 30 | Shell.executeDockerCommand(args); 31 | }) 32 | .parse(process.argv); 33 | -------------------------------------------------------------------------------- /src/Node/NodeFactory.ts: -------------------------------------------------------------------------------- 1 | import Node from './Node'; 2 | import GethNode from './GethNode'; 3 | import ParityNode from './ParityNode'; 4 | import NodeDescription from './NodeDescription'; 5 | import ChainInfo, { GETH_CLIENT, PARITY_CLIENT } from './ChainInfo'; 6 | 7 | /** 8 | * Builds node based on the given chain id. 9 | */ 10 | export default class NodeFactory { 11 | /** 12 | * Builds a node based on the `chain identifier` and returns it. 13 | * Any chain id that matches `ChainInfo.chainsSupportedByParityClient` will return a `ParityNode`. 14 | * Otherwise, it returns a `GethNode`. 15 | * @param nodeDescription The parameters of the requested node. 16 | * @returns The node; based of the given input. 17 | */ 18 | public static create(nodeDescription: NodeDescription): Node { 19 | if (!nodeDescription.client) { 20 | // default for aux chains is GETH and for origin chains in Parity 21 | nodeDescription.client = nodeDescription.originChain ? GETH_CLIENT : PARITY_CLIENT; 22 | } 23 | if (nodeDescription.client === PARITY_CLIENT) { 24 | return new ParityNode(nodeDescription); 25 | } 26 | const node = new GethNode(nodeDescription); 27 | node.readBootnodes(); 28 | 29 | return node; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chains/ethereum/1414/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 1414, 4 | "homesteadBlock": 1, 5 | "eip150Block": 2, 6 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 7 | "eip155Block": 3, 8 | "eip158Block": 3, 9 | "byzantiumBlock": 4, 10 | "clique": { 11 | "period": 3, 12 | "epoch": 30000 13 | } 14 | }, 15 | "nonce": "0x0", 16 | "timestamp": "0x5a71660b", 17 | "extraData": "0x7574696c69747920636861696e0000000000000000000000000000000000000082Af4EFe75Fa0fe0525f59e51453DF961528fD570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 18 | "gasLimit": "0x989680", 19 | "difficulty": "0x1", 20 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 21 | "coinbase": "0x0000000000000000000000000000000000000000", 22 | "alloc": { 23 | "2daded60b5d68d7e4f12307c4f6fa8fb11956f32": { 24 | "balance": "0x295be96e640669720000000" 25 | } 26 | }, 27 | "number": "0x0", 28 | "gasUsed": "0x0", 29 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" 30 | } -------------------------------------------------------------------------------- /chains/ropsten/1406/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 1406, 4 | "homesteadBlock": 1, 5 | "eip150Block": 2, 6 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 7 | "eip155Block": 3, 8 | "eip158Block": 3, 9 | "byzantiumBlock": 4, 10 | "clique": { 11 | "period": 3, 12 | "epoch": 30000 13 | } 14 | }, 15 | "nonce": "0x0", 16 | "timestamp": "0x5a71660b", 17 | "extraData": "0x7574696c69747920636861696e000000000000000000000000000000000000002a3217c2A5818C117a0E1A84A133262F9e1513Bc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 18 | "gasLimit": "0x989680", 19 | "difficulty": "0x1", 20 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 21 | "coinbase": "0x0000000000000000000000000000000000000000", 22 | "alloc": { 23 | "537f8cdc8c706650f497bcb1eee2fc8ceedc19bf": { 24 | "balance": "0x295be96e640669720000000" 25 | } 26 | }, 27 | "number": "0x0", 28 | "gasUsed": "0x0", 29 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" 30 | } -------------------------------------------------------------------------------- /chains/ropsten/1407/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 1407, 4 | "homesteadBlock": 1, 5 | "eip150Block": 2, 6 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 7 | "eip155Block": 3, 8 | "eip158Block": 3, 9 | "byzantiumBlock": 4, 10 | "clique": { 11 | "period": 3, 12 | "epoch": 30000 13 | } 14 | }, 15 | "nonce": "0x0", 16 | "timestamp": "0x5a71660b", 17 | "extraData": "0x7574696c69747920636861696e00000000000000000000000000000000000000d3E8E61c23f9302CB0FdA62bc7696064990A17080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 18 | "gasLimit": "0x989680", 19 | "difficulty": "0x1", 20 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 21 | "coinbase": "0x0000000000000000000000000000000000000000", 22 | "alloc": { 23 | "537f8cdc8c706650f497bcb1eee2fc8ceedc19bf": { 24 | "balance": "0x295be96e640669720000000" 25 | } 26 | }, 27 | "number": "0x0", 28 | "gasUsed": "0x0", 29 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" 30 | } -------------------------------------------------------------------------------- /src/Config/PublishMosaicConfig.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import Directory from '../Directory'; 3 | import FileSystem from '../FileSystem '; 4 | 5 | /** 6 | * Publish Mosaic Config. 7 | */ 8 | export default class PublishMosaicConfig { 9 | /** 10 | * Copies the contents from chains/ directory to mosaic home 11 | * directory if it does not exists. This is called from all mosaic commands, 12 | * to ensure that the required files exists in the mosaic home directory. 13 | */ 14 | public static tryPublish(originChain: string): void { 15 | const projectConfig = path.join( 16 | Directory.projectRoot, 17 | 'chains', 18 | originChain, 19 | Directory.getMosaicFileName(), 20 | ); 21 | 22 | if (FileSystem.existsSync(projectConfig)) { 23 | const configHomePath = path.join( 24 | Directory.getDefaultMosaicDataDir, 25 | originChain, 26 | ); 27 | 28 | FileSystem.ensureDirSync(configHomePath); 29 | 30 | const mosaicConfig = path.join( 31 | configHomePath, 32 | Directory.getMosaicFileName(), 33 | ); 34 | 35 | if (!FileSystem.existsSync(mosaicConfig)) { 36 | FileSystem.copySync( 37 | projectConfig, 38 | mosaicConfig, 39 | ); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Graph/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | graph-node: 4 | image: graphprotocol/graph-node:v0.16.0 5 | ports: 6 | - '${MOSAIC_GRAPH_RPC_PORT}:8000' 7 | - '${MOSAIC_GRAPH_WS_PORT}:8001' 8 | - '${MOSAIC_GRAPH_RPC_ADMIN_PORT}:8020' 9 | depends_on: 10 | - ipfs 11 | - postgres 12 | environment: 13 | postgres_host: postgres:5432 14 | postgres_user: '${MOSAIC_POSTGRES_USER}' 15 | postgres_pass: '${MOSAIC_POSTGRES_PASSWORD}' 16 | postgres_db: '${MOSAIC_POSTGRES_DATABASE}' 17 | ipfs: 'ipfs:5001' 18 | ethereum: 'dev:http://${MOSAIC_GRAPH_NODE_HOST}:${MOSAIC_ETHEREUM_RPC_PORT}' 19 | RUST_LOG: info 20 | GRAPH_LOG: info 21 | ETHEREUM_POLLING_INTERVAL: 3000 22 | ETHEREUM_REORG_THRESHOLD: 0 23 | ipfs: 24 | image: ipfs/go-ipfs:v0.4.22-rc1 25 | ports: 26 | - '${MOSAIC_GRAPH_IPFS_PORT}:5001' 27 | volumes: 28 | - '${MOSAIC_GRAPH_DATA_FOLDER}/ipfs:/data/ipfs' 29 | postgres: 30 | image: postgres:alpine 31 | ports: 32 | - '${MOSAIC_POSTGRES_PORT}:5432' 33 | environment: 34 | POSTGRES_USER: '${MOSAIC_POSTGRES_USER}' 35 | POSTGRES_PASSWORD: '${MOSAIC_POSTGRES_PASSWORD}' 36 | POSTGRES_DB: '${MOSAIC_POSTGRES_DATABASE}' 37 | volumes: 38 | - '${MOSAIC_GRAPH_DATA_FOLDER}/postgres:/var/lib/postgresql/data' 39 | -------------------------------------------------------------------------------- /tests/Graph/SubGraphDeployment/origin-verifier.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as commander from 'commander'; 4 | import { SubscriptionClient } from 'subscriptions-transport-ws'; 5 | 6 | import Logger from '../../../src/Logger'; 7 | 8 | import WebSocket = require('ws'); 9 | 10 | const mosaic = commander 11 | .arguments(' { 18 | const subGraphName = `mosaic/origin-${gatewayAddresses.substr(2, 25)}` 19 | Logger.info(`subgraph name for verification ${subGraphName}`); 20 | Logger.info(`graph ws port ${graphWsPort}`); 21 | const wsEndPoint = `ws://127.0.0.1:${graphWsPort}/subgraphs/name/${subGraphName}`; 22 | 23 | // Creates subscription client 24 | const subscriptionClient = new SubscriptionClient(wsEndPoint, { 25 | reconnect: true, 26 | }, 27 | WebSocket); 28 | subscriptionClient.onConnected(() => { 29 | Logger.info(`Connected to sub graph: ${subGraphName}`); 30 | subscriptionClient.close(); 31 | process.exit(0); 32 | }); 33 | subscriptionClient.onError((error) => { 34 | Logger.error(`Could not connect to sub graph: ${subGraphName}`); 35 | Logger.error(`Error: ${error.message}`); 36 | process.exit(1); 37 | }); 38 | }, 39 | ) 40 | .parse(process.argv); 41 | -------------------------------------------------------------------------------- /tests/Validator/isValidOriginChain.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import {assert} from 'chai'; 3 | import Validator from '../../src/bin/Validator'; 4 | 5 | describe('Validation.isValidOriginChain', () => { 6 | it('should return true for valid origin chain', () => { 7 | let originChain = 'ropsten'; 8 | assert.isTrue( 9 | Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, 10 | ); 11 | originChain = 'ethereum'; 12 | assert.isTrue( 13 | Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, 14 | ); 15 | originChain = 'goerli'; 16 | assert.isTrue( 17 | Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, 18 | ); 19 | }); 20 | 21 | it('should return true for dev origin chain', () => { 22 | let originChain = 'dev-origin'; 23 | assert.isTrue( 24 | Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, 25 | ); 26 | originChain = '1515'; 27 | assert.isTrue( 28 | Validator.isValidOriginChain(originChain), `Must return true for ${originChain} `, 29 | ); 30 | }); 31 | 32 | it('should return false for invalid origin chain', () => { 33 | const originChain = '1405'; 34 | assert.isNotTrue( 35 | Validator.isValidOriginChain(originChain), `Must return false for ${originChain} `, 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/Validator/isValidAuxiliaryChain.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import {assert} from 'chai'; 3 | import Validator from '../../src/bin/Validator'; 4 | 5 | describe('Validation.isValidAuxiliaryChain', () => { 6 | it('should return true for valid auxiliary chain', () => { 7 | let auxChain = '1405'; 8 | assert.isTrue( 9 | Validator.isValidAuxChain(auxChain, 'goerli'), `Must return true for ${auxChain} `, 10 | ); 11 | auxChain = '1406'; 12 | assert.isTrue( 13 | Validator.isValidAuxChain(auxChain, 'ropsten'), `Must return true for ${auxChain} `, 14 | ); 15 | auxChain = '1407'; 16 | assert.isTrue( 17 | Validator.isValidAuxChain(auxChain, 'ropsten'), `Must return true for ${auxChain} `, 18 | ); 19 | }); 20 | 21 | it('should return true for dev auxiliary chain', () => { 22 | let auxChain = 'dev-auxiliary'; 23 | assert.isTrue( 24 | Validator.isValidAuxChain(auxChain, 'dev-origin'), `Must return true for ${auxChain} `, 25 | ); 26 | auxChain = '1000'; 27 | assert.isTrue( 28 | Validator.isValidAuxChain(auxChain, 'dev-origin'), `Must return true for ${auxChain} `, 29 | ); 30 | }); 31 | 32 | it('should return false for invalid aux chain', () => { 33 | const originChain = '2000'; 34 | assert.isNotTrue( 35 | Validator.isValidAuxChain(originChain, 'invalid'), `Must return false for ${originChain} `, 36 | ); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir_path="$(cd "$(dirname "$0")" && pwd)" 4 | root_dir="${script_dir_path}/.." 5 | test_package_path="${script_dir_path}/package" 6 | 7 | function clean { 8 | cd "${test_package_path}" 9 | 10 | rm -rf "./node_modules" || exit 1 11 | rm -f openst-mosaic-chains-*.tgz || exit 1 12 | rm -f package.json || exit 1 13 | rm -f package-lock.json || exit 1 14 | 15 | cd - 16 | } 17 | 18 | clean 19 | 20 | echo "Switching to root directory." 21 | cd "${root_dir}" || exit 1 22 | 23 | echo "Executing \"npm pack\"." 24 | npm pack || exit 1 25 | 26 | echo "Switching to the package test directory." 27 | cd "${test_package_path}" || exit 1 28 | 29 | echo "Moving npm tarball into the test directory." 30 | mv $root_dir/openst-mosaic-chains-*.tgz . || exit 1 31 | 32 | echo "Initiating npm project for test." 33 | npm init -y || exit 1 34 | 35 | echo "Installing openst-mosaic-chains npm package into newly created project." 36 | npm install openst-mosaic-chains-*.tgz || exit 1 37 | 38 | echo "Accessing as binary." 39 | ./node_modules/.bin/mosaic start 1407 --origin ropsten || exit 1 40 | ./node_modules/.bin/mosaic list || exit 1 41 | ./node_modules/.bin/mosaic stop 1407 || exit 1 42 | 43 | echo "Accessing as library." 44 | ../../node_modules/.bin/ts-node "./index.ts" || exit 1 45 | 46 | echo "Cleaning up generated files." 47 | clean 48 | 49 | echo "Successfully Passed." 50 | -------------------------------------------------------------------------------- /tests/Graph/SubGraphDeployment/auxiliary-verifier.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as commander from 'commander'; 4 | import { SubscriptionClient } from 'subscriptions-transport-ws'; 5 | 6 | import Logger from '../../../src/Logger'; 7 | 8 | import WebSocket = require('ws'); 9 | 10 | const mosaic = commander 11 | .arguments(' '); 12 | 13 | mosaic.action( 14 | async ( 15 | graphWsPort: string, 16 | cogatewayAddress: string, 17 | ) => { 18 | const subGraphName = `mosaic/auxiliary-${cogatewayAddress.substr(2, 22)}`; 19 | Logger.info(`subgraph name for verification ${subGraphName}`); 20 | Logger.info(`graph ws port ${graphWsPort}`); 21 | const wsEndPoint = `ws://127.0.0.1:${graphWsPort}/subgraphs/name/${subGraphName}`; 22 | 23 | // Creates subscription client 24 | const subscriptionClient = new SubscriptionClient(wsEndPoint, { 25 | reconnect: true, 26 | }, 27 | WebSocket); 28 | subscriptionClient.onConnected(() => { 29 | Logger.info(`Connected to sub graph: ${subGraphName} url: ${wsEndPoint}`); 30 | subscriptionClient.close(); 31 | process.exit(0); 32 | }); 33 | subscriptionClient.onError((error) => { 34 | Logger.error(`Could not connect to sub graph: ${subGraphName} url: ${wsEndPoint}`); 35 | Logger.error(`Error: ${error.message}`); 36 | process.exit(1); 37 | }); 38 | }, 39 | ) 40 | .parse(process.argv); 41 | -------------------------------------------------------------------------------- /docker/readme.md: -------------------------------------------------------------------------------- 1 | Following are the steps to publish the Docker image. 2 | 3 | 1. Copy `Dockerfile` and startup.sh in docker directory 4 | 5 | `docker/Dockerfile` 6 | 7 | `docker/startup.sh` 8 | 9 | 2. Create the following directories 10 | 11 | `docker/root/origin-geth` 12 | 13 | `docker/root/1000` 14 | 15 | 3. Copy the origin and auxiliary chain data to the following location 16 | 17 | `docker/chain_data/origin-geth` 18 | 19 | `docker/chain_data/` 20 | 21 | 4. `The origin-geth` and directory should have the following content 22 | 23 | ```typescript 24 | - dev_pass (file) 25 | - genesis.json (file) 26 | - geth (directory) 27 | |- chaindata 28 | |- lightchaindata 29 | |- LOCK 30 | |- nodekey 31 | |- nodes 32 | |- transactions.rlp 33 | 34 | - keystore (directory) 35 | |- UTC--xxxxxxxxx 36 | |- UTC--xxxxxxxxx 37 | . 38 | . 39 | ``` 40 | 5. Move the mosaic config file in `configs` directory. 41 | 42 | 6. Move the gateway config of dev chain to a folder with below folder structure: 43 | 44 | ` 45 | docker/root/origin-geth/1000/gateway-{gatewayAddress}/gateway-config.json 46 | ` 47 | 48 | 7. Go to docker folder. Run the following command to build the docker image. 49 | 50 | `docker build -t mosaicdao/dev-chains . -f docker/Dockerfile` 51 | 52 | 8. Authenticate docker with `Docker login` if not already done. 53 | 54 | 9. Run the following command to publish the Docker image. 55 | 56 | `docker push mosaicdao/dev-chains` 57 | 58 | -------------------------------------------------------------------------------- /src/bin/mosaic-create.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import * as commander from 'commander'; 3 | 4 | import Logger from '../Logger'; 5 | import Initialization from '../NewChain/Initialization'; 6 | import NodeOptions from './NodeOptions'; 7 | import NodeDescription from '../Node/NodeDescription'; 8 | import Directory from '../Directory'; 9 | import Validator from './Validator'; 10 | 11 | let mosaic = commander 12 | .arguments(' '); 13 | mosaic = NodeOptions.addCliOptions(mosaic); 14 | mosaic.action( 15 | async ( 16 | newChainId: string, 17 | originWebsocket: string, 18 | passwordFile: string, 19 | options, 20 | ) => { 21 | const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); 22 | if (!isValidWeb3Connection) { 23 | Logger.error('Could not connect to origin node with web3'); 24 | } 25 | const nodeOptions: NodeOptions = NodeOptions.parseOptions(options, newChainId); 26 | if (nodeOptions.originChain === '') { 27 | Logger.error('Unknown origin, please provide --origin'); 28 | process.exit(1); 29 | } 30 | let nodeDescription = new NodeDescription(newChainId); 31 | nodeDescription = Object.assign(nodeDescription, nodeOptions); 32 | nodeDescription.password = Directory.sanitize(passwordFile); 33 | 34 | try { 35 | await Initialization.initialize( 36 | newChainId, 37 | originWebsocket, 38 | nodeDescription, 39 | ); 40 | } catch (error) { 41 | Logger.error('error while executing mosaic create: ', { message: error.toString() }); 42 | process.exit(1); 43 | } 44 | 45 | process.exit(0); 46 | }, 47 | ) 48 | .parse(process.argv); 49 | -------------------------------------------------------------------------------- /tests/Node/NodeFactory.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import { assert } from 'chai'; 3 | import Node from '../../src/Node/Node'; 4 | import NodeDescription from '../../src/Node/NodeDescription'; 5 | import NodeFactory from '../../src/Node/NodeFactory'; 6 | import ParityNode from '../../src/Node/ParityNode'; 7 | import GethNode from '../../src/Node/GethNode'; 8 | import { GETH_CLIENT } from '../../src/Node/ChainInfo'; 9 | 10 | describe('NodeFactory.create()', () => { 11 | 12 | it('returns parity node for a chain supported by parity', () => { 13 | const chains = [ 14 | 'ethereum', 15 | 'ropsten', 16 | 'goerli', 17 | ]; 18 | 19 | for (const chain of chains) { 20 | const node: Node = NodeFactory.create(new NodeDescription(chain)); 21 | assert.instanceOf(node, ParityNode); 22 | } 23 | }); 24 | 25 | it('returns geth node for a chain supported by parity if client is set to geth', () => { 26 | const chains = [ 27 | 'ethereum', 28 | 'ropsten', 29 | 'goerli', 30 | ]; 31 | 32 | for (const chain of chains) { 33 | const nodeDescription = new NodeDescription(chain); 34 | nodeDescription.client = GETH_CLIENT; 35 | const node: Node = NodeFactory.create(nodeDescription); 36 | assert.instanceOf(node, GethNode); 37 | } 38 | }); 39 | 40 | it('returns geth node for a auxiliary chains', () => { 41 | const chains = [ 42 | '200', 43 | '1409', 44 | ]; 45 | 46 | for (const chain of chains) { 47 | const nodeDescription = new NodeDescription(chain); 48 | nodeDescription.originChain = 'ropsten'; 49 | const node: Node = NodeFactory.create(nodeDescription); 50 | assert.instanceOf(node, GethNode); 51 | } 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEs 2 | .idea/ 3 | .vscode/ 4 | *.swp 5 | tags 6 | 7 | # Node passwords 8 | password.txt 9 | 10 | # integration test chain data 11 | chains/1 12 | 13 | # Packaging output 14 | lib/** 15 | openst-mosaic-chains-*.tgz 16 | 17 | # Initialization configs for new chains; keep example 18 | initialize/*.json 19 | !initialize/example.json 20 | !initialize/500.json 21 | 22 | # Logs 23 | logs 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # TypeScript build target 35 | build/ 36 | 37 | # Custom configurations 38 | config/*.json 39 | 40 | # Directory for instrumented libs generated by jscoverage/JSCover 41 | lib-cov 42 | 43 | # Coverage directory used by tools like istanbul 44 | coverage 45 | 46 | # nyc test coverage 47 | .nyc_output 48 | 49 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 50 | .grunt 51 | 52 | # Bower dependency directory (https://bower.io/) 53 | bower_components 54 | 55 | # node-waf configuration 56 | .lock-wscript 57 | 58 | # Compiled binary addons (https://nodejs.org/api/addons.html) 59 | build/Release 60 | 61 | # Dependency directories 62 | node_modules/ 63 | jspm_packages/ 64 | 65 | # TypeScript v1 declaration files 66 | typings/ 67 | 68 | # Optional npm cache directory 69 | .npm 70 | 71 | # Optional eslint cache 72 | .eslintcache 73 | 74 | # Optional REPL history 75 | .node_repl_history 76 | 77 | # Output of 'npm pack' 78 | *.tgz 79 | 80 | # Yarn Integrity file 81 | .yarn-integrity 82 | 83 | # dotenv environment variables file 84 | .env 85 | 86 | # next.js build output 87 | .next 88 | 89 | 90 | # Docker data folders 91 | docker/chain_data 92 | docker/configs 93 | docker/root 94 | -------------------------------------------------------------------------------- /tests/NewChain/OriginChainInteract/SetCoanchorAddress.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import * as sinon from 'sinon'; 3 | import { ContractInteract } from '@openst/mosaic.js'; 4 | import InitConfig from '../../../src/Config/InitConfig'; 5 | import OriginChainInteract from '../../../src/NewChain/OriginChainInteract'; 6 | import SpyAssert from '../../test_utils/SpyAssert'; 7 | 8 | describe('OriginChainInteract.setCoAnchorAddress()', () => { 9 | let mockInitConfig; 10 | let web3; 11 | const auxiliaryChainId = '123'; 12 | let originChainInteract: OriginChainInteract; 13 | let anchor; 14 | let setCoAnchorSpy; 15 | const fakeReceipt = 'TXReceipt'; 16 | const originTxOptions = { 17 | from: '0x000000000000000000000000000000000000001', 18 | }; 19 | const coAnchorAddress = '0x000000000000000000000000000000000000002'; 20 | 21 | beforeEach(() => { 22 | mockInitConfig = sinon.createStubInstance(InitConfig); 23 | 24 | mockInitConfig.originTxOptions = originTxOptions; 25 | 26 | web3 = sinon.fake(); 27 | 28 | originChainInteract = new OriginChainInteract( 29 | mockInitConfig, 30 | web3, 31 | auxiliaryChainId, 32 | ); 33 | 34 | anchor = sinon.createStubInstance(ContractInteract.Anchor); 35 | setCoAnchorSpy = sinon.replace( 36 | anchor, 37 | 'setCoAnchorAddress', 38 | sinon.fake.resolves(fakeReceipt), 39 | ); 40 | }); 41 | 42 | it('should set co-anchor address', async () => { 43 | await originChainInteract.setCoAnchorAddress( 44 | anchor, 45 | coAnchorAddress, 46 | ); 47 | 48 | SpyAssert.assert( 49 | setCoAnchorSpy, 50 | 1, 51 | [[ 52 | coAnchorAddress, 53 | originTxOptions, 54 | ]], 55 | ); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: node_js 3 | cache: npm 4 | sudo: required 5 | services: 6 | - docker 7 | env: 8 | global: 9 | - DOCKER_COMPOSE_VERSION=1.23.2 10 | before_install: 11 | - sudo rm /usr/local/bin/docker-compose 12 | - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose 13 | - chmod +x docker-compose 14 | - sudo mv docker-compose /usr/local/bin 15 | - sudo apt-get install libsecret-1-dev 16 | node_js: 17 | - "11" 18 | branches: 19 | only: 20 | - master 21 | - develop 22 | - /^release-.*/ 23 | notifications: 24 | email: 25 | recipients: 26 | - ci.report@ost.com 27 | on_success: always 28 | on_failure: always 29 | install: 30 | - npm ci 31 | jobs: 32 | include: 33 | - stage: "Tests" 34 | script: npm run test:unit 35 | name: Unit 36 | - script: sudo env PATH=$PATH npm run test:integration 37 | name: Integration 38 | - stage: "Smoke" 39 | script: npm run test:smoke:goerli 40 | name: goerli 41 | - script: npm run test:smoke:1405 42 | name: 1405 43 | - script: npm run test:smoke:dev-origin 44 | name: "Dev Origin" 45 | - script: npm run test:smoke:dev-auxiliary 46 | name: "Dev Auxiliary" 47 | - if: type = cron 48 | stage: "Nightly Build" 49 | script: npm run test:smoke:ropsten 50 | name: ropsten 51 | - if: type = cron 52 | script: npm run test:smoke:ethereum 53 | name: ethereum 54 | - if: type = cron 55 | script: npm run test:smoke:1406 56 | name: 1406 57 | - if: type = cron 58 | script: npm run test:smoke:1407 59 | name: 1407 60 | - if: type = cron 61 | script: npm run test:smoke:1414 62 | name: 1414 63 | after_failure: 64 | - cat /home/travis/.npm/_logs/*-debug.log 65 | -------------------------------------------------------------------------------- /src/Utils.ts: -------------------------------------------------------------------------------- 1 | import * as ip from 'ip'; 2 | import * as markdownTable from 'markdown-table'; 3 | import ChainInfo from './Node/ChainInfo'; 4 | 5 | import Web3 = require('web3'); 6 | 7 | /** 8 | * It contains utility methods. 9 | */ 10 | export default class Utils { 11 | /** 12 | * It provides checksum address using web3. 13 | * @param address Address. 14 | * @returns It returns checksum address. 15 | */ 16 | public static toChecksumAddress(address: string): string { 17 | return Web3.utils.toChecksumAddress(address); 18 | } 19 | 20 | /** 21 | * It return ip address of the system. 22 | * @returns Ip address. 23 | */ 24 | public static ipAddress(): string { 25 | return ip.address(); 26 | } 27 | 28 | /** 29 | * Generates WS endpoint for subgraph. 30 | * @param subGraphName Name of subgraph. 31 | */ 32 | public static graphWSEndpoint(subGraphName: string): string { 33 | return `ws://{host}:{graph-ws-port}/subgraphs/name/${subGraphName}`; 34 | } 35 | 36 | /** 37 | * Generates rpc endpoint for subgraph. 38 | * @param subgraphName Name of subgraph. 39 | */ 40 | public static graphRPCEndPoint(subgraphName: string): string { 41 | return `http://{host}:{graph-http-port}/subgraphs/name/${subgraphName}`; 42 | } 43 | 44 | /** 45 | * Print contract addresses in tabular format. 46 | * @param contractNames Array of contract names. 47 | * @param addresses Array of contract addresses. 48 | */ 49 | public static printContracts(contractNames: string[], addresses: string[]): void { 50 | const rows = [['ContractName', 'Address']]; 51 | for (let i = 0; i < contractNames.length; i++) { 52 | rows.push([contractNames[i], addresses[i]]); 53 | } 54 | const details = markdownTable(rows, { 55 | align: ['c', 'c'], 56 | }); 57 | console.log(`\n ${details}`); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/NewChain/AuxiliaryChainInteract/SetCoanchorAddress.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import * as sinon from 'sinon'; 3 | import { ContractInteract } from '@openst/mosaic.js'; 4 | import InitConfig from '../../../src/Config/InitConfig'; 5 | import AuxiliaryChainInteract 6 | from '../../../src/NewChain/AuxiliaryChainInteract'; 7 | import SpyAssert from '../../test_utils/SpyAssert'; 8 | import NodeDescription from '../../../src/Node/NodeDescription'; 9 | 10 | describe('AuxiliaryChainInteract.setCoAnchorAddress()', () => { 11 | let mockInitConfig; 12 | const auxiliaryChainId = '123'; 13 | let auxiliaryChainInteract: AuxiliaryChainInteract; 14 | let anchor; 15 | let setCoAnchorSpy; 16 | const fakeReceipt = 'TXReceipt'; 17 | const auxiliaryTxOptions = { 18 | // Deployer address is not defined aux chain interact it's created on chain setup. 19 | from: undefined, 20 | gasPrice: '0', 21 | gas: '10000000', 22 | }; 23 | const coAnchorAddress = '0x000000000000000000000000000000000000002'; 24 | 25 | beforeEach(() => { 26 | mockInitConfig = sinon.createStubInstance(InitConfig); 27 | 28 | auxiliaryChainInteract = new AuxiliaryChainInteract( 29 | mockInitConfig, 30 | auxiliaryChainId, 31 | 'originChain', 32 | new NodeDescription(auxiliaryChainId), 33 | ); 34 | 35 | anchor = sinon.createStubInstance(ContractInteract.Anchor); 36 | setCoAnchorSpy = sinon.replace( 37 | anchor, 38 | 'setCoAnchorAddress', 39 | sinon.fake.resolves(fakeReceipt), 40 | ); 41 | }); 42 | 43 | it('should set co-anchor address', async () => { 44 | await auxiliaryChainInteract.setCoAnchorAddress( 45 | anchor, 46 | coAnchorAddress, 47 | ); 48 | 49 | SpyAssert.assert( 50 | setCoAnchorSpy, 51 | 1, 52 | [[ 53 | coAnchorAddress, 54 | auxiliaryTxOptions, 55 | ]], 56 | ); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /tools/abi_downloader/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs-extra'; 3 | 4 | import Logger from '../../src/Logger'; 5 | import Directory from '../../src/Directory'; 6 | 7 | import mosaicContract = require('@openst/mosaic-contracts'); 8 | 9 | const args = process.argv.slice(2); 10 | const version = args[0]; 11 | const abiFolder = './abi'; 12 | const allowedABIs = [ 13 | 'Anchor', 14 | 'OSTComposer', 15 | 'EIP20Gateway', 16 | 'EIP20CoGateway', 17 | 'RedeemPool', 18 | ]; 19 | 20 | /** 21 | * This method removes patch contractVersion from the contractVersion if any. 22 | * @param contractVersion Version of contract. 23 | */ 24 | function getMajorMinorVersion(contractVersion: string): string { 25 | // Check if patch version exists. 26 | if (contractVersion.split('.').length > 2) { 27 | const patchVersionIndex = contractVersion.lastIndexOf('.'); 28 | return contractVersion.slice(0, patchVersionIndex); 29 | } 30 | return contractVersion; 31 | } 32 | 33 | /** 34 | * This method write abi to files. 35 | * @param name Name of the contract. 36 | * @param abi Contract ABI. 37 | * @param contractVersion Version of contract. 38 | */ 39 | function writeToFile(name: string, abi: object, contractVersion: string): void { 40 | const directory = path.join( 41 | Directory.projectRoot, 42 | abiFolder, 43 | getMajorMinorVersion(contractVersion), 44 | ); 45 | fs.ensureDirSync(directory); 46 | const filePath = path.join( 47 | directory, 48 | `${name}.json`, 49 | ); 50 | Logger.info(`storing ${name} abi to path ${filePath} `); 51 | 52 | fs.writeFileSync( 53 | filePath, 54 | JSON.stringify(abi, null, ' '), 55 | ); 56 | } 57 | 58 | 59 | const contracts = Object.keys(mosaicContract.contracts); 60 | contracts.filter(c => allowedABIs.indexOf(c) !== -1) 61 | .map(contract => writeToFile(contract, mosaicContract.contracts[contract].abi, version)); 62 | -------------------------------------------------------------------------------- /tests/test_utils/SpyAssert.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { assert } from 'chai'; 4 | 5 | /** 6 | * This class includes the utility functions to assert spy data. 7 | */ 8 | export default class SpyAssert { 9 | /** 10 | * @function assertSpy 11 | * 12 | * Asserts the spy data. 13 | * 14 | * @param {Object} spy Spy object. 15 | * @param {number} callCount of times the spy was called. 16 | * @param {Array} inputArgs Input arguments 17 | * 18 | */ 19 | public static assert(spy: any, callCount: number, inputArgs: any[]): void { 20 | assert.strictEqual( 21 | spy.callCount, 22 | callCount, 23 | 'Call count must match with the expected value.', 24 | ); 25 | if (inputArgs) { 26 | for (let i = 0; i < callCount; i += 1) { 27 | const expectedArguments = inputArgs[i]; 28 | const actualArguments = spy.args[i]; 29 | assert.strictEqual( 30 | actualArguments.length, 31 | expectedArguments.length, 32 | 'Expected and actual argument counts should be same', 33 | ); 34 | for (let params = 0; params < actualArguments.length; params += 1) { 35 | assert.deepStrictEqual( 36 | actualArguments[params], 37 | expectedArguments[params], 38 | `Input param value ${ 39 | actualArguments[params] 40 | } must match with the expected param value ${ 41 | expectedArguments[params] 42 | }.`, 43 | ); 44 | } 45 | } 46 | } 47 | } 48 | 49 | /** 50 | * @function assertSpy 51 | * 52 | * Asserts the spy data. 53 | * 54 | * @param {Object} spy Spy object. 55 | * @param {number} callCount of times the spy was called. 56 | */ 57 | public static assertCall(spy: any, callCount: number) { 58 | assert.strictEqual( 59 | spy.callCount, 60 | callCount, 61 | 'Call count must match with the expected value.', 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Shell.ts: -------------------------------------------------------------------------------- 1 | import { spawnSync, execSync } from 'child_process'; 2 | 3 | /** 4 | * Executes shell commands in child processes. 5 | */ 6 | export default class Shell { 7 | /** 8 | * Executes a docker child process with the given arguments passed to docker. 9 | * @param args Arguments to the docker command. 10 | * @throws If the docker command returns an error. 11 | */ 12 | public static executeDockerCommand(args: string[]): void { 13 | Shell.execute('docker', args); 14 | } 15 | 16 | /** 17 | * Executes a command in a child process and returns it. 18 | * The child process inherits stdio from this node process. 19 | * Inheriting stdio means that all input and output is piped to/from the spawned process. 20 | * For example, docker output is directly forwarded to stdout of the node process and 21 | * the attach command can accept input from the node process' stdin (terminal). 22 | * @param command The command to execute. 23 | * @param args The args to pass to the command. 24 | * @returns The child process that was spawned by this call. 25 | * @throws If the child process returns an error. 26 | */ 27 | public static execute(command: string, args: string[]): void { 28 | // `stdio: 'inherit'` ensures that stdio of this process is inherited by the child process. 29 | const childProcess = spawnSync(command, args, { stdio: 'inherit' }); 30 | if (childProcess.error instanceof Error) { 31 | throw childProcess.error; 32 | } 33 | } 34 | 35 | /** 36 | * Executes a command in child process and returns it. 37 | * It must be used when we need to utilize shell functionality such as pipe, redirects. 38 | * @param {string} command Command string to execute. 39 | * @param {Object} Options to execSync command. 40 | * @returns Child process that was spawned by this call. 41 | */ 42 | public static executeInShell(command: string, options = {}): Buffer { 43 | return execSync(command, options); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/bin/mosaic-verify-chain.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as commander from 'commander'; 4 | 5 | import Logger from '../Logger'; 6 | import ChainVerifier from '../NewChain/ChainVerifier'; 7 | import NodeOptions from './NodeOptions'; 8 | import Validator from './Validator'; 9 | 10 | let mosaic = commander 11 | .arguments(' '); 12 | mosaic = NodeOptions.addCliOptions(mosaic); 13 | mosaic.action( 14 | async ( 15 | originWebsocket: string, 16 | auxiliaryWebsocket: string, 17 | originChainIdentifier: string, 18 | auxiliaryChainIdentifier: string, 19 | ) => { 20 | const isValidOriginWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); 21 | if (!isValidOriginWeb3Connection) { 22 | Logger.error('Could not connect to origin node with web3'); 23 | } 24 | 25 | const isValidAuxWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); 26 | if (!isValidAuxWeb3Connection) { 27 | Logger.error('Could not connect to aux node with web3'); 28 | } 29 | 30 | if (!Validator.isValidOriginChain(originChainIdentifier)) { 31 | Logger.error(`Invalid origin chain identifier: ${originChainIdentifier}`); 32 | process.exit(1); 33 | } 34 | 35 | if (!Validator.isValidAuxChain(auxiliaryChainIdentifier, originChainIdentifier)) { 36 | Logger.error(`Invalid aux chain identifier: ${auxiliaryChainIdentifier}`); 37 | process.exit(1); 38 | } 39 | try { 40 | const chainVerifier = new ChainVerifier( 41 | originWebsocket, 42 | auxiliaryWebsocket, 43 | originChainIdentifier, 44 | auxiliaryChainIdentifier, 45 | ); 46 | await chainVerifier.verify(); 47 | } catch (error) { 48 | Logger.error('error while executing mosaic chain verification', { error: error.toString() }); 49 | process.exit(1); 50 | } 51 | 52 | process.exit(0); 53 | }, 54 | ) 55 | .parse(process.argv); 56 | -------------------------------------------------------------------------------- /src/lib/StakePool.ts: -------------------------------------------------------------------------------- 1 | import PublishMosaicConfig from '../Config/PublishMosaicConfig'; 2 | import OriginChainInteract from '../NewChain/OriginChainInteract'; 3 | import MosaicConfig from '../Config/MosaicConfig'; 4 | import { MosaicConfigNotFoundException } from '../Exception'; 5 | 6 | import Web3 = require('web3'); 7 | 8 | /** 9 | * This function deploys stake pool(OST composer contract). 10 | * @param chain Origin chain identifier. 11 | * @param originWebsocket Websocket connection for origin chain. 12 | * @param deployer Address of the deployer. 13 | * @param organizationOwner Address of organization owner of OST Composer. 14 | * @param organizationAdmin Address of organization admin of OST Composer. 15 | * @param mosaicConfigPath Mosaic config Path. 16 | */ 17 | const deployStakePool = async ( 18 | chain: string, 19 | originWebsocket: string, 20 | deployer: string, 21 | organizationOwner: string, 22 | organizationAdmin: string, 23 | mosaicConfigPath?: string, 24 | ): Promise => { 25 | // Publishes mosaic configs for existing chains 26 | PublishMosaicConfig.tryPublish(chain); 27 | 28 | if (!(MosaicConfig.exists(chain) || mosaicConfigPath)) { 29 | throw new MosaicConfigNotFoundException(`Mosaic config for ${chain} not found.`); 30 | } 31 | 32 | const mosaicConfig: MosaicConfig = mosaicConfigPath 33 | ? MosaicConfig.fromFile(mosaicConfigPath) 34 | : MosaicConfig.fromChain(chain); 35 | 36 | const originWeb3 = new Web3(originWebsocket); 37 | const ostComposer = await OriginChainInteract.setupOSTComposer( 38 | originWeb3, 39 | organizationOwner, 40 | organizationAdmin, 41 | deployer, 42 | ); 43 | 44 | mosaicConfig.originChain.chain = chain; 45 | mosaicConfig.originChain.contractAddresses.stakePoolAddress = ostComposer.options.address; 46 | 47 | if (mosaicConfigPath) { 48 | mosaicConfig.writeToFile(mosaicConfigPath); 49 | } else { 50 | mosaicConfig.writeToMosaicConfigDirectory(); 51 | } 52 | return ostComposer.options.address; 53 | }; 54 | 55 | export default deployStakePool; 56 | -------------------------------------------------------------------------------- /src/bin/mosaic-setup-stake-pool.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as commander from 'commander'; 4 | import Logger from '../Logger'; 5 | import deployStakePool from '../lib/StakePool'; 6 | import Validator from './Validator'; 7 | import Utils from '../Utils'; 8 | 9 | const mosaic = commander 10 | .arguments(' '); 11 | mosaic.option('-m,--mosaic-config ', 'Mosaic config absolute path.'); 12 | mosaic.action( 13 | async ( 14 | chain: string, 15 | originWebsocket: string, 16 | deployer: string, 17 | organizationOwner: string, 18 | organizationAdmin: string, 19 | options, 20 | ) => { 21 | const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); 22 | if (!isValidWeb3Connection) { 23 | Logger.error('Could not connect to origin node with web3'); 24 | } 25 | if (!Validator.isValidOriginChain(chain)) { 26 | Logger.error(`Invalid origin chain identifier: ${chain}`); 27 | process.exit(1); 28 | } 29 | 30 | if (!Validator.isValidAddress(deployer)) { 31 | Logger.error(`Invalid deployer address: ${deployer}`); 32 | process.exit(1); 33 | } 34 | if (!Validator.isValidAddress(organizationOwner)) { 35 | Logger.error(`Invalid organization owner address: ${organizationOwner}`); 36 | process.exit(1); 37 | } 38 | 39 | if (!Validator.isValidAddress(organizationAdmin)) { 40 | Logger.error(`Invalid organization admin address: ${organizationAdmin}`); 41 | process.exit(1); 42 | } 43 | try { 44 | const stakePoolAddress = await deployStakePool( 45 | chain, 46 | originWebsocket, 47 | deployer, 48 | organizationOwner, 49 | organizationAdmin, 50 | options.mosaicConfig, 51 | ); 52 | Utils.printContracts(['Stake pool'], [stakePoolAddress]); 53 | } catch (error) { 54 | Logger.error('error while executing mosaic setup stake pool', { error: error.toString() }); 55 | process.exit(1); 56 | } 57 | 58 | process.exit(0); 59 | }, 60 | ) 61 | .parse(process.argv); 62 | -------------------------------------------------------------------------------- /src/bin/mosaic.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as mosaic from 'commander'; 4 | import Logger from '../Logger'; 5 | 6 | mosaic 7 | .command('start ', 'start container that runs a given chain') 8 | .command('stop ', 'stop the containers that run the given chains') 9 | .command('attach ', 'attach to the ethereum node of the given chain') 10 | .command('logs ', 'follow the logs in the container of the given chain') 11 | .command('list', 'list all running containers that run a mosaic chain') 12 | .command('create ', 'creates a new auxiliary chain on the given origin chain') 13 | .command('libraries ', 'Deploys libraries on an origin chain') 14 | .command('verify-chain ', 'Verifies an auxiliary chain.') 15 | .command('setup-redeem-pool ', 'Deploys redeem pool contract.') 16 | .command('setup-stake-pool ', 'Deploys stake pool contract.') 17 | .command('subgraph ', 'Deploys mosaic subgraph on a graph node') 18 | .on('command:*', (command) => { 19 | // First argument is the command name. 20 | const firstCommand = command[0]; 21 | // Check if command is implemented. 22 | const searchedCommand = mosaic.commands.find(c => c._name === firstCommand); 23 | if (!searchedCommand) { 24 | Logger.error('Invalid command: %s\nSee --help for a list of available commands.', mosaic.args.join(' ')); 25 | process.exit(1); 26 | } 27 | const requiredArgsCount = searchedCommand._args.filter(cmd => cmd.required).length; 28 | // Check for required arguments. 29 | if ((command.length - 1) < requiredArgsCount) { 30 | Logger.error('Missing required argument: See --help for details.'); 31 | process.exit(1); 32 | } 33 | }) 34 | .parse(process.argv); 35 | -------------------------------------------------------------------------------- /graph/origin/src/OSTComposerMapping.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StakeRequested as StakeRequestedEvent, 3 | StakeRevoked as StakeRevokedEvent, 4 | StakeRejected as StakeRejectedEvent, 5 | } from '../generated/Contract/OSTComposer'; 6 | import { 7 | StakeRequested, 8 | StakeRevoked, 9 | StakeRejected, 10 | } from '../generated/OSTComposerSchema'; 11 | 12 | export function handleStakeRequested(event: StakeRequestedEvent): void { 13 | let entity = new StakeRequested( 14 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 15 | ) 16 | entity.amount = event.params.amount 17 | entity.beneficiary = event.params.beneficiary 18 | entity.gasPrice = event.params.gasPrice 19 | entity.gasLimit = event.params.gasLimit 20 | entity.nonce = event.params.nonce 21 | entity.staker = event.params.staker 22 | entity.stakerProxy = event.params.stakerProxy 23 | entity.gateway = event.params.gateway 24 | entity.stakeRequestHash = event.params.stakeRequestHash 25 | entity.blockNumber = event.block.number 26 | entity.blockHash = event.block.hash 27 | entity.contractAddress = event.address 28 | entity.uts = event.block.timestamp 29 | entity.save() 30 | } 31 | 32 | export function handleStakeRevoked(event: StakeRevokedEvent): void { 33 | let entity = new StakeRevoked( 34 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 35 | ) 36 | entity.staker = event.params.staker 37 | entity.stakeRequestHash = event.params.stakeRequestHash 38 | entity.blockNumber = event.block.number 39 | entity.blockHash = event.block.hash 40 | entity.contractAddress = event.address 41 | entity.uts = event.block.timestamp 42 | entity.save() 43 | } 44 | 45 | export function handleStakeRejected(event: StakeRejectedEvent): void { 46 | let entity = new StakeRejected( 47 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 48 | ) 49 | entity.staker = event.params.staker 50 | entity.stakeRequestHash = event.params.stakeRequestHash 51 | entity.blockNumber = event.block.number 52 | entity.blockHash = event.block.hash 53 | entity.contractAddress = event.address 54 | entity.uts = event.block.timestamp 55 | entity.save() 56 | } 57 | -------------------------------------------------------------------------------- /graph/auxiliary/src/RedeemPoolMapping.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RedeemRequested as RedeemRequestedEvent, 3 | RedeemRevoked as RedeemRevokedEvent, 4 | RedeemRejected as RedeemRejectedEvent, 5 | } from '../generated/Contract/RedeemPool'; 6 | import { 7 | RedeemRequested, 8 | RedeemRevoked, 9 | RedeemRejected, 10 | } from '../generated/RedeemPoolSchema'; 11 | 12 | export function handleRedeemRequested(event: RedeemRequestedEvent): void { 13 | let entity = new RedeemRequested( 14 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 15 | ) 16 | entity.amount = event.params.amount 17 | entity.beneficiary = event.params.beneficiary 18 | entity.gasPrice = event.params.gasPrice 19 | entity.gasLimit = event.params.gasLimit 20 | entity.nonce = event.params.nonce 21 | entity.redeemer = event.params.redeemer 22 | entity.redeemerProxy = event.params.redeemerProxy 23 | entity.cogateway = event.params.cogateway 24 | entity.redeemRequestHash = event.params.redeemRequestHash 25 | entity.blockNumber = event.block.number 26 | entity.blockHash = event.block.hash 27 | entity.contractAddress = event.address 28 | entity.uts = event.block.timestamp 29 | entity.save() 30 | } 31 | 32 | export function handleRedeemRevoked(event: RedeemRevokedEvent): void { 33 | let entity = new RedeemRevoked( 34 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 35 | ) 36 | entity.redeemer = event.params.redeemer 37 | entity.redeemRequestHash = event.params.redeemRequestHash 38 | entity.blockNumber = event.block.number 39 | entity.blockHash = event.block.hash 40 | entity.contractAddress = event.address 41 | entity.uts = event.block.timestamp 42 | entity.save() 43 | } 44 | 45 | export function handleRedeemRejected(event: RedeemRejectedEvent): void { 46 | let entity = new RedeemRejected( 47 | event.transaction.hash.toHex() + "-" + event.logIndex.toString() 48 | ) 49 | entity.redeemer = event.params.redeemer 50 | entity.redeemRequestHash = event.params.redeemRequestHash 51 | entity.blockNumber = event.block.number 52 | entity.blockHash = event.block.hash 53 | entity.contractAddress = event.address 54 | entity.uts = event.block.timestamp 55 | entity.save() 56 | } 57 | -------------------------------------------------------------------------------- /src/bin/mosaic-subgraph.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as commander from 'commander'; 4 | import * as markdownTable from 'markdown-table'; 5 | import Logger from '../Logger'; 6 | import { SubGraphType } from '../Graph/SubGraph'; 7 | import deploySubGraph from '../lib/SubGraph'; 8 | import Utils from '../Utils'; 9 | 10 | const mosaic = commander 11 | .arguments(' '); 12 | 13 | mosaic.option('-m,--mosaic-config ', 'Mosaic config absolute path.'); 14 | mosaic.option('-t,--gateway-config ', 'Gateway config absolute path.'); 15 | mosaic.option('-g,--gateway-address ', 'gateway address of origin.'); 16 | mosaic.action( 17 | async ( 18 | originChain: string, 19 | auxiliaryChain: string, 20 | subgraphType: SubGraphType, 21 | graphAdminRPC: string, 22 | graphIPFS: string, 23 | options, 24 | ) => { 25 | try { 26 | const response = deploySubGraph( 27 | originChain, 28 | auxiliaryChain, 29 | subgraphType, 30 | graphAdminRPC, 31 | graphIPFS, 32 | options.mosaicConfig, 33 | options.gatewayAddress, 34 | options.gatewayConfig, 35 | ); 36 | 37 | if (response.success) { 38 | const details = markdownTable([ 39 | ['Chain', 40 | 'Subgraph name', 41 | 'Subgraph websocket endpoint', 42 | 'Subgraph rpc endpoint', 43 | ], 44 | [ 45 | subgraphType, 46 | response.subgraphName, 47 | Utils.graphWSEndpoint(response.subgraphName), 48 | Utils.graphRPCEndPoint(response.subgraphName), 49 | ], 50 | ], { 51 | align: ['c', 'c', 'c', 'c'], 52 | }); 53 | 54 | console.log(`\n\n Sub-graph details : \n${details}\n`); 55 | console.log('ℹ️ Replace host, ws-port and http-port with actual values'); 56 | } else { 57 | console.log('\n\n Subgraph deployment failed.😱'); 58 | } 59 | } catch (error) { 60 | Logger.error('error while executing mosaic subgraph command', { error: error.toString() }); 61 | process.exit(1); 62 | } 63 | 64 | process.exit(0); 65 | }, 66 | ) 67 | .parse(process.argv); 68 | -------------------------------------------------------------------------------- /test_integration/mosaic-create/Utils.ts: -------------------------------------------------------------------------------- 1 | import { ChildProcess, spawn } from 'child_process'; 2 | import * as path from 'path'; 3 | import * as fs from 'fs-extra'; 4 | import Shell from '../../src/Shell'; 5 | import Directory from '../../src/Directory'; 6 | import { DEV_CHAIN_DOCKER } from '../../src/Node/GethNode'; 7 | 8 | const waitPort = require('wait-port'); 9 | 10 | export default class Utils { 11 | /** 12 | * This Method starts origin dev chain at given port. 13 | * @param web3Port Port where origin chain will start. 14 | */ 15 | public static startOriginChain( 16 | web3Port: number, 17 | ): Promise { 18 | return new Promise(async (resolve, reject) => { 19 | const startOriginChainArgs = [ 20 | 'run', 21 | '-p', 22 | `${web3Port}:8545`, 23 | '-p', 24 | '8546:8546', 25 | '--name', 26 | 'integration_test_origin', 27 | DEV_CHAIN_DOCKER, 28 | 'origin', 29 | ]; 30 | const originNodeDockerProcess = spawn('docker', startOriginChainArgs, { stdio: 'inherit' }); 31 | const waitForWebsocketPort = await waitPort({ 32 | port: web3Port, 33 | output: 'silent', 34 | }); 35 | await waitForWebsocketPort; 36 | await setTimeout( 37 | () => resolve(originNodeDockerProcess), 38 | // even after the ports are available the nodes need a bit of time to get online 39 | 30000, 40 | ); 41 | }); 42 | } 43 | 44 | /** 45 | * This method stops origin chain. 46 | */ 47 | public static stopOriginChain() { 48 | Shell.executeInShell('docker stop integration_test_origin'); 49 | Shell.executeInShell('docker rm integration_test_origin'); 50 | } 51 | 52 | /** 53 | * This methods cleans directory after tests are complete. 54 | * @param originChain Origin chain identifier. 55 | */ 56 | public static cleanDirectories(originChain: string) { 57 | const originDirectory = path.join( 58 | Directory.getDefaultMosaicDataDir, 59 | originChain, 60 | ); 61 | 62 | fs.removeSync(originDirectory); 63 | 64 | const originChainProjectDirectory = path.join( 65 | Directory.projectRoot, 66 | 'chains', 67 | originChain, 68 | ); 69 | fs.removeSync(originChainProjectDirectory); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/bin/mosaic-setup-redeem-pool.ts: -------------------------------------------------------------------------------- 1 | import * as commander from 'commander'; 2 | import Logger from '../Logger'; 3 | import setupRedeemPool from '../lib/RedeemPool'; 4 | import Validator from './Validator'; 5 | import Utils from '../Utils'; 6 | 7 | const mosaic = commander 8 | .arguments(' '); 9 | mosaic.option('-m,--mosaic-config ', 'Mosaic config absolute path.'); 10 | mosaic.action( 11 | async ( 12 | originChain: string, 13 | auxiliaryChain: string, 14 | auxChainWeb3EndPoint: string, 15 | deployer: string, 16 | organizationOwner: string, 17 | organizationAdmin: string, 18 | options, 19 | ) => { 20 | const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(auxChainWeb3EndPoint); 21 | if (!isValidWeb3Connection) { 22 | Logger.error('Could not connect to aux node with web3'); 23 | } 24 | 25 | if (!Validator.isValidOriginChain(originChain)) { 26 | Logger.error(`Invalid origin chain identifier: ${originChain}`); 27 | process.exit(1); 28 | } 29 | 30 | if (!Validator.isValidAuxChain(auxiliaryChain, originChain)) { 31 | Logger.error(`Invalid aux chain identifier: ${auxiliaryChain}`); 32 | process.exit(1); 33 | } 34 | 35 | if (!Validator.isValidAddress(deployer)) { 36 | Logger.error(`Invalid deployer address: ${deployer}`); 37 | process.exit(1); 38 | } 39 | if (!Validator.isValidAddress(organizationOwner)) { 40 | Logger.error(`Invalid organization owner address: ${organizationOwner}`); 41 | process.exit(1); 42 | } 43 | 44 | if (!Validator.isValidAddress(organizationAdmin)) { 45 | Logger.error(`Invalid organization admin address: ${organizationAdmin}`); 46 | process.exit(1); 47 | } 48 | 49 | try { 50 | const redeemPoolAddress = await setupRedeemPool( 51 | originChain, 52 | auxiliaryChain, 53 | auxChainWeb3EndPoint, 54 | deployer, 55 | organizationOwner, 56 | organizationAdmin, 57 | options.mosaicConfig, 58 | ); 59 | Utils.printContracts(['Redeem Pool'], [redeemPoolAddress]); 60 | } catch (error) { 61 | Logger.error('error while executing mosaic setup redeem pool', { error: error.toString() }); 62 | process.exit(1); 63 | } 64 | 65 | process.exit(0); 66 | }, 67 | ) 68 | .parse(process.argv); 69 | -------------------------------------------------------------------------------- /src/lib/RedeemPool.ts: -------------------------------------------------------------------------------- 1 | import PublishMosaicConfig from '../Config/PublishMosaicConfig'; 2 | import Contracts from '../NewChain/Contracts'; 3 | import MosaicConfig from '../Config/MosaicConfig'; 4 | import { MosaicConfigNotFoundException } from '../Exception'; 5 | 6 | import Web3 = require('web3'); 7 | 8 | /** 9 | * This function deploys redeem pool contract. 10 | * @param originChain origin chain identifier. 11 | * @param auxiliaryChain auxiliary chain identifier. 12 | * @param auxiliaryChainEndPoint Websocket connection for auxiliary chain. 13 | * @param deployer Address of the deployer. 14 | * @param organizationOwner Address of organization owner of redeem pool. 15 | * @param organizationAdmin Address of organization admin of redeem pool. 16 | * @param mosaicConfigFilePath Mosaic config file path. 17 | */ 18 | const setupRedeemPool = async ( 19 | originChain: string, 20 | auxiliaryChain: string, 21 | auxiliaryChainEndPoint: string, 22 | deployer: string, 23 | organizationOwner: string, 24 | organizationAdmin: string, 25 | mosaicConfigFilePath?: string, 26 | ): Promise => { 27 | // Publishes mosaic configs for existing chains 28 | PublishMosaicConfig.tryPublish(originChain); 29 | 30 | if (!(MosaicConfig.exists(originChain) || mosaicConfigFilePath)) { 31 | throw new MosaicConfigNotFoundException(`Mosaic config for ${originChain} not found.`); 32 | } 33 | 34 | const mosaicConfig = mosaicConfigFilePath 35 | ? MosaicConfig.fromFile(mosaicConfigFilePath) 36 | : MosaicConfig.fromChain(originChain); 37 | 38 | if (!mosaicConfig.auxiliaryChains[auxiliaryChain]) { 39 | throw new Error( 40 | `Mosaic config for ${originChain} does not contain auxiliary chain ${auxiliaryChain}.`, 41 | ); 42 | } 43 | 44 | const auxiliaryWeb3 = new Web3(auxiliaryChainEndPoint); 45 | const gasPrice = await auxiliaryWeb3.eth.getGasPrice(); 46 | const redeemPool = await Contracts.setupRedeemPool( 47 | auxiliaryWeb3, 48 | organizationOwner, 49 | organizationAdmin, 50 | { from: deployer, gasPrice }, 51 | ); 52 | mosaicConfig.auxiliaryChains[auxiliaryChain] 53 | .contractAddresses 54 | .auxiliary 55 | .redeemPoolAddress = redeemPool.options.address; 56 | 57 | if (mosaicConfigFilePath) { 58 | mosaicConfig.writeToFile(mosaicConfigFilePath); 59 | } else { 60 | mosaicConfig.writeToMosaicConfigDirectory(); 61 | } 62 | return redeemPool.options.address; 63 | }; 64 | 65 | export default setupRedeemPool; 66 | -------------------------------------------------------------------------------- /src/FileSystem .ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | 3 | const path = require('path'); 4 | 5 | /** 6 | * Provides file system related operations. 7 | */ 8 | export default class FileSystem { 9 | /** 10 | * Checks if path exists on disk (in sync). 11 | * @param {string} path 12 | * @return {boolean} 13 | */ 14 | public static pathExistsSync(path: string): boolean { 15 | return fs.pathExistsSync(path); 16 | } 17 | 18 | /** 19 | * Ensures (in sync) that this path exists on disk (if it doesn't it is created). 20 | * @param {string} path 21 | * @return {boolean} 22 | */ 23 | public static ensureDirSync(path: string) { 24 | return fs.ensureDirSync(path); 25 | } 26 | 27 | /** 28 | * Copies (in sync) contents from source to destination. 29 | * @param {string} source 30 | * @param {string} destination 31 | */ 32 | public static copySync(source: string, destination: string) { 33 | return fs.copySync(source, destination); 34 | } 35 | 36 | /** 37 | * Removes (in sync) contents on a path from disk. 38 | * @param {string} source 39 | */ 40 | public static removeSync(source: string) { 41 | return fs.removeSync(source); 42 | } 43 | 44 | /** 45 | * Reads (in sync) file content from disk. 46 | * @param path 47 | * @return {Buffer} 48 | */ 49 | public static readFileSync(path: string): Buffer { 50 | return fs.readFileSync(path); 51 | } 52 | 53 | /** 54 | * Writes (in sync) content to file at given path on disk. 55 | * @param {string} path 56 | * @param {Buffer} content 57 | */ 58 | public static writeFileSync(path: string, content: Buffer) { 59 | return fs.writeFileSync(path, content); 60 | } 61 | 62 | /** 63 | * Check if the file exists in the filesystem. 64 | * @param {string} path 65 | * @return boolean `true` if exists, otherwise false. 66 | */ 67 | public static existsSync(path: string): boolean { 68 | try { 69 | return fs.existsSync(path); 70 | } catch (err) { 71 | return false; 72 | } 73 | } 74 | 75 | /** 76 | * It resolves home path to absolute path after resolving tilde. 77 | * 78 | * @param filePath GatewayConfig file path. e.g. ~/.mosaic/goerli/gatewayAddress.json 79 | * @return {string} Absolute file path. 80 | */ 81 | public static resolveHomePath(filePath: string) { 82 | if (filePath[0] === '~') { 83 | return path.join(process.env.HOME, filePath.slice(1)); 84 | } 85 | return filePath; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/bin/mosaic-libraries.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as commander from 'commander'; 4 | 5 | import Logger from '../Logger'; 6 | import OriginChainInteract from '../NewChain/OriginChainInteract'; 7 | import MosaicConfig from '../Config/MosaicConfig'; 8 | import PublishMosaicConfig from '../Config/PublishMosaicConfig'; 9 | import Utils from '../Utils'; 10 | import Validator from './Validator'; 11 | 12 | import Web3 = require('web3'); 13 | 14 | const mosaic = commander 15 | .arguments(' '); 16 | mosaic.action( 17 | async ( 18 | chain: string, 19 | originWebsocket: string, 20 | deployer: string, 21 | ) => { 22 | const isValidWeb3Connection = await Validator.isValidWeb3EndPoint(originWebsocket); 23 | if (!isValidWeb3Connection) { 24 | Logger.error('Could not connect to origin node with web3'); 25 | } 26 | 27 | if (!Validator.isValidOriginChain(chain)) { 28 | Logger.error(`Invalid origin chain identifier: ${chain}`); 29 | process.exit(1); 30 | } 31 | if (!Validator.isValidAddress(deployer)) { 32 | Logger.error(`Invalid deployer address: ${deployer}`); 33 | process.exit(1); 34 | } 35 | try { 36 | // Publishes mosaic configs for existing chains 37 | PublishMosaicConfig.tryPublish(chain); 38 | 39 | const mosaicConfig: MosaicConfig = MosaicConfig.fromChain(chain); 40 | const originWeb3 = new Web3(originWebsocket); 41 | const { 42 | gatewayLib, 43 | messageBus, 44 | merklePatriciaProof, 45 | } = await OriginChainInteract.deployLibraries(originWeb3, deployer); 46 | 47 | mosaicConfig.originChain.chain = chain; 48 | mosaicConfig.originChain.contractAddresses.gatewayLibAddress = Utils.toChecksumAddress( 49 | gatewayLib.address, 50 | ); 51 | mosaicConfig.originChain.contractAddresses.messageBusAddress = Utils.toChecksumAddress( 52 | messageBus.address, 53 | ); 54 | mosaicConfig.originChain.contractAddresses.merklePatriciaLibAddress = Utils.toChecksumAddress( 55 | merklePatriciaProof.address, 56 | ); 57 | 58 | mosaicConfig.writeToMosaicConfigDirectory(); 59 | Utils.printContracts(['Gateway library', 'Message bus library', 'Merkle patricia proof library'], 60 | [gatewayLib.address, messageBus.address, merklePatriciaProof.address]); 61 | } catch (error) { 62 | Logger.error('error while executing mosaic libraries', { error: error.toString() }); 63 | process.exit(1); 64 | } 65 | 66 | process.exit(0); 67 | }, 68 | ) 69 | .parse(process.argv); 70 | -------------------------------------------------------------------------------- /graph/auxiliary/generated/AnchorSchema.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | 3 | import { 4 | TypedMap, 5 | Entity, 6 | Value, 7 | ValueKind, 8 | store, 9 | Address, 10 | Bytes, 11 | BigInt, 12 | BigDecimal 13 | } from "@graphprotocol/graph-ts"; 14 | 15 | export class StateRootAvailable extends Entity { 16 | constructor(id: string) { 17 | super(); 18 | this.set("id", Value.fromString(id)); 19 | } 20 | 21 | save(): void { 22 | let id = this.get("id"); 23 | assert(id !== null, "Cannot save StateRootAvailable entity without an ID"); 24 | assert( 25 | id.kind == ValueKind.STRING, 26 | "Cannot save StateRootAvailable entity with non-string ID. " + 27 | 'Considering using .toHex() to convert the "id" to a string.' 28 | ); 29 | store.set("StateRootAvailable", id.toString(), this); 30 | } 31 | 32 | static load(id: string): StateRootAvailable | null { 33 | return store.get("StateRootAvailable", id) as StateRootAvailable | null; 34 | } 35 | 36 | get id(): string { 37 | let value = this.get("id"); 38 | return value.toString(); 39 | } 40 | 41 | set id(value: string) { 42 | this.set("id", Value.fromString(value)); 43 | } 44 | 45 | get _blockHeight(): BigInt { 46 | let value = this.get("_blockHeight"); 47 | return value.toBigInt(); 48 | } 49 | 50 | set _blockHeight(value: BigInt) { 51 | this.set("_blockHeight", Value.fromBigInt(value)); 52 | } 53 | 54 | get _stateRoot(): Bytes { 55 | let value = this.get("_stateRoot"); 56 | return value.toBytes(); 57 | } 58 | 59 | set _stateRoot(value: Bytes) { 60 | this.set("_stateRoot", Value.fromBytes(value)); 61 | } 62 | 63 | get blockNumber(): BigInt { 64 | let value = this.get("blockNumber"); 65 | return value.toBigInt(); 66 | } 67 | 68 | set blockNumber(value: BigInt) { 69 | this.set("blockNumber", Value.fromBigInt(value)); 70 | } 71 | 72 | get blockHash(): Bytes { 73 | let value = this.get("blockHash"); 74 | return value.toBytes(); 75 | } 76 | 77 | set blockHash(value: Bytes) { 78 | this.set("blockHash", Value.fromBytes(value)); 79 | } 80 | 81 | get contractAddress(): Bytes { 82 | let value = this.get("contractAddress"); 83 | return value.toBytes(); 84 | } 85 | 86 | set contractAddress(value: Bytes) { 87 | this.set("contractAddress", Value.fromBytes(value)); 88 | } 89 | 90 | get uts(): BigInt { 91 | let value = this.get("uts"); 92 | return value.toBigInt(); 93 | } 94 | 95 | set uts(value: BigInt) { 96 | this.set("uts", Value.fromBigInt(value)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /graph/origin/generated/AnchorSchema.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | 3 | import { 4 | TypedMap, 5 | Entity, 6 | Value, 7 | ValueKind, 8 | store, 9 | Address, 10 | Bytes, 11 | BigInt, 12 | BigDecimal 13 | } from "@graphprotocol/graph-ts/index"; 14 | 15 | export class StateRootAvailable extends Entity { 16 | constructor(id: string) { 17 | super(); 18 | this.set("id", Value.fromString(id)); 19 | } 20 | 21 | save(): void { 22 | let id = this.get("id"); 23 | assert(id !== null, "Cannot save StateRootAvailable entity without an ID"); 24 | assert( 25 | id.kind == ValueKind.STRING, 26 | "Cannot save StateRootAvailable entity with non-string ID. " + 27 | 'Considering using .toHex() to convert the "id" to a string.' 28 | ); 29 | store.set("StateRootAvailable", id.toString(), this); 30 | } 31 | 32 | static load(id: string): StateRootAvailable | null { 33 | return store.get("StateRootAvailable", id) as StateRootAvailable | null; 34 | } 35 | 36 | get id(): string { 37 | let value = this.get("id"); 38 | return value.toString(); 39 | } 40 | 41 | set id(value: string) { 42 | this.set("id", Value.fromString(value)); 43 | } 44 | 45 | get _blockHeight(): BigInt { 46 | let value = this.get("_blockHeight"); 47 | return value.toBigInt(); 48 | } 49 | 50 | set _blockHeight(value: BigInt) { 51 | this.set("_blockHeight", Value.fromBigInt(value)); 52 | } 53 | 54 | get _stateRoot(): Bytes { 55 | let value = this.get("_stateRoot"); 56 | return value.toBytes(); 57 | } 58 | 59 | set _stateRoot(value: Bytes) { 60 | this.set("_stateRoot", Value.fromBytes(value)); 61 | } 62 | 63 | get blockNumber(): BigInt { 64 | let value = this.get("blockNumber"); 65 | return value.toBigInt(); 66 | } 67 | 68 | set blockNumber(value: BigInt) { 69 | this.set("blockNumber", Value.fromBigInt(value)); 70 | } 71 | 72 | get blockHash(): Bytes { 73 | let value = this.get("blockHash"); 74 | return value.toBytes(); 75 | } 76 | 77 | set blockHash(value: Bytes) { 78 | this.set("blockHash", Value.fromBytes(value)); 79 | } 80 | 81 | get contractAddress(): Bytes { 82 | let value = this.get("contractAddress"); 83 | return value.toBytes(); 84 | } 85 | 86 | set contractAddress(value: Bytes) { 87 | this.set("contractAddress", Value.fromBytes(value)); 88 | } 89 | 90 | get uts(): BigInt { 91 | let value = this.get("uts"); 92 | return value.toBigInt(); 93 | } 94 | 95 | set uts(value: BigInt) { 96 | this.set("uts", Value.fromBigInt(value)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/Directory.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import { assert } from 'chai'; 3 | import * as os from 'os'; 4 | import * as path from 'path'; 5 | import Directory from '../src/Directory'; 6 | 7 | describe('Directory.projectRoot', () => { 8 | it('returns the correct root of the project', () => { 9 | assert.strictEqual( 10 | Directory.projectRoot, 11 | path.join( 12 | __dirname, 13 | '..', 14 | ), 15 | 'Project root is not set to the actual root of this project.', 16 | ); 17 | }); 18 | }); 19 | 20 | describe('Directory.sanitize()', () => { 21 | it('replaces tilde with the home directory', () => { 22 | assert.strictEqual( 23 | Directory.sanitize('~/test/other'), 24 | path.join( 25 | os.homedir(), 26 | 'test', 27 | 'other', 28 | ), 29 | 'Directory does not correctly replace the tilde with the home directory.', 30 | ); 31 | }); 32 | 33 | it('converts relative to absolute path', () => { 34 | assert.strictEqual( 35 | Directory.sanitize('./some/path'), 36 | path.join( 37 | Directory.projectRoot, 38 | 'some', 39 | 'path', 40 | ), 41 | 'Does not properly replace `.` with the project root.', 42 | ); 43 | 44 | assert.strictEqual( 45 | Directory.sanitize('other/path'), 46 | path.join( 47 | Directory.projectRoot, 48 | 'other', 49 | 'path', 50 | ), 51 | 'Does not properly add project root before a relative path.', 52 | ); 53 | }); 54 | 55 | it('Returns full path of GatewayConfig', () => { 56 | const originChain = 'dev'; 57 | const auxChainId = 1000; 58 | const gatewayAddress = '0xae02c7b1c324a8d94a564bc8d713df89eae441fe'; 59 | const expectedPath = `${Directory.getDefaultMosaicDataDir}/dev/1000/gateway-0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe/gateway-config.json`; 60 | const fullPath = Directory.getGatewayConfigPath( 61 | originChain, 62 | auxChainId, 63 | gatewayAddress, 64 | ); 65 | assert.strictEqual( 66 | fullPath, 67 | expectedPath, 68 | `Path: ${fullPath} is not equal to expectedPath: ${expectedPath}`, 69 | ); 70 | }); 71 | }); 72 | 73 | describe('Directory.getProjectChainsDirectory()', () => { 74 | it('should return project chain directory', () => { 75 | const projectChainsDirectory = Directory.getProjectChainsDirectory; 76 | 77 | assert.strictEqual( 78 | projectChainsDirectory, 79 | path.join( 80 | Directory.projectRoot, 81 | 'chains', 82 | ), 83 | 'Expected project chain directory doesnot match', 84 | ); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /src/bin/Validator.ts: -------------------------------------------------------------------------------- 1 | import * as web3Utils from 'web3-utils'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import ChainInfo from '../Node/ChainInfo'; 5 | import Directory from '../Directory'; 6 | import MosaicConfig from '../Config/MosaicConfig'; 7 | 8 | import Web3 = require('web3'); 9 | 10 | /** 11 | * This class contains methods to validate commandline arguments. 12 | */ 13 | export default class Validator { 14 | /** 15 | * This method validates an ethereum address. 16 | * @param value Ethereum address. 17 | */ 18 | public static isValidAddress(value: string): boolean { 19 | return web3Utils.isAddress(value); 20 | } 21 | 22 | /** 23 | * This method validates an origin chain. 24 | * @param value Chain identifier. 25 | */ 26 | public static isValidOriginChain(value: string): boolean { 27 | return !!ChainInfo.publicOriginChainNameToIdMap[value] || ChainInfo.isDevChain(value); 28 | } 29 | 30 | /** 31 | * This method validates an aux chain. 32 | * @param auxChain Chain identifier. 33 | * @param originChainId Origin chain identifier. 34 | */ 35 | public static isValidAuxChain(auxChain: string, originChainId: string): boolean { 36 | if (MosaicConfig.exists(originChainId)) { 37 | const mosaicConfig = MosaicConfig.fromChain(originChainId); 38 | if (mosaicConfig.auxiliaryChains[auxChain]) { 39 | return true; 40 | } 41 | } 42 | if (ChainInfo.isDevChain(auxChain)) { 43 | return true; 44 | } 45 | let validAuxChain = false; 46 | const chainDir = Directory.getProjectChainsDirectory; 47 | const originChainDirectories = Validator.getDirectories(chainDir); 48 | originChainDirectories.forEach((originChain) => { 49 | const auxDirectory = Directory.getProjectUtilityChainDir(originChain, auxChain); 50 | if (fs.existsSync(auxDirectory)) { 51 | validAuxChain = true; 52 | } 53 | }); 54 | return validAuxChain; 55 | } 56 | 57 | public static async isValidWeb3EndPoint(web3EndPoint: string): Promise { 58 | const web3 = new Web3(web3EndPoint); 59 | // https://web3js.readthedocs.io/en/v1.2.0/web3-net.html#islistening 60 | return web3.eth.net.isListening(); 61 | } 62 | 63 | /** 64 | * This function returns directories inside a folder. 65 | * @param folderPath Path of folder. 66 | */ 67 | private static getDirectories(folderPath) { 68 | return fs.readdirSync(folderPath) 69 | .map(file => ({ 70 | fullPath: path.join(folderPath, file), 71 | name: file, 72 | })) 73 | .filter(dir => fs.statSync(dir.fullPath).isDirectory()) 74 | .map(dir => dir.name); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /graph/origin/subgraph.yaml.mustache: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.1 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum/contract 6 | name: OSTComposer 7 | network: dev 8 | source: 9 | address: "{{stakePoolAddress}}" 10 | abi: OSTComposer 11 | mapping: 12 | kind: ethereum/events 13 | apiVersion: 0.0.2 14 | language: wasm/assemblyscript 15 | entities: 16 | - StakeRequested 17 | abis: 18 | - name: OSTComposer 19 | file: "{{{projectRoot}}}/abi/0.12/OSTComposer.json" 20 | eventHandlers: 21 | - event: StakeRequested(uint256,address,uint256,uint256,uint256,address,address,address,bytes32) 22 | handler: handleStakeRequested 23 | file: ./src/OSTComposerMapping.ts 24 | 25 | - kind: ethereum/contract 26 | name: EIP20Gateway 27 | network: dev 28 | source: 29 | address: "{{eip20GatewayAddress}}" 30 | abi: EIP20Gateway 31 | mapping: 32 | kind: ethereum/events 33 | apiVersion: 0.0.2 34 | language: wasm/assemblyscript 35 | entities: 36 | - StakeIntentDeclared 37 | - StakeProgressed 38 | - RedeemIntentConfirmed 39 | - UnstakeProgressed 40 | - GatewayProven 41 | abis: 42 | - name: EIP20Gateway 43 | file: "{{{projectRoot}}}/abi/0.12/EIP20Gateway.json" 44 | eventHandlers: 45 | - event: StakeIntentDeclared(bytes32,address,uint256,address,uint256) 46 | handler: handleStakeIntentDeclared 47 | - event: StakeProgressed(bytes32,address,uint256,uint256,bool,bytes32) 48 | handler: handleStakeProgressed 49 | - event: RedeemIntentConfirmed(bytes32,address,uint256,address,uint256,uint256,bytes32) 50 | handler: handleRedeemIntentConfirmed 51 | - event: GatewayProven(address,uint256,bytes32,bool) 52 | handler: handleGatewayProven 53 | - event: UnstakeProgressed(bytes32,address,address,uint256,uint256,uint256,bool,bytes32) 54 | handler: handleUnstakeProgressed 55 | file: ./src/EIP20GatewayMapping.ts 56 | 57 | - kind: ethereum/contract 58 | name: Anchor 59 | network: dev 60 | source: 61 | address: "{{anchorAddress}}" 62 | abi: Anchor 63 | mapping: 64 | kind: ethereum/events 65 | apiVersion: 0.0.2 66 | language: wasm/assemblyscript 67 | entities: 68 | - StateRootAvailable 69 | abis: 70 | - name: Anchor 71 | file: "{{{projectRoot}}}/abi/0.12/Anchor.json" 72 | eventHandlers: 73 | - event: StateRootAvailable(uint256,bytes32) 74 | handler: handleStateRootAvailable 75 | file: ./src/AnchorMapping.ts 76 | 77 | -------------------------------------------------------------------------------- /graph/auxiliary/subgraph.yaml.mustache: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.1 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum/contract 6 | name: Anchor 7 | network: dev 8 | source: 9 | address: "{{anchorAddress}}" 10 | abi: Anchor 11 | mapping: 12 | kind: ethereum/events 13 | apiVersion: 0.0.2 14 | language: wasm/assemblyscript 15 | entities: 16 | - StateRootAvailable 17 | abis: 18 | - name: Anchor 19 | file: "{{{projectRoot}}}/abi/0.12/Anchor.json" 20 | eventHandlers: 21 | - event: StateRootAvailable(uint256,bytes32) 22 | handler: handleStateRootAvailable 23 | file: ./src/AnchorMapping.ts 24 | 25 | - kind: ethereum/contract 26 | name: EIP20CoGateway 27 | network: dev 28 | source: 29 | address: "{{eip20CoGatewayAddress}}" 30 | abi: EIP20CoGateway 31 | mapping: 32 | kind: ethereum/events 33 | apiVersion: 0.0.2 34 | language: wasm/assemblyscript 35 | entities: 36 | - StakeIntentConfirmed 37 | - MintProgressed 38 | - GatewayProven 39 | - RedeemIntentDeclared 40 | - RedeemProgressed 41 | abis: 42 | - name: EIP20CoGateway 43 | file: "{{{projectRoot}}}/abi/0.12/EIP20CoGateway.json" 44 | eventHandlers: 45 | - event: StakeIntentConfirmed(bytes32,address,uint256,address,uint256,uint256,bytes32) 46 | handler: handleStakeIntentConfirmed 47 | - event: MintProgressed(bytes32,address,address,uint256,uint256,uint256,bool,bytes32) 48 | handler: handleMintProgressed 49 | - event: GatewayProven(address,uint256,bytes32,bool) 50 | handler: handleGatewayProven 51 | - event: RedeemIntentDeclared(bytes32,address,uint256,address,uint256) 52 | handler: handleRedeemIntentDeclared 53 | - event: RedeemProgressed(bytes32,address,uint256,uint256,bool,bytes32) 54 | handler: handleRedeemProgressed 55 | file: ./src/EIP20CoGatewayMapping.ts 56 | 57 | - kind: ethereum/contract 58 | name: RedeemPool 59 | network: dev 60 | source: 61 | address: "{{redeemPoolAddress}}" 62 | abi: RedeemPool 63 | mapping: 64 | kind: ethereum/events 65 | apiVersion: 0.0.2 66 | language: wasm/assemblyscript 67 | entities: 68 | - RedeemRequested 69 | - RedeemRevoked 70 | - RedeemRejected 71 | abis: 72 | - name: RedeemPool 73 | file: "{{{projectRoot}}}/abi/0.12/RedeemPool.json" 74 | eventHandlers: 75 | - event: RedeemRequested(uint256,address,uint256,uint256,uint256,address,address,address,bytes32) 76 | handler: handleRedeemRequested 77 | - event: RedeemRevoked(address,bytes32) 78 | handler: handleRedeemRevoked 79 | - event: RedeemRejected(address,bytes32) 80 | handler: handleRedeemRejected 81 | file: ./src/RedeemPoolMapping.ts 82 | -------------------------------------------------------------------------------- /src/Node/ParityNode.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as os from 'os'; 3 | import Node from './Node'; 4 | import Shell from '../Shell'; 5 | 6 | /** 7 | * Represents a parity node that runs in a docker container. 8 | */ 9 | export default class ParityNode extends Node { 10 | /** 11 | * Starts the container that runs this chain node. 12 | */ 13 | public start(): void { 14 | this.logInfo('starting parity container'); 15 | this.initializeDirectories(); 16 | super.ensureNetworkExists(); 17 | 18 | let args = [ 19 | 'run', 20 | ]; 21 | 22 | if (!this.keepAfterStop) { 23 | args = args.concat('--rm'); 24 | } 25 | 26 | args = args.concat([ 27 | '--network', Node.network, 28 | '--detach', 29 | '--name', this.containerName, 30 | '--publish', `${this.port}:${this.port}`, 31 | '--publish', `${this.rpcPort}:8545`, 32 | '--publish', `${this.websocketPort}:8546`, 33 | '--volume', `${this.chainDir}:/home/parity/.local/share/io.parity.ethereum/`, 34 | ]); 35 | 36 | // Running the parity process inside the container as the same user id that is executing this 37 | // script. 38 | // This is required, because otherwise the parity process will not be able to write to the 39 | // mounted directory. The parity process inside the container is not run as root (as usual), 40 | // but instead runs with uid/guid 1000/1000 by default. This option overrides that default 41 | // behavior so that the parity process can write to its mounted chain directory in all 42 | // environments. This was introduced after failing tests on Travis CI. 43 | const userInfo = os.userInfo(); 44 | args = args.concat([ 45 | '--user', `${userInfo.uid}:${userInfo.gid}`, 46 | ]); 47 | 48 | if (this.password !== '') { 49 | args = args.concat([ 50 | '--volume', `${this.password}:/home/parity/password.txt`, 51 | ]); 52 | } 53 | 54 | args = args.concat([ 55 | 'parity/parity:v2.5.5-stable', 56 | `--chain=${this.chain}`, 57 | '--base-path=/home/parity/.local/share/io.parity.ethereum/', 58 | `--port=${this.port}`, 59 | '--jsonrpc-apis=all', 60 | '--jsonrpc-interface=all', 61 | '--jsonrpc-experimental', 62 | '--ws-port=8546', 63 | '--ws-interface=all', 64 | '--ws-apis=all', 65 | '--ws-origins=all', 66 | '--ws-hosts=all', 67 | ]); 68 | 69 | if (this.unlock !== '') { 70 | args = args.concat([ 71 | '--unlock', this.unlock, 72 | '--password', '/home/parity/password.txt', 73 | ]); 74 | } 75 | Shell.executeDockerCommand(args); 76 | } 77 | 78 | /** 79 | * Initialize directories required by parity to run. 80 | */ 81 | private initializeDirectories(): void { 82 | super.initializeDataDir(); 83 | 84 | if (!fs.existsSync(this.chainDir)) { 85 | this.logInfo(`${this.chainDir} does not exist; initializing`); 86 | fs.mkdirSync(this.chainDir, { recursive: true }); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/lib/SubGraph.ts: -------------------------------------------------------------------------------- 1 | import GatewayAddresses from '../Config/GatewayAddresses'; 2 | import GatewayConfig from '../Config/GatewayConfig'; 3 | import MosaicConfig from '../Config/MosaicConfig'; 4 | import SubGraph, { SubGraphType } from '../Graph/SubGraph'; 5 | import Validator from '../bin/Validator'; 6 | 7 | const deploySubGraph = ( 8 | originChain: string, 9 | auxiliaryChain: string, 10 | subgraphType: SubGraphType, 11 | graphAdminRPC: string, 12 | graphIPFS: string, 13 | mosaicConfigPath?: string, 14 | gatewayAddress?: string, 15 | gatewayConfigPath?: string, 16 | ): {success: boolean; message: string; subgraphName: string} => { 17 | if (!Validator.isValidOriginChain(originChain)) { 18 | throw new Error(`Invalid origin chain identifier: ${originChain}`); 19 | } 20 | 21 | if (!Validator.isValidAuxChain(auxiliaryChain, originChain)) { 22 | throw new Error(`Invalid aux chain identifier: ${auxiliaryChain}`); 23 | } 24 | if (gatewayAddress && !Validator.isValidAddress(gatewayAddress)) { 25 | throw new Error(`Invalid deployer address: ${gatewayAddress}`); 26 | } 27 | 28 | let gatewayAddresses: GatewayAddresses; 29 | let gatewayConfig: GatewayConfig; 30 | let mosaicConfig: MosaicConfig; 31 | 32 | const inputSet = new Set(); 33 | inputSet.add(mosaicConfigPath); 34 | inputSet.add(gatewayAddress); 35 | inputSet.add(gatewayConfigPath); 36 | 37 | if (inputSet.size > 2) { 38 | throw new Error('Only one option should be passed from --mosaic-config,--gateway-config and --gateway-address.'); 39 | } 40 | if (gatewayConfigPath) { 41 | gatewayConfig = GatewayConfig.fromFile(gatewayConfigPath); 42 | } else if (gatewayAddress) { 43 | gatewayConfig = GatewayConfig.fromChain( 44 | originChain, 45 | parseInt(auxiliaryChain, 10), 46 | gatewayAddress, 47 | ); 48 | } 49 | 50 | if (mosaicConfigPath) { 51 | mosaicConfig = MosaicConfig.fromFile(mosaicConfigPath); 52 | } else if (MosaicConfig.exists(originChain)) { 53 | mosaicConfig = MosaicConfig.fromChain(originChain); 54 | } 55 | 56 | if (gatewayConfig) { 57 | if (parseInt(auxiliaryChain, 10) !== gatewayConfig.auxChainId) { 58 | throw new Error(`Auxiliary chain id in gateway config is ${gatewayConfig.auxChainId} but value passed is ${auxiliaryChain}`); 59 | } 60 | gatewayAddresses = GatewayAddresses.fromGatewayConfig(gatewayConfig); 61 | } else if (mosaicConfig) { 62 | if (mosaicConfig.originChain.chain !== originChain) { 63 | throw new Error(`Origin chain id in mosaic config is ${mosaicConfig.originChain.chain} but received argument is ${originChain}`); 64 | } 65 | gatewayAddresses = GatewayAddresses.fromMosaicConfig( 66 | mosaicConfig, 67 | auxiliaryChain.toString(), 68 | ); 69 | } 70 | 71 | if (!gatewayAddresses) { 72 | throw new Error('Mosaic config or gateway config not found . Use --mosaic-config or --gateway-config option to provide path.'); 73 | } 74 | 75 | return new SubGraph( 76 | originChain, 77 | auxiliaryChain.toString(), 78 | subgraphType, 79 | graphAdminRPC, 80 | graphIPFS, 81 | gatewayAddresses, 82 | ).deploy(); 83 | }; 84 | 85 | export default deploySubGraph; 86 | -------------------------------------------------------------------------------- /chains/ethereum/mosaic.json: -------------------------------------------------------------------------------- 1 | { 2 | "originChain": { 3 | "contractAddresses": { 4 | "gatewayLibAddress": "0x1c9e18c8ab156753f7eca95d8c042f7842aaf09e", 5 | "messageBusAddress": "0x0b71bb83d2814633f7dfeb9cb5c4c2e63be215ed", 6 | "merklePatriciaLibAddress": "0x30db9dd1875dbea601ba061e81dd88325d68fd18", 7 | "valueTokenAddress": "0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca" 8 | }, 9 | "chain": "ethereum" 10 | }, 11 | "auxiliaryChains": { 12 | "1414": { 13 | "bootNodes": [ 14 | "enode://c677ea2e455828db3d2f7bbc232518cfe683475edae66ab46fcf27bc21efb991fb37776f815d679a449374e7363692aee3c89a70f6b8cd754abc95fa11a3c757@99.80.181.153:30301" 15 | ], 16 | "contractAddresses": { 17 | "origin": { 18 | "baseTokenAddress": "0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca", 19 | "anchorOrganizationAddress": "0xc450a2fa865972fb7a07acad6cf6c3654e37c617", 20 | "anchorAddress": "0x981884d4369f7b7892d8c279d2125daf7422d9e0", 21 | "gatewayOrganizationAddress": "0x813bcae43e1aff7466d9d34a8163a2bf5216dc1f", 22 | "eip20GatewayAddress": "0xe0a3929c6947cdc091e6e6725826a09615062131" 23 | }, 24 | "auxiliary": { 25 | "anchorOrganizationAddress": "0xbe29a93ced3ca8801ccdc4a4f8cc756f293e8973", 26 | "anchorAddress": "0xe66af880713e8b34e54150b95794736bea96e4be", 27 | "coGatewayOrganizationAddress": "0x47dfd95dbcb3006813a483b1122f81b843a873a7", 28 | "utilityTokenAddress": "0x2f973cb687ebab040c3e448cc31ca281d9a5ca45", 29 | "eip20CoGatewayAddress": "0x25cf88ef96af49950b1bb942d04b5563312dd82b", 30 | "gatewayLibAddress": "0x9902f4fe064a8471333224e68c6e9159e38ad73f", 31 | "messageBusAddress": "0x82207dd728f447056b61284983595fa95ad3b627", 32 | "merklePatriciaLibAddress": "0x4045c0155fe09757f2576c2624aa15826bb87fa7" 33 | } 34 | }, 35 | "chainId": 1414, 36 | "genesis": { 37 | "config": { 38 | "chainId": 1414, 39 | "homesteadBlock": 1, 40 | "eip150Block": 2, 41 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 42 | "eip155Block": 3, 43 | "eip158Block": 3, 44 | "byzantiumBlock": 4, 45 | "clique": { 46 | "period": 3, 47 | "epoch": 30000 48 | } 49 | }, 50 | "nonce": "0x0", 51 | "timestamp": "0x5a71660b", 52 | "extraData": "0x7574696c69747920636861696e0000000000000000000000000000000000000082Af4EFe75Fa0fe0525f59e51453DF961528fD570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 53 | "gasLimit": "0x989680", 54 | "difficulty": "0x1", 55 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 56 | "coinbase": "0x0000000000000000000000000000000000000000", 57 | "alloc": { 58 | "2daded60b5d68d7e4f12307c4f6fa8fb11956f32": { 59 | "balance": "0x295be96e640669720000000" 60 | } 61 | }, 62 | "number": "0x0", 63 | "gasUsed": "0x0", 64 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openst/mosaic-chains", 3 | "version": "0.1.0-alpha.10", 4 | "description": "Mosaic chains is an executable to run and manage mosaic chains.", 5 | "bin": { 6 | "mosaic": "lib/src/bin/mosaic.js" 7 | }, 8 | "main": "lib/index.js", 9 | "files": [ 10 | "lib/**" 11 | ], 12 | "scripts": { 13 | "lint": "eslint src -c .eslintrc.json --ext ts", 14 | "package": "./package.sh", 15 | "prepack": "npm run package", 16 | "test": "npm run test:unit && npm run test:smoke && npm run test:integration", 17 | "test:unit": "mocha -r ts-node/register tests/**/**/*.test.ts tests/**/*.test.ts tests/*.test.ts", 18 | "test:smoke": "npm run test:smoke:ropsten && npm run test:smoke:goerli && npm run test:smoke:1405 && npm run test:smoke:1406 && npm run test:smoke:1407 && npm run test:smoke:dev-origin && npm run test:smoke:dev-auxiliary", 19 | "test:smoke:ethereum": "./tests/smoke.sh ethereum", 20 | "test:smoke:ropsten": "./tests/smoke.sh ropsten", 21 | "test:smoke:goerli": "./tests/smoke.sh goerli", 22 | "test:smoke:1405": "./tests/smoke.sh 1405", 23 | "test:smoke:1406": "./tests/smoke.sh 1406", 24 | "test:smoke:1407": "./tests/smoke.sh 1407", 25 | "test:smoke:1414": "./tests/smoke.sh 1414", 26 | "test:smoke:dev-origin": "./tests/smoke.sh dev-origin", 27 | "test:smoke:dev-auxiliary": "./tests/smoke.sh dev-auxiliary", 28 | "test:package": "./tests/package.sh", 29 | "test:integration": "mocha -r ts-node/register test_integration/**/*.test.ts test_integration/*.test.ts --timeout 3000000", 30 | "pull-abi": "sh ./tools/abi_downloader/run.sh", 31 | "whitelist-workers": "ts-node tools/whitelistworker.ts" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/mosaicdao/mosaic-chains.git" 36 | }, 37 | "author": "OpenST Ltd.", 38 | "license": "LGPL-3.0-only", 39 | "bugs": { 40 | "url": "https://github.com/mosaicdao/mosaic-chains/issues" 41 | }, 42 | "homepage": "https://github.com/mosaicdao/mosaic-chains#readme", 43 | "dependencies": { 44 | "@graphprotocol/graph-cli": "0.12.0", 45 | "@graphprotocol/graph-ts": "0.11.0", 46 | "@openst/mosaic-contracts": "^0.12.0", 47 | "@openst/mosaic.js": "^0.10.0-beta.5", 48 | "commander": "2.19.0", 49 | "fs-extra": "7.0.1", 50 | "ip": "1.1.5", 51 | "jsonschema": "1.2.4", 52 | "markdown-table": "1.1.3", 53 | "mustache": "3.0.1", 54 | "rlp": "2.2.3", 55 | "ts-node": "8.0.3", 56 | "typescript": "3.3.3333", 57 | "wait-port": "0.2.2", 58 | "web3": "1.0.0-beta.37", 59 | "winston": "3.2.1" 60 | }, 61 | "devDependencies": { 62 | "@types/chai": "4.1.7", 63 | "@types/fs-extra": "5.0.5", 64 | "@types/mocha": "5.2.6", 65 | "@types/node": "11.11.3", 66 | "@types/web3": "1.0.18", 67 | "@typescript-eslint/eslint-plugin": "1.5.0", 68 | "@typescript-eslint/parser": "1.4.2", 69 | "chai": "4.2.0", 70 | "eslint": "5.16.0", 71 | "eslint-config-airbnb-base": "13.1.0", 72 | "eslint-plugin-import": "2.14.0", 73 | "eslint-plugin-json": "1.4.0", 74 | "mocha": "6.1.4", 75 | "subscriptions-transport-ws": "0.9.16", 76 | "ws": "7.0.0", 77 | "sinon": "7.3.2", 78 | "@types/sinon": "7.0.13", 79 | "@types/chai-as-promised": "7.1.0", 80 | "chai-as-promised": "7.1.1" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/Config/fromFile.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import * as sinon from 'sinon'; 3 | import { assert } from 'chai'; 4 | import GatewayConfig from '../../src/Config/GatewayConfig'; 5 | 6 | 7 | describe('GatewayConfig.fromFile()', () => { 8 | const filePath = './tests/Config/testdata/0xae02c7b1c324a8d94a564bc8d713df89eae441fe.json'; 9 | const auxChainId = 1000; 10 | const originBaseTokenAddress = '0x9ac77f4c0ca4d0f2142d7a77175cf4f1295fb2d8'; 11 | const valueTokenAddress = '0x8e183Fd2cd55C7C05bBf4FAC989740f69e559A6d'; 12 | const gatewayOrganizationAddress = '0x3f99f42d226A0CD1C1Fcae1e8dC11b2f7a9DcE4B'; 13 | const eip20GatewayAddress = '0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe'; 14 | const coGatewayOrganizationAddress = '0x2D586C7E220839a9284888B10aDF4823AcD6EdF3'; 15 | const utilityTokenAddress = '0x62F8729C1C282C231a22252e90CE9735533D2518'; 16 | const eip20CoGatewayAddress = '0xc6fF898ceBf631eFb58eEc7187E4c1f70AE8d943'; 17 | 18 | it('Should return TokenConfig object', async () => { 19 | const gatewayConfig = GatewayConfig.fromFile(filePath); 20 | 21 | assert.strictEqual( 22 | gatewayConfig.auxChainId, 23 | auxChainId, 24 | 'Expected GatewayConfig auxChainId is not equal to actual auxChainId', 25 | ); 26 | 27 | assert.strictEqual( 28 | gatewayConfig.originContracts.baseTokenAddress, 29 | originBaseTokenAddress, 30 | 'Expected GatewayConfig baseTokenAddress ' 31 | + 'is not equal to actual originBaseTokenAddress', 32 | ); 33 | assert.strictEqual( 34 | gatewayConfig.originContracts.eip20GatewayAddress, 35 | eip20GatewayAddress, 36 | 'Expected GatewayConfig origin eip20GatewayAddress ' 37 | + 'is not equal to actual eip20GatewayAddress', 38 | ); 39 | assert.strictEqual( 40 | gatewayConfig.originContracts.gatewayOrganizationAddress, 41 | gatewayOrganizationAddress, 42 | 'Expected GatewayConfig origin gatewayOrganizationAddress ' 43 | + 'is not equal to actual gatewayOrganizationAddress', 44 | ); 45 | assert.strictEqual( 46 | gatewayConfig.originContracts.valueTokenAddress, 47 | valueTokenAddress, 48 | 'Expected GatewayConfig origin valueTokenAddress ' 49 | + 'is not equal to actual valueTokenAddress', 50 | ); 51 | assert.strictEqual( 52 | gatewayConfig.auxiliaryContracts.utilityTokenAddress, 53 | utilityTokenAddress, 54 | 'Expected GatewayConfig auxiliary utilityTokenAddress ' 55 | + 'is not equal to actual utilityTokenAddress', 56 | ); 57 | assert.strictEqual( 58 | gatewayConfig.auxiliaryContracts.eip20CoGatewayAddress, 59 | eip20CoGatewayAddress, 60 | 'Expected GatewayConfig eip20CoGatewayAddress ' 61 | + 'is not equal to actual eip20CoGatewayAddress', 62 | ); 63 | assert.strictEqual( 64 | gatewayConfig.auxiliaryContracts.coGatewayOrganizationAddress, 65 | coGatewayOrganizationAddress, 66 | 'Expected GatewayConfig auxiliary coGatewayOrganizationAddress ' 67 | + 'is not equal to actual coGatewayOrganizationAddress', 68 | ); 69 | sinon.restore(); 70 | }); 71 | 72 | it('Should fail when input filePath is incorrect', async () => { 73 | assert.throws( 74 | () => GatewayConfig.fromFile('wrongPath'), 75 | 'Missing GatewayConfig file at path: wrongPath', 76 | ); 77 | sinon.restore(); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /src/Config/GatewayAddresses.ts: -------------------------------------------------------------------------------- 1 | import MosaicConfig from './MosaicConfig'; 2 | import GatewayConfig from './GatewayConfig'; 3 | 4 | /** 5 | * This class represents set of addresses specific to a gateway pair. 6 | */ 7 | export default class GatewayAddresses { 8 | public readonly stakePoolAddress: string; 9 | 10 | public readonly eip20GatewayAddress: string; 11 | 12 | public readonly anchorAddress: string; 13 | 14 | public readonly coAnchorAddress: string; 15 | 16 | public readonly eip20CoGatewayAddress: string; 17 | 18 | public readonly redeemPoolAddress: string; 19 | 20 | /** 21 | * Constructor 22 | * @param stakePoolAddress StakePool address. 23 | * @param eip20GatewayAddress eip20Gateway address. 24 | * @param anchorAddress anchor address. 25 | * @param coAnchorAddress coanchor address. 26 | * @param eip20CoGatewayAddress cogateway address. 27 | * @param redeemPoolAddress redeem pool address. 28 | */ 29 | private constructor( 30 | stakePoolAddress: string, 31 | eip20GatewayAddress: string, 32 | anchorAddress: string, 33 | coAnchorAddress: string, 34 | eip20CoGatewayAddress: string, 35 | redeemPoolAddress: string, 36 | ) { 37 | this.stakePoolAddress = stakePoolAddress; 38 | this.eip20GatewayAddress = eip20GatewayAddress; 39 | this.anchorAddress = anchorAddress; 40 | this.coAnchorAddress = coAnchorAddress; 41 | this.eip20CoGatewayAddress = eip20CoGatewayAddress; 42 | this.redeemPoolAddress = redeemPoolAddress; 43 | } 44 | 45 | /** 46 | * Create Gateway address instance based on mosaic config. 47 | * @param mosaicConfig Mosaic config object. 48 | * @param auxiliaryChain aux chain identifier. 49 | */ 50 | public static fromMosaicConfig( 51 | mosaicConfig: MosaicConfig, 52 | auxiliaryChain: string, 53 | ): GatewayAddresses { 54 | const auxiliaryContractAddresses = mosaicConfig.auxiliaryChains[auxiliaryChain] 55 | .contractAddresses.auxiliary; 56 | const originContractAddresses = mosaicConfig.auxiliaryChains[auxiliaryChain] 57 | .contractAddresses.origin; 58 | return new GatewayAddresses( 59 | mosaicConfig.originChain.contractAddresses.stakePoolAddress, 60 | originContractAddresses.eip20GatewayAddress, 61 | originContractAddresses.anchorAddress, 62 | auxiliaryContractAddresses.anchorAddress, 63 | auxiliaryContractAddresses.eip20CoGatewayAddress, 64 | auxiliaryContractAddresses.redeemPoolAddress, 65 | ); 66 | } 67 | 68 | /** 69 | * Creates gateway address instance from gateway config. 70 | * @param gatewayConfig GatewayConfig instance. 71 | */ 72 | public static fromGatewayConfig( 73 | gatewayConfig: GatewayConfig, 74 | ): GatewayAddresses { 75 | const { auxChainId } = gatewayConfig; 76 | const stakePoolAddress = gatewayConfig.originContracts.stakePoolAddress 77 | || gatewayConfig.mosaicConfig.originChain.contractAddresses.stakePoolAddress; 78 | 79 | const auxiliaryChain = gatewayConfig.mosaicConfig.auxiliaryChains[auxChainId]; 80 | const auxiliaryContracts = auxiliaryChain.contractAddresses.auxiliary; 81 | const redeemPool = gatewayConfig.auxiliaryContracts.redeemPoolAddress 82 | || auxiliaryContracts.redeemPoolAddress; 83 | 84 | const originContracts = auxiliaryChain.contractAddresses.origin; 85 | return new GatewayAddresses( 86 | stakePoolAddress, 87 | gatewayConfig.originContracts.eip20GatewayAddress, 88 | originContracts.anchorAddress, 89 | auxiliaryContracts.anchorAddress, 90 | gatewayConfig.auxiliaryContracts.eip20CoGatewayAddress, 91 | redeemPool, 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tools/whitelistworker.ts: -------------------------------------------------------------------------------- 1 | import { interacts } from '@openst/mosaic-contracts'; 2 | 3 | import { TransactionObject } from '@openst/mosaic-contracts/dist/interacts/types'; 4 | import { 5 | Utils as MosaicUtils, 6 | } from '@openst/mosaic.js'; 7 | import { OSTComposer } from '@openst/mosaic-contracts/dist/interacts/OSTComposer'; 8 | import { RedeemPool } from '@openst/mosaic-contracts/dist/interacts/RedeemPool'; 9 | import MosaicConfig from '../src/Config/MosaicConfig'; 10 | import Logger from '../src/Logger'; 11 | 12 | import Web3 = require('web3'); 13 | 14 | import BN = require('bn.js'); 15 | import assert = require('assert'); 16 | 17 | const originWeb3EndPoint = process.env.ORIGIN_WEB3_ENDPOINT; 18 | const auxiliaryweb3EndPoint = process.env.AUXILIARY_WEB3_ENDPOINT; 19 | const auxiliaryChainId = process.env.AUXILIARY_CHAIN_ID; 20 | const mosaicConfigPath = process.env.MOSAIC_CONFIG_PATH; 21 | const originWorker = process.env.ORIGIN_WORKER_ADDRESS; 22 | const auxiliaryWorker = process.env.AUXILIARY_WORKER_ADDRESS; 23 | const originExpirationHeight = process.env.ORIGIN_WORKER_EXPIRATION_HEIGHT; 24 | const auxiliaryExpirationHeight = process.env.AUXILIARY_WORKER_EXPIRATION_HEIGHT; 25 | assert(originWorker, 'Origin worker must be defined'); 26 | assert(auxiliaryWorker, 'Auxiliary worker must be defined'); 27 | assert(originExpirationHeight, 'Origin expiration height must be defined'); 28 | assert(auxiliaryExpirationHeight, 'Auxiliary expiration height must be defined'); 29 | assert(originWeb3EndPoint, 'Origin web3 endpoint must be defined'); 30 | assert(auxiliaryweb3EndPoint, 'Auxiliary web3 endpoint must be defined'); 31 | assert(auxiliaryChainId, 'Auxiliary chainId must be defined.'); 32 | const mosaicConfig = MosaicConfig.fromFile(mosaicConfigPath); 33 | const { stakePoolAddress } = mosaicConfig.originChain.contractAddresses; 34 | const { redeemPoolAddress } = mosaicConfig 35 | .auxiliaryChains[auxiliaryChainId].contractAddresses.auxiliary; 36 | 37 | assert(stakePoolAddress, 'Stake pool address must be defined'); 38 | assert(redeemPoolAddress, 'Redeem pool address must be defined'); 39 | const originWeb3 = new Web3(originWeb3EndPoint!); 40 | const auxiliaryWeb3 = new Web3(auxiliaryweb3EndPoint!); 41 | 42 | const stakePoolInstance = interacts.getOSTComposer(originWeb3, stakePoolAddress); 43 | const redeemPoolInstance = interacts.getRedeemPool(auxiliaryWeb3, redeemPoolAddress); 44 | 45 | 46 | /** 47 | * This method makes set worker transaction to redeempool or ost composer organization. 48 | * @param worker Address of worker. 49 | * @param web3 Web3 connection. 50 | * @param expirationHeightDiff Worker expiration height from current block. 51 | * @param poolInstance Instance of OSTComposer or RedeemPool. 52 | */ 53 | async function whitelistWorkersInPool( 54 | worker: string, 55 | web3: Web3, 56 | expirationHeightDiff: string, poolInstance: OSTComposer | RedeemPool, 57 | ) { 58 | const currentBlock = await web3.eth.getBlockNumber(); 59 | const expirationHeight = new BN(currentBlock).add(new BN(expirationHeightDiff)); 60 | const organizationAddress = await poolInstance.methods.organization().call(); 61 | const organizationContractInstance = interacts.getOrganization(web3, organizationAddress); 62 | 63 | const admin = await organizationContractInstance.methods.admin().call(); 64 | 65 | Logger.info(`Sending transaction from admin ${admin}, this account should be unlocked`); 66 | const setWorkerRawTx: TransactionObject = organizationContractInstance.methods.setWorker( 67 | worker, 68 | expirationHeight.toString(10), 69 | ); 70 | 71 | const receipt = await MosaicUtils.sendTransaction(setWorkerRawTx, { 72 | from: admin, 73 | gasPrice: await web3.eth.getGasPrice(), 74 | }); 75 | 76 | Logger.info(`receipt status of whitelisting worker address ${worker} is ${receipt.status}. Transaction hash is ${receipt.transactionHash}`); 77 | } 78 | 79 | whitelistWorkersInPool( 80 | originWorker!, 81 | originWeb3, 82 | originExpirationHeight, 83 | stakePoolInstance, 84 | ); 85 | whitelistWorkersInPool( 86 | auxiliaryWorker!, 87 | auxiliaryWeb3, 88 | auxiliaryExpirationHeight, 89 | redeemPoolInstance, 90 | ); 91 | -------------------------------------------------------------------------------- /docker/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | declare -a origin_keys=( "--rpcport" "--rpcaddr" "--rpcapi" "--wsport" "--wsaddr" "--wsapi" ) 3 | declare -a origin_defaultValues=( '8545' '0.0.0.0' 'eth,net,web3,network,debug,txpool,admin,personal' '8546' '0.0.0.0' 'eth,net,web3,network,debug,txpool,admin,personal' ) 4 | declare -a auxiliary_keys=( "--rpcport" "--rpcaddr" "--rpcapi" "--wsport" "--wsaddr" "--wsapi" ) 5 | declare -a auxiliary_defaultValues=( '8545' '0.0.0.0' 'eth,net,web3,network,debug,txpool,admin,personal' '8546' '0.0.0.0' 'eth,net,web3,network,debug,txpool,admin,personal' ) 6 | declare -a index 7 | commonCommands=" --rpc --ws " 8 | gethCommand="${gethCommand} ${commonCommands}" 9 | # add the index of the key in index array. 10 | function getIndex () 11 | { 12 | value=$1 13 | for i in "${!args[@]}"; do 14 | if [[ "${args[$i]}" = "${value}" ]]; then 15 | index+=( $i ); 16 | fi 17 | done 18 | } 19 | function buildCommand() 20 | { 21 | chainName=$1 22 | keyName=$chainName"_keys" 23 | keyDefaultValue=$chainName"_defaultValues" 24 | eval "keys=( "\${$keyName[@]}" )" 25 | eval "defaultValue=( "\${$keyDefaultValue[@]}" )" 26 | for j in "${!keys[@]}"; do 27 | key=${keys[$j]} 28 | getIndex $key 29 | if [ -z "$index" ]; then 30 | value="${defaultValue[$j]}" 31 | gethCommand+="${keys[$j]} ${value} " 32 | else 33 | portNumber="${args[$index + 1]}" 34 | gethCommand+="${keys[$j]} ${portNumber} " 35 | nextIndex=$((index + 1 )) 36 | unset args[$index] args[$nextIndex] 37 | args=( "${args[@]}" ) 38 | fi 39 | unset index 40 | done 41 | } 42 | chainName=$1 43 | shift 44 | args=("$@") 45 | cp -r /configs/. /root/ 46 | cp -r /chain_data/origin-geth/. /root/origin-geth/ 47 | cp -r /chain_data/1000/. /root/1000/ 48 | if [ "$chainName" = "origin" ]; then 49 | buildCommand "origin" 50 | remainingCommands="${args[@]}" 51 | /usr/local/bin/geth --datadir=./root/origin-geth --allow-insecure-unlock --unlock 0x40ebe1ce3428b9cbaddbc20af27f1c4780b7d49f,0x970cadd1c487c66b05ef108e506ae16ae471cf04,0xaf9327b0a8f1af06992ff7f24eca6541c5653b30,0xeca13364a7c4aba548bdfc20d27751869b85854a,0x206a3effd972aa17a609d7708cc2b4ed1f2ff8d5,0x21b5c4ac39a15c4a4a33606a848aa7d8b00298e1,0xbb84f0861325e77397ec42ade981cef2358ebf2c,0x9dc82f76b2985851ea5c73ea8368137cbc143c4a,0x6725a1becba2c74dda4ab86876527d53e36648b4,0x22365c6a6d377effe262896fa7b34d5d12e96f87,0xfe7147289537c23be75da959576d03f31a5e942f,0x6142ae2e46a9675cfb62e290dded0dae870ef538,0xcb91e14adda50c059e1f2bcf973f057984cf8414,0xa2bdfcb744a7032a2a3808f365fe12454a496442,0x26b207c59253fc9e1213e5a237ff189261ee5f26,0x8af0deae57623ee9e8cf25026092b6edba188267,0x39ac06efaef9a4f4b8fd8e2d8dc938166245f4c4,0x0db3406ed416725256ff1f171b86bb41cf67e920,0xef464575795f60a0a0752a7d5317f417a77f0e6b,0x76a313020034b955C4a030761B75E7021d13dF68 --password ./root/origin-geth/dev_pass --minerthreads 1 --mine $gethCommand --rpccorsdomain '*' --rpcvhosts '*' --networkid 1515 --wsorigins='*' $remainingCommands 52 | elif [ "$chainName" = "auxiliary" ]; then 53 | buildCommand "auxiliary" 54 | remainingCommands="${args[@]}" 55 | /usr/local/bin/geth --datadir=./root/1000 --allow-insecure-unlock --unlock 0xa5bcdd4ad7aaf2959ea164901086b38a01e43af5,0x41ac401c408456f50b5cd30e79acd01e7e3548cb,0x8dfdb7f00e13ab72bb34e440d16a7030be423709,0x9f668fc260c442cb41bf312ba48ae6571e7d08fe,0x0c08bd1f850b23543af9a3f3b1577adacadee86a,0x4e28a6906b454f5aa5faed5c6efeef34a939e1cc,0xa3d8a8511316094de9c0916278b3acc96c095996,0x21bbb3883843611b9b41964c34c5826861f2a7e5,0x707e5cbb8a3663a2d07d9cee558a42b42fb5bd39,0x93a9949dc8ca70c594f90ebaf7ba2a0047806336,0x4a6fd6499d55aa4bd29bdb56c08264632cce0261,0xa381b1bc7886fae876f167b1aa050af5123dc736,0xa530acfd8650021f2540cc594693d7bb48550e98,0x5df7341a956f17e75eb8708cfb46c0122a540353,0x275605cba18458f45c67a1a3b8899f481e79bb18,0x1318a9e299405c899bf6f47ba58c7db4fb558bc7,0x6e26f708182c75ae92e87e3d055d7aa4261b4029,0x94d36b54fc193aadf2ca813b7815f1a29e501994,0x6ea338ef7277d420ed4242c0bc1089a7a6151596,0x57e2ea1e74da1e6292b1367d337a0f83e594b6ac,0x52d242db7c37faba315df2faf928a7f63d1799c6 --password ./root/1000/dev_pass --etherbase 0xa5bcdd4ad7aaf2959ea164901086b38a01e43af5 --minerthreads 1 --mine $gethCommand --rpccorsdomain '*' --rpcvhosts '*' --networkid 1000 --wsorigins='*' $remainingCommands 56 | else 57 | echo "invalid input" 58 | fi 59 | -------------------------------------------------------------------------------- /src/Node/ChainInfo.ts: -------------------------------------------------------------------------------- 1 | import { DEV_CHAIN_ROOT } from '../bin/DevChainOptions'; 2 | 3 | export const GETH_CLIENT = 'geth'; 4 | export const PARITY_CLIENT = 'parity'; 5 | 6 | /** 7 | * Builds node based on the given chain id. 8 | */ 9 | export default class ChainInfo { 10 | /** 11 | * array of supported origin chains. 12 | */ 13 | public static get chainsSupportedByParity(): string[] { 14 | return [ 15 | 'ethereum', 16 | 'ropsten', 17 | 'goerli', 18 | ]; 19 | } 20 | 21 | /** 22 | * Mapping of supported origin chain name against chain id. 23 | */ 24 | public static get publicOriginChainNameToIdMap(): Record { 25 | return { 26 | ethereum: '1', 27 | ropsten: '3', 28 | goerli: '5', 29 | }; 30 | } 31 | 32 | /** 33 | * Mapping of origin dev chain name against chain id. 34 | */ 35 | public static get devOriginChainInfo(): any { 36 | return { 37 | 'dev-origin': '1515', // 1515 is the dev origin chain id 38 | }; 39 | } 40 | 41 | /** 42 | * Mapping of auxiliary dev chain name against chain id. 43 | */ 44 | public static get devAuxiliaryChainInfo(): any { 45 | return { 46 | 'dev-auxiliary': '1000', // 1000 is the dev auxiliary chain id 47 | }; 48 | } 49 | 50 | /** 51 | * Mapping of auxiliary dev chain name against chain id. 52 | */ 53 | public static get devChainInfo(): any { 54 | let devChains = {}; 55 | devChains = Object.assign(devChains, ChainInfo.devOriginChainInfo); 56 | devChains = Object.assign(devChains, ChainInfo.devAuxiliaryChainInfo); 57 | return devChains; 58 | } 59 | 60 | /** 61 | * Returns the chain id for the given chain name. If the chain name is not 62 | * available in `ChainInfo.publicOriginChainNameToIdMap`, then it will return chain as chain id. 63 | * @param chain Chain name or chain id. 64 | * @returns Chain id; based on the given input. 65 | */ 66 | public static getChainId(chain: string): string { 67 | // Check if the chain is dev chains. 68 | const chainId = ChainInfo.devChainInfo[chain]; 69 | if (chainId) { 70 | return chainId; 71 | } 72 | return ChainInfo.publicOriginChainNameToIdMap[chain] || chain; 73 | } 74 | 75 | /** 76 | * Check if the given chain is a dev chain. 77 | * @param chain Chain name. 78 | */ 79 | public static isDevChain(chain: string): boolean { 80 | const setOfChain = new Set( 81 | [...Object.keys(ChainInfo.devChainInfo), 82 | ...Object.values(ChainInfo.devChainInfo)], 83 | ); 84 | return setOfChain.has(chain); 85 | } 86 | 87 | /** 88 | * Check if the given chain is origin dev chain. 89 | * @param chain Chain name. 90 | */ 91 | public static isDevOriginChain(chain: string): boolean { 92 | return (ChainInfo.devOriginChainInfo[chain] !== undefined); 93 | } 94 | 95 | /** 96 | * Check if the given chain is auxiliary dev chain. 97 | * @param chain Chain name. 98 | */ 99 | public static isDevAuxiliaryChain(chain: string): boolean { 100 | return (ChainInfo.devAuxiliaryChainInfo[chain] !== undefined); 101 | } 102 | 103 | /** 104 | * For the ease of use the mosaic command accepts origin or auxiliary as 105 | * params to start the dev chains. This function identifies the params 106 | * provided to the mosaic command. If its dev chains then it returns the 107 | * proper params that are needed to start the dev chains. For other chains like goerli/1405 or 108 | * ropsten/1406 return chain without modifying. 109 | * 110 | * @param chain Chain provided in the mosaic command. 111 | * @param options Options provided in the mosaic command. 112 | */ 113 | public static getChainParams(chain: string, options = { origin: '' }) { 114 | let chainInput = chain; 115 | const optionInput = Object.assign({}, options); 116 | if (ChainInfo.isDevOriginChain(chain)) { 117 | chainInput = DEV_CHAIN_ROOT; 118 | } else if (ChainInfo.isDevAuxiliaryChain(chain)) { 119 | chainInput = ChainInfo.getChainId(chain); 120 | optionInput.origin = DEV_CHAIN_ROOT; 121 | } 122 | return { 123 | chain: chainInput, 124 | options: optionInput, 125 | }; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/bin/GraphOptions.ts: -------------------------------------------------------------------------------- 1 | import Integer from '../Integer'; 2 | import ChainInfo from '../Node/ChainInfo'; 3 | import GraphDescription from '../Graph/GraphDescription'; 4 | import * as fs from 'fs-extra'; 5 | import Logger from '../Logger'; 6 | 7 | // These defaults will be used if the relevant option is not given on the command line. 8 | const DEFAULT_RPC_PORT = 10000; 9 | const DEFAULT_WS_PORT = 60000; 10 | const DEFAULT_RPC_ADMIN_PORT = 8020; 11 | const DEFAULT_IPFS_PORT = 5001; 12 | const DEFAULT_POSTGRES_PORT = 5432; 13 | const DEFAULT_POSTGRES_USER = 'graph-node'; 14 | const DEFAULT_POSTGRES_PASSWORD = 'let-me-in'; 15 | const DEFAULT_POSTGRES_DATABASE = 'graph-node'; 16 | 17 | /** 18 | * Command line options for running an graph node. 19 | */ 20 | export default class GraphOptions { 21 | /** 22 | * @param options The options from the command line. 23 | */ 24 | constructor(options: { 25 | rpcPort: string; 26 | websocketPort: string; 27 | rpcAdminPort: string; 28 | ipfsPort: string; 29 | postgresPort: string; 30 | keepAfterStop: boolean; 31 | }) { 32 | Object.assign(this, options); 33 | } 34 | 35 | /** 36 | * Adds the common node options to a command. 37 | * @param command The command where to add the options. 38 | * @returns The command with the options added. 39 | */ 40 | public static addCliOptions(command): any { 41 | command 42 | .option('-R,--graph-rpc-port ', 'the RPC port to use for forwarding from host to container', Integer.parseString) 43 | .option('-W,--graph-ws-port ', 'the WS port to use for forwarding from host to container', Integer.parseString) 44 | .option('-A,--graph-rpc-admin-port ', 'the RPC port to use for forwarding Admin requests from host to container', Integer.parseString) 45 | .option('-I,--graph-ipfs-port ', 'the port to use for forwarding IPFS calls from host to container', Integer.parseString) 46 | .option('-U,--graph-postgres-user ', 'the user to use for Postgres') 47 | .option('-P,--graph-postgres-password-file ', 'path to the file containing password to use for Postgres') 48 | .option('-S,--graph-postgres-port ', 'the port to use for forwarding Postgres from host to container', Integer.parseString); 49 | 50 | return command; 51 | } 52 | 53 | /** 54 | * Parses the commander options and returns an Options object. 55 | * @param options Options as they are given by commander. 56 | * @param chain Chain identifier. 57 | * @returns The parsed options with defaults for options that are missing from the command line. 58 | */ 59 | public static parseOptions(options, chain): GraphDescription { 60 | const chainIdNumber = ChainInfo.getChainId(chain); 61 | const graphDescription: GraphDescription = new GraphDescription(chain); 62 | graphDescription.rpcPort = options.graphRpcPort || Integer.parseString(chainIdNumber) + DEFAULT_RPC_PORT; 63 | graphDescription.websocketPort = options.graphWsPort || Integer.parseString(chainIdNumber) + DEFAULT_WS_PORT; 64 | graphDescription.rpcAdminPort = options.graphRpcAdminPort || Integer.parseString(chainIdNumber) + DEFAULT_RPC_ADMIN_PORT; 65 | graphDescription.ipfsPort = options.graphIpfsPort || Integer.parseString(chainIdNumber) + DEFAULT_IPFS_PORT; 66 | graphDescription.postgresPort = options.graphPostgresPort || Integer.parseString(chainIdNumber) + DEFAULT_POSTGRES_PORT; 67 | graphDescription.postgresUser = options.graphPostgresUser || DEFAULT_POSTGRES_USER; 68 | if (options.graphPostgresPasswordFile) { 69 | if (!fs.pathExistsSync(options.graphPostgresPasswordFile)) { 70 | const message = `Postgres password file ${options.graphPostgresPasswordFile} does not exist`; 71 | Logger.error(message); 72 | throw new Error(message); 73 | } 74 | graphDescription.postgresPassword = `${fs.readFileSync( 75 | options.graphPostgresPasswordFile, 76 | { 77 | encoding: 'utf8', 78 | }, 79 | ).trim()}`; 80 | } else { 81 | graphDescription.postgresPassword = DEFAULT_POSTGRES_PASSWORD; 82 | } 83 | graphDescription.postgresDatabase = DEFAULT_POSTGRES_DATABASE; 84 | graphDescription.keepAfterStop = !!options.keep; 85 | graphDescription.originChain = options.origin; 86 | return graphDescription; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /abi/0.10/Anchor.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "organization", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": true, 18 | "inputs": [], 19 | "name": "coAnchor", 20 | "outputs": [ 21 | { 22 | "name": "", 23 | "type": "address" 24 | } 25 | ], 26 | "payable": false, 27 | "stateMutability": "view", 28 | "type": "function" 29 | }, 30 | { 31 | "inputs": [ 32 | { 33 | "name": "_remoteChainId", 34 | "type": "uint256" 35 | }, 36 | { 37 | "name": "_blockHeight", 38 | "type": "uint256" 39 | }, 40 | { 41 | "name": "_stateRoot", 42 | "type": "bytes32" 43 | }, 44 | { 45 | "name": "_maxStateRoots", 46 | "type": "uint256" 47 | }, 48 | { 49 | "name": "_organization", 50 | "type": "address" 51 | } 52 | ], 53 | "payable": false, 54 | "stateMutability": "nonpayable", 55 | "type": "constructor" 56 | }, 57 | { 58 | "anonymous": false, 59 | "inputs": [ 60 | { 61 | "indexed": false, 62 | "name": "_blockHeight", 63 | "type": "uint256" 64 | }, 65 | { 66 | "indexed": false, 67 | "name": "_stateRoot", 68 | "type": "bytes32" 69 | } 70 | ], 71 | "name": "StateRootAvailable", 72 | "type": "event" 73 | }, 74 | { 75 | "constant": false, 76 | "inputs": [ 77 | { 78 | "name": "_coAnchor", 79 | "type": "address" 80 | } 81 | ], 82 | "name": "setCoAnchorAddress", 83 | "outputs": [ 84 | { 85 | "name": "success_", 86 | "type": "bool" 87 | } 88 | ], 89 | "payable": false, 90 | "stateMutability": "nonpayable", 91 | "type": "function" 92 | }, 93 | { 94 | "constant": true, 95 | "inputs": [ 96 | { 97 | "name": "_blockHeight", 98 | "type": "uint256" 99 | } 100 | ], 101 | "name": "getStateRoot", 102 | "outputs": [ 103 | { 104 | "name": "stateRoot_", 105 | "type": "bytes32" 106 | } 107 | ], 108 | "payable": false, 109 | "stateMutability": "view", 110 | "type": "function" 111 | }, 112 | { 113 | "constant": true, 114 | "inputs": [], 115 | "name": "getLatestStateRootBlockHeight", 116 | "outputs": [ 117 | { 118 | "name": "height_", 119 | "type": "uint256" 120 | } 121 | ], 122 | "payable": false, 123 | "stateMutability": "view", 124 | "type": "function" 125 | }, 126 | { 127 | "constant": false, 128 | "inputs": [ 129 | { 130 | "name": "_blockHeight", 131 | "type": "uint256" 132 | }, 133 | { 134 | "name": "_stateRoot", 135 | "type": "bytes32" 136 | } 137 | ], 138 | "name": "anchorStateRoot", 139 | "outputs": [ 140 | { 141 | "name": "success_", 142 | "type": "bool" 143 | } 144 | ], 145 | "payable": false, 146 | "stateMutability": "nonpayable", 147 | "type": "function" 148 | }, 149 | { 150 | "constant": true, 151 | "inputs": [], 152 | "name": "getRemoteChainId", 153 | "outputs": [ 154 | { 155 | "name": "remoteChainId_", 156 | "type": "uint256" 157 | } 158 | ], 159 | "payable": false, 160 | "stateMutability": "view", 161 | "type": "function" 162 | } 163 | ] -------------------------------------------------------------------------------- /abi/0.12/Anchor.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "organization", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "address" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": true, 18 | "inputs": [], 19 | "name": "coAnchor", 20 | "outputs": [ 21 | { 22 | "name": "", 23 | "type": "address" 24 | } 25 | ], 26 | "payable": false, 27 | "stateMutability": "view", 28 | "type": "function" 29 | }, 30 | { 31 | "inputs": [ 32 | { 33 | "name": "_remoteChainId", 34 | "type": "uint256" 35 | }, 36 | { 37 | "name": "_blockHeight", 38 | "type": "uint256" 39 | }, 40 | { 41 | "name": "_stateRoot", 42 | "type": "bytes32" 43 | }, 44 | { 45 | "name": "_maxStateRoots", 46 | "type": "uint256" 47 | }, 48 | { 49 | "name": "_organization", 50 | "type": "address" 51 | } 52 | ], 53 | "payable": false, 54 | "stateMutability": "nonpayable", 55 | "type": "constructor" 56 | }, 57 | { 58 | "anonymous": false, 59 | "inputs": [ 60 | { 61 | "indexed": false, 62 | "name": "_blockHeight", 63 | "type": "uint256" 64 | }, 65 | { 66 | "indexed": false, 67 | "name": "_stateRoot", 68 | "type": "bytes32" 69 | } 70 | ], 71 | "name": "StateRootAvailable", 72 | "type": "event" 73 | }, 74 | { 75 | "constant": false, 76 | "inputs": [ 77 | { 78 | "name": "_coAnchor", 79 | "type": "address" 80 | } 81 | ], 82 | "name": "setCoAnchorAddress", 83 | "outputs": [ 84 | { 85 | "name": "success_", 86 | "type": "bool" 87 | } 88 | ], 89 | "payable": false, 90 | "stateMutability": "nonpayable", 91 | "type": "function" 92 | }, 93 | { 94 | "constant": true, 95 | "inputs": [ 96 | { 97 | "name": "_blockHeight", 98 | "type": "uint256" 99 | } 100 | ], 101 | "name": "getStateRoot", 102 | "outputs": [ 103 | { 104 | "name": "stateRoot_", 105 | "type": "bytes32" 106 | } 107 | ], 108 | "payable": false, 109 | "stateMutability": "view", 110 | "type": "function" 111 | }, 112 | { 113 | "constant": true, 114 | "inputs": [], 115 | "name": "getLatestStateRootBlockHeight", 116 | "outputs": [ 117 | { 118 | "name": "height_", 119 | "type": "uint256" 120 | } 121 | ], 122 | "payable": false, 123 | "stateMutability": "view", 124 | "type": "function" 125 | }, 126 | { 127 | "constant": false, 128 | "inputs": [ 129 | { 130 | "name": "_blockHeight", 131 | "type": "uint256" 132 | }, 133 | { 134 | "name": "_stateRoot", 135 | "type": "bytes32" 136 | } 137 | ], 138 | "name": "anchorStateRoot", 139 | "outputs": [ 140 | { 141 | "name": "success_", 142 | "type": "bool" 143 | } 144 | ], 145 | "payable": false, 146 | "stateMutability": "nonpayable", 147 | "type": "function" 148 | }, 149 | { 150 | "constant": true, 151 | "inputs": [], 152 | "name": "getRemoteChainId", 153 | "outputs": [ 154 | { 155 | "name": "remoteChainId_", 156 | "type": "uint256" 157 | } 158 | ], 159 | "payable": false, 160 | "stateMutability": "view", 161 | "type": "function" 162 | } 163 | ] -------------------------------------------------------------------------------- /src/Config/GatewayConfig.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "$ref": "#/definitions/GatewayConfig", 4 | "definitions": { 5 | "Address": { 6 | "type": "string", 7 | "pattern":"^0x[a-fA-F0-9]{40}$" 8 | }, 9 | "OriginChainContractAddresses": { 10 | "type": "object", 11 | "additionalProperties": false, 12 | "properties": { 13 | "baseTokenAddress": { 14 | "$ref": "#/definitions/Address", 15 | "description": "Address of base token contract on origin chain" 16 | }, 17 | "valueTokenAddress": { 18 | "$ref": "#/definitions/Address", 19 | "description": "Address of value token contract on origin chain" 20 | }, 21 | "gatewayOrganizationAddress": { 22 | "$ref": "#/definitions/Address", 23 | "description": "Address of ost gateway organization on origin chain" 24 | }, 25 | "eip20GatewayAddress": { 26 | "$ref": "#/definitions/Address", 27 | "description": "Address of gateway contract on origin chain" 28 | }, 29 | "merklePatriciaLibAddress": { 30 | "$ref": "#/definitions/Address", 31 | "description": "Address of merkle patricia library on origin chain" 32 | }, 33 | "gatewayLibAddress": { 34 | "$ref": "#/definitions/Address", 35 | "description": "Address of gateway library on origin chain" 36 | }, 37 | "messageBusAddress": { 38 | "$ref": "#/definitions/Address", 39 | "description": "Address of message bus library on origin chain" 40 | }, 41 | "stakePoolAddress": { 42 | "$ref": "#/definitions/Address", 43 | "description": "Address of staker pool contract on origin chain." 44 | } 45 | }, 46 | "required": [ 47 | "baseTokenAddress", 48 | "valueTokenAddress", 49 | "gatewayOrganizationAddress", 50 | "eip20GatewayAddress" 51 | ], 52 | "title": "OriginChainContractAddresses", 53 | "description": "Contract addresses on origin chain" 54 | }, 55 | "AuxiliaryChainContractAddresses": { 56 | "type": "object", 57 | "additionalProperties": false, 58 | "properties": { 59 | "coGatewayOrganizationAddress": { 60 | "$ref": "#/definitions/Address", 61 | "description": "Address of ost cogateway organization on auxiliary chain" 62 | }, 63 | "eip20CoGatewayAddress": { 64 | "$ref": "#/definitions/Address", 65 | "description": "Address of cogateway contract on auxiliary chain" 66 | }, 67 | "utilityTokenAddress": { 68 | "$ref": "#/definitions/Address", 69 | "description": "Utility token contract address" 70 | }, 71 | "merklePatriciaLibAddress": { 72 | "$ref": "#/definitions/Address", 73 | "description": "Address of merkle patricia library on auxiliary chain" 74 | }, 75 | "gatewayLibAddress": { 76 | "$ref": "#/definitions/Address", 77 | "description": "Address of gateway library on auxiliary chain" 78 | }, 79 | "messageBusAddress": { 80 | "$ref": "#/definitions/Address", 81 | "description": "Address of message bus library on auxiliary chain" 82 | }, 83 | "redeemPoolAddress": { 84 | "$ref": "#/definitions/Address", 85 | "description": "Address of redeem pool contract on auxiliary chain" 86 | } 87 | }, 88 | "required": [ 89 | "coGatewayOrganizationAddress", 90 | "eip20CoGatewayAddress", 91 | "utilityTokenAddress" 92 | ], 93 | "title": "Auxiliary", 94 | "description": "Contract addresses of auxiliary chain" 95 | }, 96 | "GatewayConfig": { 97 | "type": "object", 98 | "additionalProperties": false, 99 | "properties": { 100 | "mosaicConfigFilePath": { 101 | "type": "string", 102 | "description": "File path of mosaic config." 103 | }, 104 | "auxChainId": { 105 | "type": "integer", 106 | "description": "auxiliary chain id as integer" 107 | }, 108 | "originContracts": { 109 | "$ref": "#/definitions/OriginChainContractAddresses", 110 | "description": "Origin chain contract addresses." 111 | }, 112 | "auxiliaryContracts": { 113 | "$ref": "#/definitions/AuxiliaryChainContractAddresses", 114 | "description": "Auxiliary chain contract addresses." 115 | } 116 | }, 117 | "required": [ 118 | "mosaicConfigFilePath", 119 | "auxChainId", 120 | "originContracts", 121 | "auxiliaryContracts" 122 | ], 123 | "title": "GatewayConfig", 124 | "description": "This object represents GatewayConfig of single gateway pair." 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Config/InitConfig.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import Logger from '../Logger'; 4 | import Directory from '../Directory'; 5 | 6 | /** 7 | * An InitConfig is used when initializing a new auxiliary chain. 8 | */ 9 | export default class InitConfig { 10 | /** The chain ID of the auxiliary chain that is to be created. */ 11 | readonly auxiliaryChainId: string = ''; 12 | 13 | /** Required to know the sender account when sending transactions to the origin node. */ 14 | readonly originTxOptions: { from: string; gasPrice: number }; 15 | 16 | /** Bounty to set on the gateway on origin. */ 17 | readonly originBounty: string = ''; 18 | 19 | /** Bounty to set on the co-gateway on auxiliary. */ 20 | readonly auxiliaryBounty: string = ''; 21 | 22 | /** For the initial stake, the gas price to set. */ 23 | readonly originStakeGasPrice: string = ''; 24 | 25 | /** For the initial stake, the gas limit to set. */ 26 | readonly originStakeGasLimit: string = ''; 27 | 28 | /** The amount of OST to stake to initialize the auxiliary chain with, in Wei. */ 29 | readonly originStakeAmount: string = ''; 30 | 31 | /** 32 | * For the initial stake, wait this number of blocks after staking before reading the state root 33 | * from origin. 34 | */ 35 | readonly originStakeBlocksToWait: number = 0; 36 | 37 | /** The address of the OST EIP20 token on origin. */ 38 | readonly originOstAddress: string = ''; 39 | 40 | /** Where to send burned tokens on origin. */ 41 | readonly originBurnerAddress: string = ''; 42 | 43 | /** Where to send burned tokens on auxiliary. */ 44 | readonly auxiliaryBurnerAddress: string = ''; 45 | 46 | /** How many state roots to store in the anchor ring buffer on origin. */ 47 | readonly originAnchorBufferSize: string = ''; 48 | 49 | /** How many state roots to store in the anchor ring buffer on auxiliary. */ 50 | readonly auxiliaryAnchorBufferSize: string = ''; 51 | 52 | /** 53 | * The owners and admins of the organizations. 54 | * 55 | * IMPORTANT (applies to all organizations' owners and admins): 56 | * Note that the origin and auxiliary gateway and co-gateway organization admins cannot be 57 | * set when creating the chain. They will be set to the addresses of the accounts that deploy 58 | * the contracts, as it is required to activate the gateway and set the co-gateway on OST prime. 59 | */ 60 | readonly originAnchorOrganizationOwner: string = ''; 61 | 62 | readonly originAnchorOrganizationAdmin: string = ''; 63 | 64 | readonly auxiliaryAnchorOrganizationOwner: string = ''; 65 | 66 | readonly auxiliaryAnchorOrganizationAdmin: string = ''; 67 | 68 | readonly originGatewayOrganizationOwner: string = ''; 69 | 70 | readonly auxiliaryCoGatewayAndOstPrimeOrganizationOwner: string = ''; 71 | 72 | /** 73 | * @param initialValues All properties of the initial values will be assigned to this class. 74 | * @throws If the configuration based on the initial values is invalid. 75 | */ 76 | constructor(initialValues: any) { 77 | Object.assign(this, initialValues); 78 | 79 | if (!this.isValid()) { 80 | throw new Error('no valid configuration found'); 81 | } 82 | } 83 | 84 | /** 85 | * Create a new InitConfig from the json in the `initialize` directory. 86 | * The name of the file must match the chain id. 87 | * @param auxiliaryChainId The chain id of the auxiliary chain 88 | * @returns The initialized InitConfig. 89 | */ 90 | public static createFromFile(auxiliaryChainId: string): InitConfig { 91 | const fileValues = JSON.parse( 92 | fs.readFileSync( 93 | path.join( 94 | Directory.projectRoot, 95 | 'initialize', 96 | `${auxiliaryChainId}.json`, 97 | ), 98 | { encoding: 'utf8' }, 99 | ), 100 | ); 101 | 102 | fileValues.auxiliaryChainId = auxiliaryChainId; 103 | 104 | return new InitConfig(fileValues); 105 | } 106 | 107 | /** 108 | * Checks whether the provided InitConfig is complete. 109 | * @returns True if all values are set, false otherwise. 110 | */ 111 | private isValid(): boolean { 112 | // Checking for every member that it is not the (initial) empty string if it is a string 113 | // and that it is not the (initial) zero value if it is a number. 114 | for (const member in this) { 115 | if (this.hasOwnProperty(member)) { 116 | const value = this[member]; 117 | 118 | if (typeof value === 'string' && value === '') { 119 | Logger.error('invalid initial config', { missingKey: member }); 120 | 121 | return false; 122 | } 123 | 124 | if (typeof value === 'number' && value === 0) { 125 | Logger.error( 126 | 'invalid initial config; value cannot be zero or missing', 127 | { missingKey: member }, 128 | ); 129 | 130 | return false; 131 | } 132 | } 133 | } 134 | 135 | return true; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tests/Config/fromChain.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import * as sinon from 'sinon'; 3 | import { assert } from 'chai'; 4 | import GatewayConfig from '../../src/Config/GatewayConfig'; 5 | import Directory from '../../src/Directory'; 6 | import SpyAssert from '../test_utils/SpyAssert'; 7 | 8 | describe('GatewayConfig.fromChain()', () => { 9 | const filePath = './tests/Config/testdata/0xae02c7b1c324a8d94a564bc8d713df89eae441fe.json'; 10 | const originChain = 'dev'; 11 | const auxChainId = 1000; 12 | const originBaseTokenAddress = '0x9ac77f4c0ca4d0f2142d7a77175cf4f1295fb2d8'; 13 | const valueTokenAddress = '0x8e183Fd2cd55C7C05bBf4FAC989740f69e559A6d'; 14 | const gatewayOrganizationAddress = '0x3f99f42d226A0CD1C1Fcae1e8dC11b2f7a9DcE4B'; 15 | const eip20GatewayAddress = '0xaE02C7b1C324A8D94A564bC8d713Df89eae441fe'; 16 | const coGatewayOrganizationAddress = '0x2D586C7E220839a9284888B10aDF4823AcD6EdF3'; 17 | const utilityTokenAddress = '0x62F8729C1C282C231a22252e90CE9735533D2518'; 18 | const eip20CoGatewayAddress = '0xc6fF898ceBf631eFb58eEc7187E4c1f70AE8d943'; 19 | 20 | it('Should return TokenConfig object', async () => { 21 | const getGatewayConfigPathSpy = sinon.replace( 22 | Directory, 23 | 'getGatewayConfigPath', 24 | sinon.fake.returns(filePath), 25 | ); 26 | 27 | const gatewayConfig = GatewayConfig.fromChain(originChain, auxChainId, eip20GatewayAddress); 28 | 29 | 30 | SpyAssert.assert( 31 | getGatewayConfigPathSpy, 32 | 1, 33 | [[originChain, auxChainId, eip20GatewayAddress]], 34 | ); 35 | 36 | assert.strictEqual( 37 | gatewayConfig.auxChainId, 38 | auxChainId, 39 | 'Expected GatewayConfig auxChainId is not equal to actual auxChainId', 40 | ); 41 | 42 | assert.strictEqual( 43 | gatewayConfig.originContracts.baseTokenAddress, 44 | originBaseTokenAddress, 45 | 'Expected GatewayConfig origin baseTokenAddress is ' 46 | + 'not equal to actual baseTokenAddress', 47 | ); 48 | assert.strictEqual( 49 | gatewayConfig.originContracts.eip20GatewayAddress, 50 | eip20GatewayAddress, 51 | 'Expected GatewayConfig origin eip20GatewayAddress ' 52 | + 'is not equal to actual eip20GatewayAddress', 53 | ); 54 | assert.strictEqual( 55 | gatewayConfig.originContracts.gatewayOrganizationAddress, 56 | gatewayOrganizationAddress, 57 | 'Expected GatewayConfig origin gatewayOrganizationAddress ' 58 | + 'is not equal to actual gatewayOrganizationAddress', 59 | ); 60 | assert.strictEqual( 61 | gatewayConfig.originContracts.valueTokenAddress, 62 | valueTokenAddress, 63 | 'Expected GatewayConfig origin valueTokenAddress ' 64 | + 'is not equal to actual valueTokenAddress', 65 | ); 66 | assert.strictEqual( 67 | gatewayConfig.auxiliaryContracts.utilityTokenAddress, 68 | utilityTokenAddress, 69 | 'Expected GatewayConfig auxiliary utilityTokenAddress ' 70 | + 'is not equal to actual utilityTokenAddress', 71 | ); 72 | assert.strictEqual( 73 | gatewayConfig.auxiliaryContracts.eip20CoGatewayAddress, 74 | eip20CoGatewayAddress, 75 | 'Expected GatewayConfig eip20CoGatewayAddress ' 76 | + 'is not equal to actual eip20CoGatewayAddress', 77 | ); 78 | assert.strictEqual( 79 | gatewayConfig.auxiliaryContracts.coGatewayOrganizationAddress, 80 | coGatewayOrganizationAddress, 81 | 'Expected GatewayConfig auxiliary coGatewayOrganizationAddress ' 82 | + 'is not equal to actual coGatewayOrganizationAddress', 83 | ); 84 | sinon.restore(); 85 | }); 86 | 87 | it('Should fail when origin chain is incorrect', async () => { 88 | const expectedFilePath = Directory.getGatewayConfigPath( 89 | 'wrongChain', 90 | auxChainId, 91 | eip20GatewayAddress, 92 | ); 93 | assert.throws( 94 | () => GatewayConfig.fromChain('wrongChain', auxChainId, eip20GatewayAddress), 95 | `Missing GatewayConfig file at path: ${expectedFilePath}`, 96 | ); 97 | sinon.restore(); 98 | }); 99 | 100 | it('Should fail when aux chain id is incorrect', async () => { 101 | const expectedFilePath = Directory.getGatewayConfigPath( 102 | originChain, 103 | 0, 104 | eip20GatewayAddress, 105 | ); 106 | assert.throws( 107 | () => GatewayConfig.fromChain(originChain, 0, eip20GatewayAddress), 108 | `Missing GatewayConfig file at path: ${expectedFilePath}`, 109 | ); 110 | sinon.restore(); 111 | }); 112 | 113 | it('Should fail when gateway address is incorrect', async () => { 114 | const expectedFilePath = Directory.getGatewayConfigPath( 115 | originChain, 116 | auxChainId, 117 | '0x19F64B29789F02FFcCE2c37DFB3d65FEaDdea66a', 118 | ); 119 | assert.throws( 120 | () => GatewayConfig.fromChain(originChain, auxChainId, '0x19F64B29789F02FFcCE2c37DFB3d65FEaDdea66a'), 121 | `Missing GatewayConfig file at path: ${expectedFilePath}`, 122 | ); 123 | sinon.restore(); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /graph/origin/generated/Contract/Anchor.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | 3 | import { 4 | EthereumCall, 5 | EthereumEvent, 6 | SmartContract, 7 | EthereumValue, 8 | JSONValue, 9 | TypedMap, 10 | Entity, 11 | EthereumTuple, 12 | Bytes, 13 | Address, 14 | BigInt 15 | } from "@graphprotocol/graph-ts"; 16 | 17 | export class StateRootAvailable extends EthereumEvent { 18 | get params(): StateRootAvailable__Params { 19 | return new StateRootAvailable__Params(this); 20 | } 21 | } 22 | 23 | export class StateRootAvailable__Params { 24 | _event: StateRootAvailable; 25 | 26 | constructor(event: StateRootAvailable) { 27 | this._event = event; 28 | } 29 | 30 | get _blockHeight(): BigInt { 31 | return this._event.parameters[0].value.toBigInt(); 32 | } 33 | 34 | get _stateRoot(): Bytes { 35 | return this._event.parameters[1].value.toBytes(); 36 | } 37 | } 38 | 39 | export class Contract extends SmartContract { 40 | static bind(address: Address): Contract { 41 | return new Contract("Contract", address); 42 | } 43 | 44 | organization(): Address { 45 | let result = super.call("organization", []); 46 | return result[0].toAddress(); 47 | } 48 | 49 | coAnchor(): Address { 50 | let result = super.call("coAnchor", []); 51 | return result[0].toAddress(); 52 | } 53 | 54 | getStateRoot(_blockHeight: BigInt): Bytes { 55 | let result = super.call("getStateRoot", [ 56 | EthereumValue.fromUnsignedBigInt(_blockHeight) 57 | ]); 58 | return result[0].toBytes(); 59 | } 60 | 61 | getLatestStateRootBlockHeight(): BigInt { 62 | let result = super.call("getLatestStateRootBlockHeight", []); 63 | return result[0].toBigInt(); 64 | } 65 | 66 | getRemoteChainId(): BigInt { 67 | let result = super.call("getRemoteChainId", []); 68 | return result[0].toBigInt(); 69 | } 70 | } 71 | 72 | export class ConstructorCall extends EthereumCall { 73 | get inputs(): ConstructorCall__Inputs { 74 | return new ConstructorCall__Inputs(this); 75 | } 76 | 77 | get outputs(): ConstructorCall__Outputs { 78 | return new ConstructorCall__Outputs(this); 79 | } 80 | } 81 | 82 | export class ConstructorCall__Inputs { 83 | _call: ConstructorCall; 84 | 85 | constructor(call: ConstructorCall) { 86 | this._call = call; 87 | } 88 | 89 | get _remoteChainId(): BigInt { 90 | return this._call.inputValues[0].value.toBigInt(); 91 | } 92 | 93 | get _blockHeight(): BigInt { 94 | return this._call.inputValues[1].value.toBigInt(); 95 | } 96 | 97 | get _stateRoot(): Bytes { 98 | return this._call.inputValues[2].value.toBytes(); 99 | } 100 | 101 | get _maxStateRoots(): BigInt { 102 | return this._call.inputValues[3].value.toBigInt(); 103 | } 104 | 105 | get _organization(): Address { 106 | return this._call.inputValues[4].value.toAddress(); 107 | } 108 | } 109 | 110 | export class ConstructorCall__Outputs { 111 | _call: ConstructorCall; 112 | 113 | constructor(call: ConstructorCall) { 114 | this._call = call; 115 | } 116 | } 117 | 118 | export class SetCoAnchorAddressCall extends EthereumCall { 119 | get inputs(): SetCoAnchorAddressCall__Inputs { 120 | return new SetCoAnchorAddressCall__Inputs(this); 121 | } 122 | 123 | get outputs(): SetCoAnchorAddressCall__Outputs { 124 | return new SetCoAnchorAddressCall__Outputs(this); 125 | } 126 | } 127 | 128 | export class SetCoAnchorAddressCall__Inputs { 129 | _call: SetCoAnchorAddressCall; 130 | 131 | constructor(call: SetCoAnchorAddressCall) { 132 | this._call = call; 133 | } 134 | 135 | get _coAnchor(): Address { 136 | return this._call.inputValues[0].value.toAddress(); 137 | } 138 | } 139 | 140 | export class SetCoAnchorAddressCall__Outputs { 141 | _call: SetCoAnchorAddressCall; 142 | 143 | constructor(call: SetCoAnchorAddressCall) { 144 | this._call = call; 145 | } 146 | 147 | get success_(): boolean { 148 | return this._call.outputValues[0].value.toBoolean(); 149 | } 150 | } 151 | 152 | export class AnchorStateRootCall extends EthereumCall { 153 | get inputs(): AnchorStateRootCall__Inputs { 154 | return new AnchorStateRootCall__Inputs(this); 155 | } 156 | 157 | get outputs(): AnchorStateRootCall__Outputs { 158 | return new AnchorStateRootCall__Outputs(this); 159 | } 160 | } 161 | 162 | export class AnchorStateRootCall__Inputs { 163 | _call: AnchorStateRootCall; 164 | 165 | constructor(call: AnchorStateRootCall) { 166 | this._call = call; 167 | } 168 | 169 | get _blockHeight(): BigInt { 170 | return this._call.inputValues[0].value.toBigInt(); 171 | } 172 | 173 | get _stateRoot(): Bytes { 174 | return this._call.inputValues[1].value.toBytes(); 175 | } 176 | } 177 | 178 | export class AnchorStateRootCall__Outputs { 179 | _call: AnchorStateRootCall; 180 | 181 | constructor(call: AnchorStateRootCall) { 182 | this._call = call; 183 | } 184 | 185 | get success_(): boolean { 186 | return this._call.outputValues[0].value.toBoolean(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /graph/auxiliary/generated/Contract/Anchor.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | 3 | import { 4 | EthereumCall, 5 | EthereumEvent, 6 | SmartContract, 7 | EthereumValue, 8 | JSONValue, 9 | TypedMap, 10 | Entity, 11 | EthereumTuple, 12 | Bytes, 13 | Address, 14 | BigInt 15 | } from "@graphprotocol/graph-ts"; 16 | 17 | export class StateRootAvailable extends EthereumEvent { 18 | get params(): StateRootAvailable__Params { 19 | return new StateRootAvailable__Params(this); 20 | } 21 | } 22 | 23 | export class StateRootAvailable__Params { 24 | _event: StateRootAvailable; 25 | 26 | constructor(event: StateRootAvailable) { 27 | this._event = event; 28 | } 29 | 30 | get _blockHeight(): BigInt { 31 | return this._event.parameters[0].value.toBigInt(); 32 | } 33 | 34 | get _stateRoot(): Bytes { 35 | return this._event.parameters[1].value.toBytes(); 36 | } 37 | } 38 | 39 | export class Contract extends SmartContract { 40 | static bind(address: Address): Contract { 41 | return new Contract("Contract", address); 42 | } 43 | 44 | organization(): Address { 45 | let result = super.call("organization", []); 46 | return result[0].toAddress(); 47 | } 48 | 49 | coAnchor(): Address { 50 | let result = super.call("coAnchor", []); 51 | return result[0].toAddress(); 52 | } 53 | 54 | getStateRoot(_blockHeight: BigInt): Bytes { 55 | let result = super.call("getStateRoot", [ 56 | EthereumValue.fromUnsignedBigInt(_blockHeight) 57 | ]); 58 | return result[0].toBytes(); 59 | } 60 | 61 | getLatestStateRootBlockHeight(): BigInt { 62 | let result = super.call("getLatestStateRootBlockHeight", []); 63 | return result[0].toBigInt(); 64 | } 65 | 66 | getRemoteChainId(): BigInt { 67 | let result = super.call("getRemoteChainId", []); 68 | return result[0].toBigInt(); 69 | } 70 | } 71 | 72 | export class ConstructorCall extends EthereumCall { 73 | get inputs(): ConstructorCall__Inputs { 74 | return new ConstructorCall__Inputs(this); 75 | } 76 | 77 | get outputs(): ConstructorCall__Outputs { 78 | return new ConstructorCall__Outputs(this); 79 | } 80 | } 81 | 82 | export class ConstructorCall__Inputs { 83 | _call: ConstructorCall; 84 | 85 | constructor(call: ConstructorCall) { 86 | this._call = call; 87 | } 88 | 89 | get _remoteChainId(): BigInt { 90 | return this._call.inputValues[0].value.toBigInt(); 91 | } 92 | 93 | get _blockHeight(): BigInt { 94 | return this._call.inputValues[1].value.toBigInt(); 95 | } 96 | 97 | get _stateRoot(): Bytes { 98 | return this._call.inputValues[2].value.toBytes(); 99 | } 100 | 101 | get _maxStateRoots(): BigInt { 102 | return this._call.inputValues[3].value.toBigInt(); 103 | } 104 | 105 | get _organization(): Address { 106 | return this._call.inputValues[4].value.toAddress(); 107 | } 108 | } 109 | 110 | export class ConstructorCall__Outputs { 111 | _call: ConstructorCall; 112 | 113 | constructor(call: ConstructorCall) { 114 | this._call = call; 115 | } 116 | } 117 | 118 | export class SetCoAnchorAddressCall extends EthereumCall { 119 | get inputs(): SetCoAnchorAddressCall__Inputs { 120 | return new SetCoAnchorAddressCall__Inputs(this); 121 | } 122 | 123 | get outputs(): SetCoAnchorAddressCall__Outputs { 124 | return new SetCoAnchorAddressCall__Outputs(this); 125 | } 126 | } 127 | 128 | export class SetCoAnchorAddressCall__Inputs { 129 | _call: SetCoAnchorAddressCall; 130 | 131 | constructor(call: SetCoAnchorAddressCall) { 132 | this._call = call; 133 | } 134 | 135 | get _coAnchor(): Address { 136 | return this._call.inputValues[0].value.toAddress(); 137 | } 138 | } 139 | 140 | export class SetCoAnchorAddressCall__Outputs { 141 | _call: SetCoAnchorAddressCall; 142 | 143 | constructor(call: SetCoAnchorAddressCall) { 144 | this._call = call; 145 | } 146 | 147 | get success_(): boolean { 148 | return this._call.outputValues[0].value.toBoolean(); 149 | } 150 | } 151 | 152 | export class AnchorStateRootCall extends EthereumCall { 153 | get inputs(): AnchorStateRootCall__Inputs { 154 | return new AnchorStateRootCall__Inputs(this); 155 | } 156 | 157 | get outputs(): AnchorStateRootCall__Outputs { 158 | return new AnchorStateRootCall__Outputs(this); 159 | } 160 | } 161 | 162 | export class AnchorStateRootCall__Inputs { 163 | _call: AnchorStateRootCall; 164 | 165 | constructor(call: AnchorStateRootCall) { 166 | this._call = call; 167 | } 168 | 169 | get _blockHeight(): BigInt { 170 | return this._call.inputValues[0].value.toBigInt(); 171 | } 172 | 173 | get _stateRoot(): Bytes { 174 | return this._call.inputValues[1].value.toBytes(); 175 | } 176 | } 177 | 178 | export class AnchorStateRootCall__Outputs { 179 | _call: AnchorStateRootCall; 180 | 181 | constructor(call: AnchorStateRootCall) { 182 | this._call = call; 183 | } 184 | 185 | get success_(): boolean { 186 | return this._call.outputValues[0].value.toBoolean(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Config/GatewayConfig.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import { Validator } from 'jsonschema'; 3 | import MosaicConfig, { Address } from './MosaicConfig'; 4 | import { 5 | InvalidGatewayConfigException, 6 | GatewayConfigNotFoundException, 7 | } from '../Exception'; 8 | import FileSystem from '../FileSystem '; 9 | import Directory from '../Directory'; 10 | import Logger from '../Logger'; 11 | 12 | /* eslint-disable @typescript-eslint/no-var-requires */ 13 | const schema = require('./GatewayConfig.schema.json'); 14 | 15 | /** 16 | * Contract addresses of the origin chain specific to a gateway pair. 17 | */ 18 | export class OriginContracts { 19 | public baseTokenAddress: Address; 20 | 21 | public valueTokenAddress: Address; 22 | 23 | public gatewayOrganizationAddress: Address; 24 | 25 | public eip20GatewayAddress: Address; 26 | 27 | public gatewayLibAddress?: Address; 28 | 29 | public messageBusAddress?: Address; 30 | 31 | public merklePatriciaLibAddress?: Address; 32 | 33 | public stakePoolAddress?: Address; 34 | } 35 | 36 | /** 37 | * Contract addresses deployed on the auxiliary chain specific to a gateway pair. 38 | */ 39 | export class AuxiliaryContracts { 40 | public coGatewayOrganizationAddress: Address; 41 | 42 | public utilityTokenAddress: Address; 43 | 44 | public eip20CoGatewayAddress: Address; 45 | 46 | public gatewayLibAddress?: Address; 47 | 48 | public messageBusAddress?: Address; 49 | 50 | public merklePatriciaLibAddress?: Address; 51 | 52 | public redeemPoolAddress?: Address; 53 | } 54 | 55 | /** 56 | * Holds the config of a deployed gateway pair. 57 | */ 58 | export default class GatewayConfig { 59 | public mosaicConfig: MosaicConfig; 60 | 61 | public auxChainId: number; 62 | 63 | public originContracts: OriginContracts; 64 | 65 | public auxiliaryContracts: AuxiliaryContracts; 66 | 67 | private constructor(config: any) { 68 | this.mosaicConfig = MosaicConfig.fromFile(config.mosaicConfigFilePath); 69 | this.auxChainId = config.auxChainId; 70 | this.originContracts = config.originContracts; 71 | this.auxiliaryContracts = config.auxiliaryContracts; 72 | } 73 | 74 | /** 75 | * @param filePath GatewayConfig absolute path. 76 | * 77 | * @return GatewayConfig object. 78 | */ 79 | public static fromFile(filePath: string): GatewayConfig { 80 | if (GatewayConfig.exists(filePath)) { 81 | const configObject = GatewayConfig.readConfigFromFile(filePath); 82 | return new GatewayConfig(configObject); 83 | } 84 | throw new GatewayConfigNotFoundException(`Missing GatewayConfig file at path: ${filePath}`); 85 | } 86 | 87 | /** 88 | * Construct GatewayConfig object 89 | * 90 | * @param originChain Origin chain identifier. 91 | * @param auxChainId Auxiliary chain Id. 92 | * @param gatewayAddress Address of Gateway. 93 | * 94 | * @return gateway config 95 | */ 96 | public static fromChain(originChain: string, auxChainId: number, gatewayAddress: string): 97 | GatewayConfig { 98 | const filePath = Directory.getGatewayConfigPath(originChain, auxChainId, gatewayAddress); 99 | Logger.info(`filepath for gateway config ${filePath}`); 100 | if (GatewayConfig.exists(filePath)) { 101 | const configObject = GatewayConfig.readConfigFromFile(filePath); 102 | return new GatewayConfig(configObject); 103 | } 104 | throw new GatewayConfigNotFoundException(`Missing GatewayConfig file at path: ${filePath}`); 105 | } 106 | 107 | /** 108 | * Read config from file, validate it and return as JSON object. 109 | * 110 | * @param filePath GatewayConfig absolute path. 111 | * 112 | * @return Json parsed object. 113 | */ 114 | private static readConfigFromFile(filePath: string): object { 115 | const configString = fs.readFileSync(filePath).toString(); 116 | if (configString && configString.length > 0) { 117 | const configObject = JSON.parse(configString); 118 | GatewayConfig.validateSchema(configObject); 119 | configObject.mosaicConfigFilePath = FileSystem.resolveHomePath( 120 | configObject.mosaicConfigFilePath, 121 | ); 122 | return configObject; 123 | } 124 | throw new InvalidGatewayConfigException(`blank config file found at: ${filePath}`); 125 | } 126 | 127 | /** 128 | * This method validate json object against GatewayConfig schema. 129 | * Also throws an exception on failure. 130 | * 131 | * @param jsonObject JSON object to be validated against schema. 132 | */ 133 | private static validateSchema(jsonObject: any): void { 134 | const validator = new Validator(); 135 | try { 136 | validator.validate(jsonObject, schema, { throwError: true }); 137 | } catch (error) { 138 | throw new InvalidGatewayConfigException(error.message); 139 | } 140 | } 141 | 142 | /** 143 | * Checks if GatewayConfig path exists or not. 144 | * 145 | * @param filePath GatewayConfig file path. 146 | * 147 | * @return True if file exists. 148 | */ 149 | private static exists(filePath: string): boolean { 150 | return fs.existsSync(filePath); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/Node/Node.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import * as path from 'path'; 3 | import Logger from '../Logger'; 4 | import Shell from '../Shell'; 5 | import NodeDescription from './NodeDescription'; 6 | import PublishMosaicConfig from '../Config/PublishMosaicConfig'; 7 | 8 | /** 9 | * Represents a chain that is managed by docker. 10 | */ 11 | export default abstract class Node { 12 | /** The chain identifier identifies the chain this node should run. For example ropsten or 200. */ 13 | protected chain: string; 14 | 15 | /** The base directory of the mosaic chains that will hold the chains' data in a subdirectory. */ 16 | protected mosaicDir: string; 17 | 18 | /** The directory where the chain data is store that this node will use. */ 19 | protected chainDir: string; 20 | 21 | /** Docker will publish this port on the host. */ 22 | protected port: number; 23 | 24 | /** Docker will publish this RPC port on the host. */ 25 | protected rpcPort: number; 26 | 27 | /** Docker will publish this websocket port on the host. */ 28 | protected websocketPort: number; 29 | 30 | /** The name of this docker container. */ 31 | protected containerName: string; 32 | 33 | /** If set to true, the container is not deleted when stopped. */ 34 | protected keepAfterStop: boolean; 35 | 36 | /** A comma separated list of addresses that get unlocked while the process is running. */ 37 | protected unlock: string; 38 | 39 | /** The path to the password file to unlock the accounts given in unlock. */ 40 | protected password: string; 41 | 42 | /** 43 | * Identifier for origin chain. 44 | * This needs to be passed if auxiliary chain needs to be started. 45 | */ 46 | public originChain: string; 47 | 48 | /** 49 | * Docker container names will have this prefix. 50 | * @returns The prefix. 51 | */ 52 | public static get prefix(): string { 53 | return 'mosaic_'; 54 | } 55 | 56 | /** 57 | * Docker containers will spawn in this docker network. 58 | * @returns The network name. 59 | */ 60 | public static get network(): string { 61 | return 'mosaic'; 62 | } 63 | 64 | public constructor(nodeDescription: NodeDescription) { 65 | this.chain = nodeDescription.chain; 66 | this.mosaicDir = nodeDescription.mosaicDir; 67 | this.port = nodeDescription.port; 68 | this.rpcPort = nodeDescription.rpcPort; 69 | this.websocketPort = nodeDescription.websocketPort; 70 | this.keepAfterStop = nodeDescription.keepAfterStop; 71 | this.unlock = nodeDescription.unlock; 72 | this.password = nodeDescription.password; 73 | this.originChain = nodeDescription.originChain; 74 | 75 | if (this.originChain === '') { 76 | this.chainDir = path.join(this.mosaicDir, this.chain, `origin-${nodeDescription.client}`); 77 | } else { 78 | this.chainDir = path.join(this.mosaicDir, this.originChain, this.chain); 79 | } 80 | this.containerName = `${Node.prefix}${this.chain}`; 81 | } 82 | 83 | public getChain(): string { 84 | return this.chain; 85 | } 86 | 87 | public getMosaicDir(): string { 88 | return this.mosaicDir; 89 | } 90 | 91 | public getChainDir(): string { 92 | return this.chainDir; 93 | } 94 | 95 | public getPort(): number { 96 | return this.port; 97 | } 98 | 99 | public getRpcPort(): number { 100 | return this.rpcPort; 101 | } 102 | 103 | public getWebsocketPort(): number { 104 | return this.websocketPort; 105 | } 106 | 107 | public getContainerName(): string { 108 | return this.containerName; 109 | } 110 | 111 | public isKeptAfterStop(): boolean { 112 | return this.keepAfterStop; 113 | } 114 | 115 | /** 116 | * Create a docker network if network doesn't exists. 117 | */ 118 | public ensureNetworkExists(): void { 119 | // `-w` in grep is used to match the exact string. 120 | // Command for creating network only if network doesn't exists. 121 | const createNetwork = `docker network ls | grep -w ${Node.network} || docker network create ${Node.network}`; 122 | Shell.executeInShell(createNetwork); 123 | } 124 | 125 | /** 126 | * Starts the docker container that runs this chain. 127 | */ 128 | public abstract start(): void; 129 | 130 | /** 131 | * Stops the docker container that runs this chain. 132 | */ 133 | public stop(): void { 134 | this.logInfo('attempting to stop chain container'); 135 | const args = [ 136 | 'stop', 137 | this.containerName, 138 | ]; 139 | 140 | Shell.executeDockerCommand(args); 141 | } 142 | 143 | /** 144 | * 1. Creates the mosaic data directory if it does not exist. 145 | * 2. Publishes mosaic configs for existing chains 146 | */ 147 | protected initializeDataDir(): void { 148 | if (!fs.existsSync(this.mosaicDir)) { 149 | this.logInfo(`${this.mosaicDir} does not exist; initializing`); 150 | fs.mkdirSync(this.mosaicDir, { recursive: true }); 151 | } 152 | 153 | // If the `this.originChain` is not present, then `this.chain` is the 154 | // origin chain itself. 155 | PublishMosaicConfig.tryPublish(this.originChain || this.chain); 156 | } 157 | 158 | /** 159 | * Logs the given message as `info`. Adds the chain id to the metadata of the log message. 160 | * @param message The message to log. 161 | */ 162 | protected logInfo(message: string): void { 163 | Logger.info(message, { chain: this.chain }); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /graph/origin/schema.graphql: -------------------------------------------------------------------------------- 1 | type StateRootAvailable @entity { 2 | id: ID! 3 | _blockHeight: BigInt! # uint256 4 | _stateRoot: Bytes! # bytes32 5 | blockNumber: BigInt! # uint256 6 | blockHash: Bytes! # bytes32 7 | contractAddress: Bytes! # address 8 | uts: BigInt! # uint256 9 | } 10 | 11 | type StakeRequested @entity { 12 | id: ID! 13 | amount: BigInt! # uint256 14 | beneficiary: Bytes! # address 15 | gasPrice: BigInt! # uint256 16 | gasLimit: BigInt! # uint256 17 | nonce: BigInt! # uint256 18 | staker: Bytes! # address 19 | gateway: Bytes! # address 20 | stakerProxy: Bytes! # address 21 | stakeRequestHash: Bytes! # bytes32 22 | blockNumber: BigInt! # uint256 23 | blockHash: Bytes! # bytes32 24 | contractAddress: Bytes! # address 25 | uts: BigInt! # uint256 26 | } 27 | 28 | type StakeRevoked @entity { 29 | id: ID! 30 | staker: Bytes! # address 31 | stakeRequestHash: Bytes! # bytes32 32 | blockNumber: BigInt! # uint256 33 | blockHash: Bytes! # bytes32 34 | contractAddress: Bytes! # address 35 | uts: BigInt! # uint256 36 | } 37 | 38 | type StakeRejected @entity { 39 | id: ID! 40 | staker: Bytes! # address 41 | stakeRequestHash: Bytes! # bytes32 42 | blockNumber: BigInt! # uint256 43 | blockHash: Bytes! # bytes32 44 | contractAddress: Bytes! # address 45 | uts: BigInt! # uint256 46 | } 47 | 48 | type StakeIntentDeclared @entity { 49 | id: ID! 50 | _messageHash: Bytes! # bytes32 51 | _staker: Bytes! # address 52 | _stakerNonce: BigInt! # uint256 53 | _beneficiary: Bytes! # address 54 | _amount: BigInt! # uint256 55 | blockNumber: BigInt! # uint256 56 | blockHash: Bytes! # bytes32 57 | contractAddress: Bytes! # address 58 | uts: BigInt! # uint256 59 | } 60 | 61 | type StakeProgressed @entity { 62 | id: ID! 63 | _messageHash: Bytes! # bytes32 64 | _staker: Bytes! # address 65 | _stakerNonce: BigInt! # uint256 66 | _amount: BigInt! # uint256 67 | _proofProgress: Boolean! # bool 68 | _unlockSecret: Bytes! # bytes32 69 | blockNumber: BigInt! # uint256 70 | blockHash: Bytes! # bytes32 71 | contractAddress: Bytes! # address 72 | uts: BigInt! # uint256 73 | } 74 | 75 | type RevertStakeIntentDeclared @entity { 76 | id: ID! 77 | _messageHash: Bytes! # bytes32 78 | _staker: Bytes! # address 79 | _stakerNonce: BigInt! # uint256 80 | _amount: BigInt! # uint256 81 | blockNumber: BigInt! # uint256 82 | blockHash: Bytes! # bytes32 83 | contractAddress: Bytes! # address 84 | uts: BigInt! # uint256 85 | } 86 | 87 | type StakeReverted @entity { 88 | id: ID! 89 | _messageHash: Bytes! # bytes32 90 | _staker: Bytes! # address 91 | _stakerNonce: BigInt! # uint256 92 | _amount: BigInt! # uint256 93 | blockNumber: BigInt! # uint256 94 | blockHash: Bytes! # bytes32 95 | contractAddress: Bytes! # address 96 | uts: BigInt! # uint256 97 | } 98 | 99 | type RedeemIntentConfirmed @entity { 100 | id: ID! 101 | _messageHash: Bytes! # bytes32 102 | _redeemer: Bytes! # address 103 | _redeemerNonce: BigInt! # uint256 104 | _beneficiary: Bytes! # address 105 | _amount: BigInt! # uint256 106 | _blockHeight: BigInt! # uint256 107 | _hashLock: Bytes! # bytes32 108 | blockNumber: BigInt! # uint256 109 | blockHash: Bytes! # bytes32 110 | contractAddress: Bytes! # address 111 | uts: BigInt! # uint256 112 | } 113 | 114 | type UnstakeProgressed @entity { 115 | id: ID! 116 | _messageHash: Bytes! # bytes32 117 | _redeemer: Bytes! # address 118 | _beneficiary: Bytes! # address 119 | _redeemAmount: BigInt! # uint256 120 | _unstakeAmount: BigInt! # uint256 121 | _rewardAmount: BigInt! # uint256 122 | _proofProgress: Boolean! # bool 123 | _unlockSecret: Bytes! # bytes32 124 | blockNumber: BigInt! # uint256 125 | blockHash: Bytes! # bytes32 126 | contractAddress: Bytes! # address 127 | uts: BigInt! # uint256 128 | } 129 | 130 | type RevertRedeemIntentConfirmed @entity { 131 | id: ID! 132 | _messageHash: Bytes! # bytes32 133 | _redeemer: Bytes! # address 134 | _redeemerNonce: BigInt! # uint256 135 | _amount: BigInt! # uint256 136 | blockNumber: BigInt! # uint256 137 | blockHash: Bytes! # bytes32 138 | contractAddress: Bytes! # address 139 | uts: BigInt! # uint256 140 | } 141 | 142 | type RevertRedeemComplete @entity { 143 | id: ID! 144 | _messageHash: Bytes! # bytes32 145 | _redeemer: Bytes! # address 146 | _redeemerNonce: BigInt! # uint256 147 | _amount: BigInt! # uint256 148 | blockNumber: BigInt! # uint256 149 | blockHash: Bytes! # bytes32 150 | contractAddress: Bytes! # address 151 | uts: BigInt! # uint256 152 | } 153 | 154 | type GatewayProven @entity { 155 | id: ID! 156 | _gateway: Bytes! # address 157 | _blockHeight: BigInt! # uint256 158 | _storageRoot: Bytes! # bytes32 159 | _wasAlreadyProved: Boolean! # bool 160 | blockNumber: BigInt! # uint256 161 | blockHash: Bytes! # bytes32 162 | contractAddress: Bytes! # address 163 | uts: BigInt! # uint256 164 | } 165 | 166 | type BountyChangeInitiated @entity { 167 | id: ID! 168 | _currentBounty: BigInt! # uint256 169 | _proposedBounty: BigInt! # uint256 170 | _unlockHeight: BigInt! # uint256 171 | blockNumber: BigInt! # uint256 172 | blockHash: Bytes! # bytes32 173 | contractAddress: Bytes! # address 174 | uts: BigInt! # uint256 175 | } 176 | 177 | type BountyChangeConfirmed @entity { 178 | id: ID! 179 | _currentBounty: BigInt! # uint256 180 | _changedBounty: BigInt! # uint256 181 | blockNumber: BigInt! # uint256 182 | blockHash: Bytes! # bytes32 183 | contractAddress: Bytes! # address 184 | uts: BigInt! # uint256 185 | } 186 | -------------------------------------------------------------------------------- /graph/auxiliary/schema.graphql: -------------------------------------------------------------------------------- 1 | type StateRootAvailable @entity { 2 | id: ID! 3 | _blockHeight: BigInt! # uint256 4 | _stateRoot: Bytes! # bytes32 5 | blockNumber: BigInt! # uint256 6 | blockHash: Bytes! # bytes32 7 | contractAddress: Bytes! # address 8 | uts: BigInt! # uint256 9 | } 10 | 11 | type StakeIntentConfirmed @entity { 12 | id: ID! 13 | _messageHash: Bytes! # bytes32 14 | _staker: Bytes! # address 15 | _stakerNonce: BigInt! # uint256 16 | _beneficiary: Bytes! # address 17 | _amount: BigInt! # uint256 18 | _blockHeight: BigInt! # uint256 19 | _hashLock: Bytes! # bytes32 20 | blockNumber: BigInt! # uint256 21 | blockHash: Bytes! # bytes32 22 | contractAddress: Bytes! # address 23 | uts: BigInt! # uint256 24 | } 25 | 26 | type MintProgressed @entity { 27 | id: ID! 28 | _messageHash: Bytes! # bytes32 29 | _staker: Bytes! # address 30 | _beneficiary: Bytes! # address 31 | _stakeAmount: BigInt! # uint256 32 | _mintedAmount: BigInt! # uint256 33 | _rewardAmount: BigInt! # uint256 34 | _proofProgress: Boolean! # bool 35 | _unlockSecret: Bytes! # bytes32 36 | blockNumber: BigInt! # uint256 37 | blockHash: Bytes! # bytes32 38 | contractAddress: Bytes! # address 39 | uts: BigInt! # uint256 40 | } 41 | 42 | type RevertStakeIntentConfirmed @entity { 43 | id: ID! 44 | _messageHash: Bytes! # bytes32 45 | _staker: Bytes! # address 46 | _stakerNonce: BigInt! # uint256 47 | _amount: BigInt! # uint256 48 | blockNumber: BigInt! # uint256 49 | blockHash: Bytes! # bytes32 50 | contractAddress: Bytes! # address 51 | uts: BigInt! # uint256 52 | } 53 | 54 | type RevertStakeProgressed @entity { 55 | id: ID! 56 | _messageHash: Bytes! # bytes32 57 | _staker: Bytes! # address 58 | _stakerNonce: BigInt! # uint256 59 | _amount: BigInt! # uint256 60 | blockNumber: BigInt! # uint256 61 | blockHash: Bytes! # bytes32 62 | contractAddress: Bytes! # address 63 | uts: BigInt! # uint256 64 | } 65 | 66 | type RedeemIntentDeclared @entity { 67 | id: ID! 68 | _messageHash: Bytes! # bytes32 69 | _redeemer: Bytes! # address 70 | _redeemerNonce: BigInt! # uint256 71 | _beneficiary: Bytes! # address 72 | _amount: BigInt! # uint256 73 | blockNumber: BigInt! # uint256 74 | blockHash: Bytes! # bytes32 75 | contractAddress: Bytes! # address 76 | uts: BigInt! # uint256 77 | } 78 | 79 | type RedeemProgressed @entity { 80 | id: ID! 81 | _messageHash: Bytes! # bytes32 82 | _redeemer: Bytes! # address 83 | _redeemerNonce: BigInt! # uint256 84 | _amount: BigInt! # uint256 85 | _proofProgress: Boolean! # bool 86 | _unlockSecret: Bytes! # bytes32 87 | blockNumber: BigInt! # uint256 88 | blockHash: Bytes! # bytes32 89 | contractAddress: Bytes! # address 90 | uts: BigInt! # uint256 91 | } 92 | 93 | type RevertRedeemDeclared @entity { 94 | id: ID! 95 | _messageHash: Bytes! # bytes32 96 | _redeemer: Bytes! # address 97 | _redeemerNonce: BigInt! # uint256 98 | _amount: BigInt! # uint256 99 | blockNumber: BigInt! # uint256 100 | blockHash: Bytes! # bytes32 101 | contractAddress: Bytes! # address 102 | uts: BigInt! # uint256 103 | } 104 | 105 | type RedeemReverted @entity { 106 | id: ID! 107 | _messageHash: Bytes! # bytes32 108 | _redeemer: Bytes! # address 109 | _redeemerNonce: BigInt! # uint256 110 | _amount: BigInt! # uint256 111 | blockNumber: BigInt! # uint256 112 | blockHash: Bytes! # bytes32 113 | contractAddress: Bytes! # address 114 | uts: BigInt! # uint256 115 | } 116 | 117 | type GatewayProven @entity { 118 | id: ID! 119 | _gateway: Bytes! # address 120 | _blockHeight: BigInt! # uint256 121 | _storageRoot: Bytes! # bytes32 122 | _wasAlreadyProved: Boolean! # bool 123 | blockNumber: BigInt! # uint256 124 | blockHash: Bytes! # bytes32 125 | contractAddress: Bytes! # address 126 | uts: BigInt! # uint256 127 | } 128 | 129 | type BountyChangeInitiated @entity { 130 | id: ID! 131 | _currentBounty: BigInt! # uint256 132 | _proposedBounty: BigInt! # uint256 133 | _unlockHeight: BigInt! # uint256 134 | blockNumber: BigInt! # uint256 135 | blockHash: Bytes! # bytes32 136 | contractAddress: Bytes! # address 137 | uts: BigInt! # uint256 138 | } 139 | 140 | type BountyChangeConfirmed @entity { 141 | id: ID! 142 | _currentBounty: BigInt! # uint256 143 | _changedBounty: BigInt! # uint256 144 | blockNumber: BigInt! # uint256 145 | blockHash: Bytes! # bytes32 146 | contractAddress: Bytes! # address 147 | uts: BigInt! # uint256 148 | } 149 | 150 | type RedeemRequested @entity { 151 | id: ID! 152 | amount: BigInt! # uint256 153 | beneficiary: Bytes! # address 154 | gasPrice: BigInt! # uint256 155 | gasLimit: BigInt! # uint256 156 | nonce: BigInt! # uint256 157 | redeemer: Bytes! # address 158 | redeemerProxy: Bytes! # address 159 | cogateway: Bytes! # address 160 | redeemRequestHash: Bytes! # bytes32 161 | blockNumber: BigInt! # uint256 162 | blockHash: Bytes! # bytes32 163 | contractAddress: Bytes! # address 164 | uts: BigInt! # uint256 165 | } 166 | 167 | type RedeemRevoked @entity { 168 | id: ID! 169 | redeemer: Bytes! # address 170 | redeemRequestHash: Bytes! # bytes32 171 | blockNumber: BigInt! # uint256 172 | blockHash: Bytes! # bytes32 173 | contractAddress: Bytes! # address 174 | uts: BigInt! # uint256 175 | } 176 | 177 | type RedeemRejected @entity { 178 | id: ID! 179 | redeemer: Bytes! # address 180 | redeemRequestHash: Bytes! # bytes32 181 | blockNumber: BigInt! # uint256 182 | blockHash: Bytes! # bytes32 183 | contractAddress: Bytes! # address 184 | uts: BigInt! # uint256 185 | } 186 | -------------------------------------------------------------------------------- /src/Directory.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as os from 'os'; 3 | import * as web3Utils from 'web3-utils'; 4 | 5 | const MOSAIC_CONFIG_FILE = 'mosaic.json'; 6 | /** 7 | * Directory provides operations on strings representing directories. 8 | */ 9 | export default class Directory { 10 | /** 11 | * @returns The absolute path to the directory in which we store mosaic data. 12 | */ 13 | public static get getDefaultMosaicDataDir(): string { 14 | return path.join( 15 | os.homedir(), 16 | '.mosaic', 17 | ); 18 | } 19 | 20 | /** 21 | * @returns The absolute path to the root of this project. 22 | */ 23 | public static get projectRoot(): string { 24 | return path.join( 25 | __dirname, 26 | '..', 27 | ); 28 | } 29 | 30 | /** 31 | * @param originChain The origin chain identifier. 32 | * @param auxiliaryChainId The auxiliary chain id of the chain. 33 | * @returns The absolute path to the directory of the given utility chain. 34 | * @throws If `auxiliaryChainId` or `originChain` is an empty string. 35 | */ 36 | public static getProjectUtilityChainDir( 37 | originChain: string, 38 | auxiliaryChainId: string, 39 | ): string { 40 | if (originChain === undefined || originChain.length === 0) { 41 | throw new Error('Origin chain identifier cannot be empty in order to get its directory'); 42 | } 43 | if (auxiliaryChainId === undefined || auxiliaryChainId.length === 0) { 44 | throw new Error('Auxiliary chain id cannot be empty in order to get its directory'); 45 | } 46 | 47 | return path.join( 48 | Directory.getProjectChainsDirectory, 49 | originChain, 50 | auxiliaryChainId, 51 | ); 52 | } 53 | 54 | /** 55 | * @returns The absolute path to the directory of the Graph code. 56 | */ 57 | public static getProjectGraphDir(): string { 58 | return path.join( 59 | Directory.projectRoot, 60 | 'src', 61 | 'Graph', 62 | ); 63 | } 64 | 65 | /** 66 | * @param {string} subGraphType 67 | * @returns The absolute path to the directory of the auto generated Graph code. 68 | */ 69 | public static getProjectAutoGenGraphDir(subGraphType: string): string { 70 | return path.join( 71 | Directory.projectRoot, 72 | 'graph', 73 | subGraphType, 74 | ); 75 | } 76 | 77 | /** 78 | * 79 | * @param originChain 80 | * @param auxiliaryChain 81 | * @return 82 | */ 83 | public static getOriginSubGraphProjectDirSuffix( 84 | originChain: string, 85 | auxiliaryChain: string, 86 | ): string { 87 | return path.join( 88 | originChain, 89 | 'origin-subgraph', 90 | auxiliaryChain, 91 | ); 92 | } 93 | 94 | /** 95 | * @param originChain Origin chain. 96 | * @param auxiliaryChain auxiliary chain id. 97 | * @return path 98 | */ 99 | public static getAuxiliarySubGraphProjectDirSuffix( 100 | originChain: string, 101 | auxiliaryChain: string, 102 | ): string { 103 | return path.join( 104 | originChain, 105 | auxiliaryChain, 106 | 'subgraph', 107 | auxiliaryChain, 108 | ); 109 | } 110 | 111 | /** 112 | * @returns The absolute path to the directory where we copy code temporarily to deploy graph. 113 | */ 114 | public static get getTempGraphInstallationDir(): string { 115 | return path.join( 116 | Directory.getDefaultMosaicDataDir, 117 | 'temp', 118 | ); 119 | } 120 | 121 | /** 122 | * Sanitizes given directory strings: 123 | * - replaces `~` at the beginning with the absolute path to the home directory. 124 | * - translates relative paths to absolute paths. 125 | * @param directory The directory string to sanitize. 126 | */ 127 | public static sanitize(directory: string): string { 128 | let sanitized: string = directory; 129 | 130 | if (sanitized.substr(0, 1) === '~') { 131 | sanitized = path.join(os.homedir(), sanitized.substr(1)); 132 | } 133 | 134 | // Relative directory name 135 | if (sanitized.substr(0, 1) !== '/') { 136 | sanitized = path.join( 137 | process.cwd(), 138 | sanitized, 139 | ); 140 | } 141 | 142 | return sanitized; 143 | } 144 | 145 | /** 146 | * Returns the mosaic json file name. 147 | */ 148 | public static getMosaicFileName(): string { 149 | return MOSAIC_CONFIG_FILE; 150 | } 151 | 152 | /** 153 | * Returns the full path of mosaic config for a given origin chain. 154 | * @param originChain Origin Chain Identifier 155 | * @return Path of mosaic config file. 156 | */ 157 | public static getMosaicConfigPath(originChain: string): string { 158 | return path.join( 159 | Directory.getDefaultMosaicDataDir, 160 | originChain, 161 | Directory.getMosaicFileName(), 162 | ); 163 | } 164 | 165 | /** 166 | * This method return project chains directory. 167 | */ 168 | public static get getProjectChainsDirectory(): string { 169 | return path.join( 170 | Directory.projectRoot, 171 | 'chains', 172 | ); 173 | } 174 | 175 | /** 176 | * Returns the full path of GatewayConfig for a given origin, auxiliary and gatewayAddress. 177 | * 178 | * @param originChain Origin chain identifier. 179 | * @param auxChainId Auxiliary chain Id. 180 | * @param gatewayAddress Address of Gateway. 181 | * 182 | * @return Path of gateway config file. 183 | */ 184 | public static getGatewayConfigPath( 185 | originChain: string, 186 | auxChainId: number, 187 | gatewayAddress: string, 188 | ): string { 189 | return path.join( 190 | Directory.getDefaultMosaicDataDir, 191 | originChain, 192 | auxChainId.toString(), 193 | `gateway-${web3Utils.toChecksumAddress(gatewayAddress)}`, 194 | 'gateway-config.json', 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/bin/NodeOptions.ts: -------------------------------------------------------------------------------- 1 | import Logger from '../Logger'; 2 | import Directory from '../Directory'; 3 | import Integer from '../Integer'; 4 | import ChainInfo from '../Node/ChainInfo'; 5 | 6 | // These defaults will be used if the relevant option is not given on the command line. 7 | const DEFAULT_MOSAIC_DIR = Directory.getDefaultMosaicDataDir; 8 | const DEFAULT_PORT = 30000; 9 | const DEFAULT_RPC_PORT = 40000; 10 | const DEFAULT_WS_PORT = 50000; 11 | 12 | /** 13 | * Command line options for running an ethereum node. 14 | */ 15 | export default class NodeOptions { 16 | /** The mosaic directory to use which holds the chains' subdirectories. */ 17 | public mosaicDir: string; 18 | 19 | /** The port of the ethereum node that docker publishes on the host. */ 20 | public port: number; 21 | 22 | /** The rpc port of the ethereum node that docker publishes on the host. */ 23 | public rpcPort: number; 24 | 25 | /** The websocket port of the ethereum node that docker publishes on the host. */ 26 | public websocketPort: number; 27 | 28 | /** If set to true, the container will not be deleted when it is stopped. Defaults to false. */ 29 | public keepAfterStop: boolean; 30 | 31 | /** A comma-separated list of accounts to unlock when starting the node. */ 32 | public unlock: string; 33 | 34 | /** 35 | * Path to a password file with one line per unlocked account. 36 | * We have to use a password file when unlocking as we cannot provide the password on the command 37 | * line when running the docker container as a daemon in the background. 38 | */ 39 | public password: string; 40 | 41 | /** 42 | * Identifier for origin chain. 43 | * This needs to be passed if auxiliary chain needs to be started. 44 | */ 45 | public originChain: string; 46 | 47 | /** List of boot nodes to start the node */ 48 | public bootNodesFile: string; 49 | 50 | /** RPC and IPC endpoint of clef */ 51 | public clefSigner?: string; 52 | 53 | /** if set, code will perform geth init before starting node. Defaults to false. */ 54 | public forceInit?: boolean; 55 | 56 | /** 57 | * @param options The options from the command line. 58 | */ 59 | constructor(options: { 60 | mosaicDir: string; 61 | port: string; 62 | rpcPort: string; 63 | websocketPort: string; 64 | keepAfterStop: boolean; 65 | unlock: string; 66 | password: string; 67 | originChain: string; 68 | bootNodesFile: string; 69 | clefSigner: string; 70 | forceInit: boolean; 71 | }) { 72 | Object.assign(this, options); 73 | this.bootNodesFile = options.bootNodesFile; 74 | this.clefSigner = options.clefSigner; 75 | } 76 | 77 | /** 78 | * Adds the common node options to a command. Does not include unlock and password. Add these 79 | * separately if you need them. 80 | * @param command The command where to add the options. 81 | * @returns The command with the options added. 82 | */ 83 | public static addCliOptions(command): any { 84 | command 85 | .option('-o,--origin ', 'identifier for origin chain. To be passed while starting auxiliary chain') 86 | .option('-c,--client ', 'identifier for client (geth/parity). To be passed while starting origin chain') 87 | .option('-d,--mosaic-dir ', 'a path to a directory where the chain data will be stored', DEFAULT_MOSAIC_DIR) 88 | .option('-p,--port ', 'the port to use for forwarding from host to container', Integer.parseString) 89 | .option('-r,--rpc-port ', 'the RPC port to use for forwarding from host to container', Integer.parseString) 90 | .option('-w,--ws-port ', 'the WS port to use for forwarding from host to container', Integer.parseString) 91 | .option('-k,--keep', 'if set, the container will not automatically be deleted when stopped') 92 | .option('-f,--force-init', 'if set, code will perform geth init before starting node'); 93 | return command; 94 | } 95 | 96 | /** 97 | * Parses the commander options and returns an Options object. 98 | * @param options Options as they are given by commander. 99 | * @param chain Chain identifier. 100 | * @returns The parsed options with defaults for options that are missing from the command line. 101 | */ 102 | public static parseOptions(options, chain): NodeOptions { 103 | const chainIdNumber = ChainInfo.getChainId(chain); 104 | const parsedOptions: NodeOptions = new NodeOptions({ 105 | mosaicDir: options.mosaicDir || DEFAULT_MOSAIC_DIR, 106 | port: options.port || Number.parseInt(chainIdNumber) + DEFAULT_PORT, 107 | rpcPort: options.rpcPort || Number.parseInt(chainIdNumber) + DEFAULT_RPC_PORT, 108 | websocketPort: options.wsPort || Number.parseInt(chainIdNumber) + DEFAULT_WS_PORT, 109 | keepAfterStop: !!options.keep, 110 | unlock: options.unlock || '', 111 | password: options.password || '', 112 | originChain: options.origin || '', 113 | bootNodesFile: options.bootnodes, 114 | clefSigner: options.clefSigner, 115 | forceInit: !!options.forceInit, 116 | }); 117 | 118 | parsedOptions.mosaicDir = Directory.sanitize(parsedOptions.mosaicDir); 119 | 120 | if (parsedOptions.password !== '') { 121 | parsedOptions.password = Directory.sanitize(parsedOptions.password); 122 | } 123 | 124 | if (parsedOptions.unlockOrPasswordWithoutTheOther()) { 125 | Logger.error('cannot use --unlock or --password without the other'); 126 | process.exit(1); 127 | } 128 | 129 | return parsedOptions; 130 | } 131 | 132 | 133 | /** 134 | * Checks if unlock or password were given as a command line option but not the other. 135 | * You have to give both options or none of them. 136 | * @param options The parsed command line options. 137 | * @returns True if one option was given but not the other. 138 | */ 139 | public unlockOrPasswordWithoutTheOther(): boolean { 140 | const { unlock, password } = this; 141 | 142 | if (unlock !== '' && password === '') { 143 | return true; 144 | } 145 | 146 | if (unlock === '' && password !== '') { 147 | return true; 148 | } 149 | 150 | return false; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /chains/ropsten/mosaic.json: -------------------------------------------------------------------------------- 1 | { 2 | "originChain": { 3 | "contractAddresses": { 4 | "gatewayLibAddress": "0x2fdbe39dcc46f9c8c973e30349f388b4490d50e0", 5 | "messageBusAddress": "0x19e9c9a9e5f53c060387567835b534dbad0b8d02", 6 | "merklePatriciaLibAddress": "0x17b27fe03befcac0712b13df7ff242d8f994fed5", 7 | "valueTokenAddress": "0xae96d2acd01ab795e5942591100f9f23adbe2a2e", 8 | "stakePoolAddress": "0x4a633b3375a5b9eaa6ddc226ebe2ac832b557746" 9 | }, 10 | "chain": "ropsten" 11 | }, 12 | "auxiliaryChains": { 13 | "1406": { 14 | "bootNodes": [ 15 | "enode://edaed9d130c1d4580a17ecf572500c2f30ef4bfc0fbee3764a1a23a837b8ee10a3698051c5339473132343e69729b243f565873b233031467599a17e08252de0@99.80.80.138:30301" 16 | ], 17 | "contractAddresses": { 18 | "origin": { 19 | "baseTokenAddress": "0xae96d2acd01ab795e5942591100f9f23adbe2a2e", 20 | "anchorOrganizationAddress": "0xd8cca90afe0a15a0d372540e2104d6098bdee9af", 21 | "anchorAddress": "0xe322ed07e235a6501bd017ae9ea059b12723c55c", 22 | "gatewayOrganizationAddress": "0x7c8ebf6da810261eb8ec9fbec989aa9c79200c07", 23 | "eip20GatewayAddress": "0x04df90efbedf393361cdf498234af818da14f562" 24 | }, 25 | "auxiliary": { 26 | "anchorOrganizationAddress": "0x29050903e7e2612743f9d6234c553d2b96ca9683", 27 | "anchorAddress": "0x959cc0a11176584d90fd77cf189b73345792e7f2", 28 | "coGatewayOrganizationAddress": "0xdff949b45ab91b7bc413bfef17aa9f5766b516f2", 29 | "utilityTokenAddress": "0x495b17dda2cd0406290af54e9ae978b381006809", 30 | "eip20CoGatewayAddress": "0x02cffaa1e06c28021fff6b36d9e418a97b3de2fc", 31 | "gatewayLibAddress": "0xcf02a1d6053b53559a7c8060a74e1286a0b7c244", 32 | "messageBusAddress": "0xa482ff75b6a87ba9b669e0636867964b9c2f7950", 33 | "merklePatriciaLibAddress": "0x8346e47aa6bf292d26ad7d08cb6b09afe7e8d4f9", 34 | "redeemPoolAddress": "0xDF2FffBe5b2C53551C6879F957F4c9421f8443ae" 35 | } 36 | }, 37 | "chainId": 1406, 38 | "genesis": { 39 | "config": { 40 | "chainId": 1406, 41 | "homesteadBlock": 1, 42 | "eip150Block": 2, 43 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 44 | "eip155Block": 3, 45 | "eip158Block": 3, 46 | "byzantiumBlock": 4, 47 | "clique": { 48 | "period": 3, 49 | "epoch": 30000 50 | } 51 | }, 52 | "nonce": "0x0", 53 | "timestamp": "0x5a71660b", 54 | "extraData": "0x7574696c69747920636861696e000000000000000000000000000000000000002a3217c2A5818C117a0E1A84A133262F9e1513Bc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 55 | "gasLimit": "0x989680", 56 | "difficulty": "0x1", 57 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 58 | "coinbase": "0x0000000000000000000000000000000000000000", 59 | "alloc": { 60 | "537f8cdc8c706650f497bcb1eee2fc8ceedc19bf": { 61 | "balance": "0x295be96e640669720000000" 62 | } 63 | }, 64 | "number": "0x0", 65 | "gasUsed": "0x0", 66 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" 67 | } 68 | }, 69 | "1407": { 70 | "bootNodes": [ 71 | "enode://e60ff2d4ed05213b8ae32ca00fd7a0252d7fe8fea48911043c3ea3db8749130014e952ee22f5600e649eb8e25fbffb64e4e1f7feb915fd2d7d518b86973c24af@99.80.85.164:30301" 72 | ], 73 | "contractAddresses": { 74 | "origin": { 75 | "baseTokenAddress": "0xae96d2acd01ab795e5942591100f9f23adbe2a2e", 76 | "anchorOrganizationAddress": "0xd8cca90afe0a15a0d372540e2104d6098bdee9af", 77 | "anchorAddress": "0x972d3e5e8fba4689add411cd8ebe27e6d060d531", 78 | "gatewayOrganizationAddress": "0x7c8ebf6da810261eb8ec9fbec989aa9c79200c07", 79 | "eip20GatewayAddress": "0x31c8870c76390c5eb0d425799b5bd214a2600438" 80 | }, 81 | "auxiliary": { 82 | "anchorOrganizationAddress": "0xe7d62158b4905a76d26ed93a254582ee6d84d5a7", 83 | "anchorAddress": "0xde1d126e4deb499bd7f64ff59fe8f9415be50ef8", 84 | "coGatewayOrganizationAddress": "0x610fb96852f0d854b0633aba0073d18246bc94d5", 85 | "utilityTokenAddress": "0x66ca08a32548593ea1860e3995b81e4354f3b1b1", 86 | "eip20CoGatewayAddress": "0xf690624171fe06d02d2f4250bff17fe3b682ebd1", 87 | "gatewayLibAddress": "0x033e8675db854ba05b08b5e076a48e3eedeb0b54", 88 | "messageBusAddress": "0xd3b63c070ef226ef6dba0abf2ea223b04766efcc", 89 | "merklePatriciaLibAddress": "0x233e3af4b022a58d25a03fff52c3e5925c289cc7", 90 | "redeemPoolAddress": "0x0FC346A86774939f6894bEE2e8c38a4A34c7BADC" 91 | } 92 | }, 93 | "chainId": 1407, 94 | "genesis": { 95 | "config": { 96 | "chainId": 1407, 97 | "homesteadBlock": 1, 98 | "eip150Block": 2, 99 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 100 | "eip155Block": 3, 101 | "eip158Block": 3, 102 | "byzantiumBlock": 4, 103 | "clique": { 104 | "period": 3, 105 | "epoch": 30000 106 | } 107 | }, 108 | "nonce": "0x0", 109 | "timestamp": "0x5a71660b", 110 | "extraData": "0x7574696c69747920636861696e00000000000000000000000000000000000000d3E8E61c23f9302CB0FdA62bc7696064990A17080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 111 | "gasLimit": "0x989680", 112 | "difficulty": "0x1", 113 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 114 | "coinbase": "0x0000000000000000000000000000000000000000", 115 | "alloc": { 116 | "537f8cdc8c706650f497bcb1eee2fc8ceedc19bf": { 117 | "balance": "0x295be96e640669720000000" 118 | } 119 | }, 120 | "number": "0x0", 121 | "gasUsed": "0x0", 122 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" 123 | } 124 | } 125 | } 126 | } 127 | --------------------------------------------------------------------------------