UNPKG

@maticnetwork/matic-cli

Version:

Testing toolkit to setup, manage and operate Polygon networks

528 lines (488 loc) 16.6 kB
// noinspection JSCheckFunctionSignatures, JSUnresolvedFunction import yaml from 'js-yaml' import fs from 'fs' import { cleanEnv, num, bool, url, host, makeValidator } from 'envalid' const shell = require('shelljs') const validStr = makeValidator((x) => { if (x !== undefined && x !== null && x !== '') return x else throw new Error(x + 'is not valid, please check your configs!') }) const validBorChainId = makeValidator((x) => { if (x !== undefined && x !== null && (x.match(/^$/g) || x > 0)) return x else throw new Error(x + 'is not valid, please check your configs!') }) const validHeimdallChainId = makeValidator((x) => { if ( x !== undefined && x !== null && (x.match(/^$|heimdall-\d+$/g) || x > 0) ) { return x } else throw new Error(x + 'is not valid, please check your configs!') }) const validAmiStr = makeValidator((x) => { if (x !== undefined && x !== null && x !== '' && x.startsWith('ami-')) { return x } else throw new Error(x + 'is not valid, please check your configs!') }) const validCertPathStr = makeValidator((x) => { if ( x !== undefined && x !== null && x !== '' && !x.startsWith('~') && (x.endsWith('.pem') || x.endsWith('.cer')) ) { return x } else throw new Error(x + 'is not valid, please check your configs!') }) function validateEnvVars() { cleanEnv(process.env, { TF_VAR_AWS_PROFILE: validStr({ choices: ['default'] }), TF_VAR_VM_NAME: validStr({ default: 'polygon-user' }), TF_VAR_DOCKERIZED: validStr({ choices: ['yes', 'no'] }), TF_VAR_DISK_SIZE_GB: num({ default: 500 }), TF_VAR_IOPS: num({ default: 3000 }), TF_VAR_VALIDATOR_COUNT: num({ default: 1 }), TF_VAR_SENTRY_COUNT: num({ default: 1 }), TF_VAR_INSTANCE_TYPE: validStr({ default: 't2.xlarge' }), TF_VAR_INSTANCE_AMI: validAmiStr({ default: 'ami-017fecd1353bcc96e' }), TF_VAR_PEM_FILE: validStr({ default: 'aws-key' }), TF_VAR_REGION: validStr({ default: 'us-west-2', choices: [ 'us-east-2', 'us-east-1', 'us-west-1', 'us-west-2', 'af-south-1', 'ap-east-1', 'ap-south-2', 'ap-southeast-3', 'ap-south-1', 'ap-northeast-3', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'ca-central-1', 'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-south-1', 'eu-west-3', 'eu-south-2', 'eu-north-1', 'eu-central-2', 'me-south-1', 'me-central-1', 'sa-east-1', 'us-gov-east-1', 'us-gov-west-1' ], docs: 'https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/' + 'Concepts.RegionsAndAvailabilityZones.html' }), PEM_FILE_PATH: validCertPathStr({ default: '/home/ubuntu/aws-key.pem' }), DEFAULT_STAKE: num({ default: 10000 }), DEFAULT_FEE: num({ default: 2000 }), BOR_CHAIN_ID: validBorChainId({ default: '15005' }), HEIMDALL_CHAIN_ID: validHeimdallChainId({ default: 'heimdall-4052' }), SPRINT_SIZE: num({ default: 64 }), BLOCK_NUMBER: validStr({ default: '0,64' }), BLOCK_TIME: validStr({ default: '3,2' }), BOR_REPO: url({ default: 'https://github.com/maticnetwork/bor.git' }), BOR_BRANCH: validStr({ default: 'develop' }), HEIMDALL_REPO: url({ default: 'https://github.com/maticnetwork/heimdall.git' }), HEIMDALL_BRANCH: validStr({ default: 'develop' }), CONTRACTS_REPO: url({ default: 'https://github.com/maticnetwork/contracts.git' }), CONTRACTS_BRANCH: validStr({ default: 'master' }), GENESIS_CONTRACTS_REPO: url({ default: 'https://github.com/maticnetwork/genesis-contracts.git' }), GENESIS_CONTRACTS_BRANCH: validStr({ default: 'master' }), MATIC_CLI_REPO: url({ default: 'https://github.com/maticnetwork/matic-cli.git' }), MATIC_CLI_BRANCH: validStr({ default: 'master' }), DEVNET_BOR_USERS: validStr({ default: 'ubuntu,ubuntu' }), INSTANCES_IDS: validStr({ default: 'i-02a1f3a2884c9edbc,i-03b2d4b3014a4becd' }), BOR_DOCKER_BUILD_CONTEXT: url({ default: 'https://github.com/maticnetwork/bor.git#develop' }), HEIMDALL_DOCKER_BUILD_CONTEXT: url({ default: 'https://github.com/maticnetwork/heimdall.git#develop' }), VERBOSE: bool({ default: true }), DD_API_KEY: validStr({ default: 'DATADOG_API_KEY' }), MNEMONIC: validStr({ default: 'clock radar mass judge dismiss just intact ' + 'mind resemble fringe diary casino' }), SPEED: num({ default: 200 }), MAX_ACCOUNTS: num({ default: 100000 }), FUND: bool({ default: true }), STRESS_DEBUG_LOGS: bool({ default: true }), BURN_CONTRACT_ADDRESS: validStr({ default: '0x000000000000000000000000000000000000dead' }), MAX_FEE: num({ default: 30000000009 }), MAX_PRIORITY_FEE: num({ default: 30000000000 }), COUNT: num({ default: 100 }) }) } function validateAwsKeyAndCertificate() { const certFilePath = process.env.PEM_FILE_PATH const certName = certFilePath .substring(certFilePath.lastIndexOf('/') + 1) .split('.')[0] if (certName !== process.env.TF_VAR_PEM_FILE) { console.log( '❌ PEM_FILE_PATH and TF_VAR_PEM_FILE are inconsistent, please check your configs!' ) process.exit(1) } } function validateUsersAndHosts() { console.log('📍Validating DEVNET_BOR_USERS and DEVNET_BOR_HOSTS...') const borUsers = process.env.DEVNET_BOR_USERS.split(',') const borHosts = process.env.DEVNET_BOR_HOSTS.split(',') const valCount = Number(process.env.TF_VAR_VALIDATOR_COUNT) const senCount = Number(process.env.TF_VAR_SENTRY_COUNT) const archiveCount = Number(process.env.TF_VAR_ARCHIVE_COUNT) if ( process.env.TF_VAR_DOCKERIZED === 'yes' && borUsers.length !== valCount + senCount + archiveCount ) { console.log( '❌ DEVNET_BOR_USERS lengths are not equal to the nodes count ' + '(TF_VAR_VALIDATOR_COUNT+TF_VAR_SENTRY_COUNT+TF_VAR_ARCHIVE_COUNT), please check your configs!' ) process.exit(1) } else if ( process.env.TF_VAR_DOCKERIZED === 'no' && (borUsers.length !== borHosts.length || borUsers.length !== valCount + senCount + archiveCount || borHosts.length !== valCount + senCount + archiveCount) ) { console.log( '❌ DEVNET_BOR_USERS or DEVNET_BOR_HOSTS lengths are not equal to the nodes count ' + '(TF_VAR_VALIDATOR_COUNT+TF_VAR_SENTRY_COUNT+TF_VAR_ARCHIVE_COUNT), please check your configs!' ) process.exit(1) } borUsers.forEach((user) => { if (user !== 'ubuntu') { console.log( "❌ DEVNET_BOR_USERS must all be named 'ubuntu', please check your configs!" ) process.exit(1) } }) borHosts.forEach((borHost) => { host(borHost) }) } function validateBlockParams() { console.log( '📍Validating genesis specific values BLOCK_NUMBER and BLOCK_TIME...' ) const blockNumbers = process.env.BLOCK_NUMBER.split(',') const blockTimes = process.env.BLOCK_TIME.split(',') if (blockNumbers.length !== blockTimes.length) { console.log( '❌ BLOCK_NUMBER and BLOCK_TIME have different lengths, please check your configs!' ) process.exit(1) } } function validateGitConfigs() { console.log('📍Validating git configs for all repos...') console.log('📍Validating bor...') shell.exec( `git ls-remote --exit-code --heads --tags ${process.env.BOR_REPO} ${process.env.BOR_BRANCH} || git fetch ${process.env.BOR_REPO} ${process.env.BOR_BRANCH}` ) if (shell.error() != null) { console.log( '❌ Error while test-cloning bor repo, please check your configs!' ) process.exit(1) } console.log('📍Validating heimdall...') shell.exec( `git ls-remote --exit-code --heads --tags ${process.env.HEIMDALL_REPO} ${process.env.HEIMDALL_BRANCH} || git fetch ${process.env.HEIMDALL_REPO} ${process.env.HEIMDALL_BRANCH}` ) if (shell.error() != null) { console.log( '❌ Error while test-cloning heimdall repo, please check your configs!' ) process.exit(1) } console.log('📍Validating matic-cli...') shell.exec( `git ls-remote --exit-code --heads --tags ${process.env.MATIC_CLI_REPO} ${process.env.MATIC_CLI_BRANCH} || git fetch ${process.env.MATIC_CLI_REPO} ${process.env.MATIC_CLI_BRANCH}` ) if (shell.error() != null) { console.log( '❌ Error while test-cloning matic-cli repo, please check your configs!' ) process.exit(1) } console.log('📍Validating contracts...') shell.exec( `git ls-remote --exit-code --heads --tags ${process.env.CONTRACTS_REPO} ${process.env.CONTRACTS_BRANCH} || git fetch ${process.env.CONTRACTS_REPO} ${process.env.CONTRACTS_BRANCH}` ) if (shell.error() != null) { console.log( '❌ Error while test-cloning contracts repo, please check your configs!' ) process.exit(1) } console.log('📍Validating genesis-contracts...') shell.exec( `git ls-remote --exit-code --heads --tags ${process.env.GENESIS_CONTRACTS_REPO} ${process.env.GENESIS_CONTRACTS_BRANCH} || git fetch ${process.env.GENESIS_CONTRACTS_REPO} ${process.env.GENESIS_CONTRACTS_BRANCH}` ) if (shell.error() != null) { console.log( '❌ Error while cloning genesis-contracts repo, please check your configs!' ) process.exit(1) } } function setCommonConfigs(doc) { setConfigValue('defaultStake', parseInt(process.env.DEFAULT_STAKE), doc) setConfigValue('defaultFee', parseInt(process.env.DEFAULT_FEE), doc) let borChainId, heimdallChainId if (!process.env.BOR_CHAIN_ID && !process.env.HEIMDALL_CHAIN_ID) { borChainId = Math.floor(Math.random() * 10000 + 1000) setConfigValue('borChainId', parseInt(borChainId), doc) setConfigValue('heimdallChainId', 'heimdall-' + borChainId, doc) } else if (!process.env.BOR_CHAIN_ID) { try { if (process.env.HEIMDALL_CHAIN_ID > 0) { borChainId = process.env.HEIMDALL_CHAIN_ID heimdallChainId = 'heimdall-' + process.env.HEIMDALL_CHAIN_ID } else { borChainId = process.env.HEIMDALL_CHAIN_ID.split('-')[1] } setConfigValue('borChainId', parseInt(borChainId), doc) setConfigValue('heimdallChainId', heimdallChainId, doc) } catch (error) { console.log( '❌ Error occured while processing heimdall chain id (Heimdall chain id should be like: heimdall-4052)!' ) process.exit(1) } } else if (!process.env.HEIMDALL_CHAIN_ID) { borChainId = process.env.BOR_CHAIN_ID setConfigValue('borChainId', parseInt(process.env.BOR_CHAIN_ID), doc) setConfigValue('heimdallChainId', 'heimdall-' + borChainId, doc) } else { if (process.env.HEIMDALL_CHAIN_ID > 0) { heimdallChainId = 'heimdall-' + process.env.HEIMDALL_CHAIN_ID } else { heimdallChainId = process.env.HEIMDALL_CHAIN_ID } setConfigValue('heimdallChainId', heimdallChainId, doc) setConfigValue('borChainId', parseInt(process.env.BOR_CHAIN_ID), doc) } setConfigList('sprintSize', process.env.SPRINT_SIZE, doc) setConfigList( 'sprintSizeBlockNumber', process.env.SPRINT_SIZE_BLOCK_NUMBER, doc ) setConfigList('blockNumber', process.env.BLOCK_NUMBER, doc) setConfigList('blockTime', process.env.BLOCK_TIME, doc) setConfigValue('borRepo', process.env.BOR_REPO, doc) setConfigValue('borBranch', process.env.BOR_BRANCH, doc) setConfigValue('heimdallRepo', process.env.HEIMDALL_REPO, doc) setConfigValue('heimdallBranch', process.env.HEIMDALL_BRANCH, doc) setConfigValue('contractsRepo', process.env.CONTRACTS_REPO, doc) setConfigValue('contractsBranch', process.env.CONTRACTS_BRANCH, doc) setConfigValue( 'genesisContractsRepo', process.env.GENESIS_CONTRACTS_REPO, doc ) setConfigValue( 'genesisContractsBranch', process.env.GENESIS_CONTRACTS_BRANCH, doc ) setConfigValue( 'numOfValidators', parseInt(process.env.TF_VAR_VALIDATOR_COUNT), doc ) setConfigValue( 'numOfNonValidators', parseInt(process.env.TF_VAR_SENTRY_COUNT), doc ) setConfigValue( 'numOfArchiveNodes', parseInt(process.env.TF_VAR_ARCHIVE_COUNT), doc ) setConfigValue('ethHostUser', process.env.ETH_HOST_USER, doc) setConfigValue( 'borDockerBuildContext', process.env.BOR_DOCKER_BUILD_CONTEXT, doc ) setConfigValue( 'heimdallDockerBuildContext', process.env.HEIMDALL_DOCKER_BUILD_CONTEXT, doc ) setConfigList('instancesIds', process.env.INSTANCES_IDS, doc) } function setConfigValue(key, value, doc) { if (value !== undefined) { doc[key] = value } } function setConfigList(key, value, doc) { if (value !== undefined) { value = value.split(' ').join('') const valueArray = value.split(',') if (valueArray.length > 0) { doc[key] = [] for (let i = 0; i < valueArray.length; i++) { doc[key][i] = valueArray[i] if (i === 0) { if (key === 'devnetBorHosts') { setEthURL(valueArray[i], doc) } if (key === 'devnetBorUsers') { setEthHostUser(valueArray[i], doc) } } } } } } function setEthURL(value, doc) { if (value !== undefined) { doc.ethURL = 'http://' + value + ':9545' process.env.ETH_URL = doc.ethURL } } function setEthHostUser(value, doc) { if (value !== undefined) { doc.ethHostUser = value } } export function splitToArray(value) { try { return value.split(' ').join('').split(',') } catch (error) { console.error('📍Failed to split to IP array: ', error) console.log('📍Exiting...') process.exit(1) } } export function splitAndGetHostIp(value) { try { return value.split('@')[0] } catch (error) { console.error('📍Failed to split IP: ', error) console.log('📍Exiting...') process.exit(1) } } export async function checkAndReturnVMIndex(n, doc) { if (typeof n === 'boolean') { console.log('📍Targeting all VMs ...') return undefined } if (typeof n === 'string') { const vmIndex = parseInt(n, 10) if (vmIndex >= 0 && vmIndex < doc.devnetBorHosts.length) { console.log(`📍Targeting VM with IP ${doc.devnetBorHosts[vmIndex]} ...`) return vmIndex } else { console.log('📍Wrong VM index, please check your configs! Exiting...') process.exit(1) } } } export function getDevnetId() { const devnetFolders = process.cwd().split('/') const ids = devnetFolders[devnetFolders.length - 1].split('-') return ids[1] } export async function loadDevnetConfig(devnetType) { return yaml.load( fs.readFileSync(`./${devnetType}-setup-config.yaml`, 'utf-8') ) } export async function editMaticCliRemoteYAMLConfig() { console.log('📍Editing matic-cli remote YAML configs...') const doc = await yaml.load( fs.readFileSync(`${process.cwd()}/remote-setup-config.yaml`, 'utf8'), undefined ) setCommonConfigs(doc) setConfigList('devnetBorHosts', process.env.DEVNET_BOR_HOSTS, doc) setConfigList('devnetHeimdallHosts', process.env.DEVNET_BOR_HOSTS, doc) setConfigList('devnetBorUsers', process.env.DEVNET_BOR_USERS, doc) setConfigList('devnetHeimdallUsers', process.env.DEVNET_BOR_USERS, doc) setConfigValue('devnetType', 'remote', doc) fs.writeFile( `${process.cwd()}/remote-setup-config.yaml`, yaml.dump(doc), (err) => { if (err) { console.log('❌ Error while writing remote YAML configs: \n', err) process.exit(1) } } ) } export async function editMaticCliDockerYAMLConfig() { console.log('📍Editing matic-cli docker YAML configs...') const doc = await yaml.load( fs.readFileSync(`${process.cwd()}/docker-setup-config.yaml`, 'utf8'), undefined ) setCommonConfigs(doc) setEthHostUser('ubuntu', doc) setConfigList('devnetBorHosts', process.env.DEVNET_BOR_HOSTS, doc) setConfigValue('devnetBorUsers', process.env.DEVNET_BOR_USERS, doc) setConfigValue('devnetType', 'docker', doc) setEthURL('ganache', doc) fs.writeFile( `${process.cwd()}/docker-setup-config.yaml`, yaml.dump(doc), (err) => { if (err) { console.log('❌ Error while writing docker YAML configs: \n', err) process.exit(1) } } ) } export async function validateConfigs() { validateEnvVars() validateAwsKeyAndCertificate() validateUsersAndHosts() validateBlockParams() validateGitConfigs() }