From c3505272e7afbe0faf7b5a901803079abf9526e1 Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Tue, 19 Nov 2024 10:17:01 -0600 Subject: [PATCH 01/18] fix: node logs command flags Signed-off-by: Jeffrey Tang --- src/commands/node/configs.ts | 3 ++- src/commands/node/flags.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/node/configs.ts b/src/commands/node/configs.ts index 06d947932..2cd16a56a 100644 --- a/src/commands/node/configs.ts +++ b/src/commands/node/configs.ts @@ -238,7 +238,8 @@ export const logsConfigBuilder = function (argv, ctx, task) { /** @type {{namespace: string, nodeAliases: NodeAliases}} */ const config = { namespace: this.configManager.getFlag(flags.namespace), - nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)) + nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)), + nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed) } ctx.config = config return config diff --git a/src/commands/node/flags.ts b/src/commands/node/flags.ts index 1a08160ec..9fc60789f 100644 --- a/src/commands/node/flags.ts +++ b/src/commands/node/flags.ts @@ -179,7 +179,7 @@ export const ADD_EXECUTE_FLAGS = { } export const LOGS_FLAGS = { - requiredFlags: [flags.nodeAliasesUnparsed], + requiredFlags: [flags.namespace, flags.nodeAliasesUnparsed], requiredFlagsWithDisabledPrompt: [], optionalFlags: [] } From edfe271bf021be35a306fbd82ae8bc6371c1d12e Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Tue, 19 Nov 2024 10:44:33 -0600 Subject: [PATCH 02/18] Fix: log download path, add unit test Signed-off-by: Jeffrey Tang --- src/core/helpers.ts | 2 +- test/test_util.ts | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/core/helpers.ts b/src/core/helpers.ts index f715de9a2..7c3525404 100644 --- a/src/core/helpers.ts +++ b/src/core/helpers.ts @@ -206,7 +206,7 @@ async function getNodeLog (pod: V1Pod, namespace: string, timeString: string, k8 await k8.copyTo(podName, ROOT_CONTAINER, sourcePath, `${HEDERA_HAPI_PATH}`) await k8.execContainer(podName, ROOT_CONTAINER, `chmod 0755 ${HEDERA_HAPI_PATH}/${scriptName}`) await k8.execContainer(podName, ROOT_CONTAINER, `${HEDERA_HAPI_PATH}/${scriptName}`) - await k8.copyFrom(podName, ROOT_CONTAINER, `${HEDERA_HAPI_PATH}/${podName}.zip`, targetDir) + await k8.copyFrom(podName, ROOT_CONTAINER, `${HEDERA_HAPI_PATH}/data/${podName}.zip`, targetDir) } catch (e: Error | any) { // not throw error here, so we can continue to finish downloading logs from other pods // and also delete namespace in the end diff --git a/test/test_util.ts b/test/test_util.ts index 0f9145c06..d4e88dd7d 100644 --- a/test/test_util.ts +++ b/test/test_util.ts @@ -52,7 +52,7 @@ import { AccountCreateTransaction, Hbar, HbarUnit, PrivateKey } from '@hashgraph/sdk' -import { MINUTES, ROOT_CONTAINER, SECONDS } from '../src/core/constants.ts' +import { MINUTES, ROOT_CONTAINER, SECONDS, SOLO_LOGS_DIR } from '../src/core/constants.ts' import crypto from 'crypto' import { AccountCommand } from '../src/commands/account.ts' import { SoloError } from '../src/core/errors.ts' @@ -292,6 +292,20 @@ export function e2eTestSuite ( expect.fail() } }).timeout(30 * MINUTES) + + it('node log command should work', async () => { + try { + await expect(nodeCmd.handlers.logs(argv)).to.eventually.be.ok + + const soloLogPath = path.join(SOLO_LOGS_DIR, 'solo.log') + const soloLog = fs.readFileSync(soloLogPath, 'utf8') + + expect(soloLog).to.not.contain('failed to download logs') + } catch (e) { + nodeCmd.logger.showUserError(e) + expect.fail() + } + }).timeout(30 * MINUTES) } }) From c181db259aa1c67d42663a7ec0eecca2db81ee2b Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Tue, 19 Nov 2024 12:40:11 -0600 Subject: [PATCH 03/18] Add command to download state files Signed-off-by: Jeffrey Tang --- src/commands/node/configs.ts | 11 ++++++++++ src/commands/node/flags.ts | 6 ++++++ src/commands/node/handlers.ts | 28 ++++++++++++++++++++++++-- src/commands/node/index.ts | 7 +++++++ src/commands/node/tasks.ts | 10 ++++++++- src/core/helpers.ts | 38 +++++++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/commands/node/configs.ts b/src/commands/node/configs.ts index 2cd16a56a..50ac7b5f4 100644 --- a/src/commands/node/configs.ts +++ b/src/commands/node/configs.ts @@ -245,6 +245,17 @@ export const logsConfigBuilder = function (argv, ctx, task) { return config } +export const statesConfigBuilder = function (argv, ctx, task) { + /** @type {{namespace: string, nodeAliases: NodeAliases}} */ + const config = { + namespace: this.configManager.getFlag(flags.namespace), + nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)), + nodeAliasesUnparsed: this.configManager.getFlag(flags.nodeAliasesUnparsed) + } + ctx.config = config + return config +} + export const refreshConfigBuilder = async function (argv, ctx, task) { ctx.config = this.getConfig(REFRESH_CONFIGS_NAME, argv.flags, [ diff --git a/src/commands/node/flags.ts b/src/commands/node/flags.ts index 9fc60789f..59c3d5de7 100644 --- a/src/commands/node/flags.ts +++ b/src/commands/node/flags.ts @@ -184,6 +184,12 @@ export const LOGS_FLAGS = { optionalFlags: [] } +export const STATES_FLAGS = { + requiredFlags: [flags.namespace, flags.nodeAliasesUnparsed], + requiredFlagsWithDisabledPrompt: [], + optionalFlags: [] +} + export const REFRESH_FLAGS = { requiredFlags: [ flags.cacheDir, diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index 0debe3553..f86067e93 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -18,8 +18,17 @@ import * as helpers from '../../core/helpers.ts' import * as NodeFlags from './flags.ts' import { - addConfigBuilder, deleteConfigBuilder, downloadGeneratedFilesConfigBuilder, keysConfigBuilder, logsConfigBuilder, - prepareUpgradeConfigBuilder, refreshConfigBuilder, setupConfigBuilder, startConfigBuilder, stopConfigBuilder, + addConfigBuilder, + deleteConfigBuilder, + downloadGeneratedFilesConfigBuilder, + keysConfigBuilder, + logsConfigBuilder, + prepareUpgradeConfigBuilder, + refreshConfigBuilder, + setupConfigBuilder, + startConfigBuilder, + statesConfigBuilder, + stopConfigBuilder, updateConfigBuilder } from './configs.ts' import { @@ -501,6 +510,21 @@ export class NodeCommandHandlers { return true } + async states (argv: any) { + argv = helpers.addFlagsToArgv(argv, NodeFlags.STATES_FLAGS) + + const action = helpers.commandActionBuilder([ + this.tasks.initialize(argv, statesConfigBuilder.bind(this), null), + this.tasks.getNodeStateFiles() + ], { + concurrent: false, + rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION + }, 'Error in downloading states from nodes', null) + + await action(argv, this) + return true + } + async refresh (argv: any) { argv = helpers.addFlagsToArgv(argv, NodeFlags.REFRESH_FLAGS) diff --git a/src/commands/node/index.ts b/src/commands/node/index.ts index 0addc10c9..1b08020d3 100644 --- a/src/commands/node/index.ts +++ b/src/commands/node/index.ts @@ -137,6 +137,13 @@ export class NodeCommand extends BaseCommand { handler: 'logs' }, NodeFlags.LOGS_FLAGS)) + .command(new YargsCommand({ + command: 'states', + description: 'Download hedera states from the network nodes and stores them in /// directory', + commandDef: nodeCmd, + handler: 'states' + }, NodeFlags.STATES_FLAGS)) + .command(new YargsCommand({ command: 'add', description: 'Adds a node with a specific version of Hedera platform', diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index f97de1232..ee5ed81d6 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -55,7 +55,7 @@ import crypto from 'crypto' import { addDebugOptions, getNodeAccountMap, - getNodeLogs, + getNodeLogs, getNodeStatesFromPod, prepareEndpoints, renameAndCopyFile, sleep, @@ -915,6 +915,14 @@ export class NodeCommandTasks { }) } + getNodeStateFiles () { + return new Task('Get node states', async (ctx: any, task: ListrTaskWrapper) => { + for (const nodeAlias of ctx.config.nodeAliases) { + await getNodeStatesFromPod(this.k8, ctx.config.namespace, nodeAlias) + } + }) + } + checkPVCsEnabled () { return new Task('Check that PVCs are enabled', (ctx: any, task: ListrTaskWrapper) => { if (!this.configManager.getFlag(flags.persistentVolumeClaims)) { diff --git a/src/core/helpers.ts b/src/core/helpers.ts index 7c3525404..80d6e66a4 100644 --- a/src/core/helpers.ts +++ b/src/core/helpers.ts @@ -215,6 +215,44 @@ async function getNodeLog (pod: V1Pod, namespace: string, timeString: string, k8 k8.logger.debug(`getNodeLogs(${pod.metadata.name}): ...end`) } + +/** + * Download state files from a pod + * @param k8 - an instance of core/K8 + * @param namespace - the namespace of the network + * @param nodeAlias - the pod name + * @returns a promise that resolves when the state files are downloaded + */ +export async function getNodeStatesFromPod (k8: K8, namespace: string, nodeAlias: string) { + const pods = await k8.getPodsByLabel([`solo.hedera.com/node-name=${nodeAlias}`]) + // get length of pods + const promises = [] + for (const pod of pods) { + promises.push(getNodeState(pod, namespace, k8)) + } + return await Promise.all(promises) +} + + +async function getNodeState (pod: V1Pod, namespace: string, k8: K8){ + const podName = pod.metadata!.name as PodName + k8.logger.debug(`getNodeState(${pod.metadata.name}): begin...`) + const targetDir = path.join(SOLO_LOGS_DIR, namespace) + try { + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }) + } + const zipCommand = `tar -czf ${HEDERA_HAPI_PATH}/${podName}.zip -C ${HEDERA_HAPI_PATH}/data/saved .` + await k8.execContainer(podName, ROOT_CONTAINER, zipCommand) + await k8.copyFrom(podName, ROOT_CONTAINER, `${HEDERA_HAPI_PATH}/${podName}.zip`, targetDir) + } catch (e: Error | any) { + k8.logger.error(`failed to download state from pod ${podName}`, e) + k8.logger.showUser(`Failed to download state from pod ${podName}` + e) + } + k8.logger.debug(`getNodeState(${pod.metadata.name}): ...end`) +} + + /** * Create a map of node aliases to account IDs * @param nodeAliases From e3ad3abd1af194262cdd27a0e6cdd9bc06d1d448 Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Tue, 19 Nov 2024 20:30:42 -0600 Subject: [PATCH 04/18] feat: add flag to upload state Signed-off-by: Jeffrey Tang --- resources/templates/settings.txt | 1 + src/commands/flags.ts | 11 +++++++++ src/commands/mirror_node.ts | 8 +++---- src/commands/node/flags.ts | 3 ++- src/commands/node/handlers.ts | 3 +++ src/commands/node/tasks.ts | 21 +++++++++++++++++ src/core/helpers.ts | 4 ++-- test/e2e/commands/node_local_hedera.test.ts | 26 +++++++++++++++++---- 8 files changed, 66 insertions(+), 11 deletions(-) diff --git a/resources/templates/settings.txt b/resources/templates/settings.txt index 3b34a834b..10cb13438 100644 --- a/resources/templates/settings.txt +++ b/resources/templates/settings.txt @@ -12,3 +12,4 @@ crypto.enableNewKeyStoreModel, true # TODO: remove this? only defaults to true when going from 0.52 to 0.53 event.migrateEventHashing, false +state.saveStatePeriod, 60 diff --git a/src/commands/flags.ts b/src/commands/flags.ts index 99ead3899..9fd508827 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -182,6 +182,16 @@ export const deployJsonRpcRelay: CommandFlag = { } } +export const stateFile: CommandFlag = { + constName: 'stateFile', + name: 'state-file', + definition: { + describe: 'A zipped state file to be used for the network', + defaultValue: '', + type: 'string' + } +} + export const releaseTag: CommandFlag = { constName: 'releaseTag', name: 'release-tag', @@ -817,6 +827,7 @@ export const allFlags: CommandFlag[] = [ relayReleaseTag, releaseTag, replicaCount, + stateFile, setAlias, settingTxt, tlsClusterIssuerType, diff --git a/src/commands/mirror_node.ts b/src/commands/mirror_node.ts index 21facbd69..42bda272f 100644 --- a/src/commands/mirror_node.ts +++ b/src/commands/mirror_node.ts @@ -337,9 +337,9 @@ export class MirrorNodeCommand extends BaseCommand { try { await tasks.run() - self.logger.debug('node start has completed') + self.logger.debug('mirror node depolyment has completed') } catch (e: Error | any) { - throw new SoloError(`Error starting node: ${e.message}`, e) + throw new SoloError(`Error deploying node: ${e.message}`, e) } finally { await lease.release() await self.accountManager.close() @@ -428,9 +428,9 @@ export class MirrorNodeCommand extends BaseCommand { try { await tasks.run() - self.logger.debug('node start has completed') + self.logger.debug('mirror node destruction has completed') } catch (e: Error | any) { - throw new SoloError(`Error starting node: ${e.message}`, e) + throw new SoloError(`Error destrong mirror node: ${e.message}`, e) } finally { await lease.release() await self.accountManager.close() diff --git a/src/commands/node/flags.ts b/src/commands/node/flags.ts index 59c3d5de7..97ef9a98c 100644 --- a/src/commands/node/flags.ts +++ b/src/commands/node/flags.ts @@ -244,7 +244,8 @@ export const START_FLAGS = { optionalFlags: [ flags.quiet, flags.nodeAliasesUnparsed, - flags.debugNodeAlias + flags.debugNodeAlias, + flags.stateFile, ] } diff --git a/src/commands/node/handlers.ts b/src/commands/node/handlers.ts index f86067e93..978688d7e 100644 --- a/src/commands/node/handlers.ts +++ b/src/commands/node/handlers.ts @@ -593,6 +593,9 @@ export class NodeCommandHandlers { const action = helpers.commandActionBuilder([ this.tasks.initialize(argv, startConfigBuilder.bind(this), lease), this.tasks.identifyExistingNodes(), + this.tasks.uploadStateFiles( + (ctx: any) => ctx.config.stateFile.length === 0 + ), this.tasks.startNodes('nodeAliases'), this.tasks.enablePortForwarding(), this.tasks.checkAllNodesAreActive('nodeAliases'), diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index ee5ed81d6..0e322ea84 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -661,6 +661,27 @@ export class NodeCommandTasks { }) } + uploadStateFiles (skip: Function | boolean) { + return new Task('Upload state files network nodes', async (ctx: any, task: ListrTaskWrapper) => { + const config = ctx.config + + const zipFile = config.stateFile + this.logger.debug(`zip file: ${zipFile}`) + await sleep(5 * SECONDS) + for (const nodeAlias of ctx.config.nodeAliases) { + const podName = ctx.config.podNames[nodeAlias] + this.logger.debug(`Uploading state files to pod ${podName}`) + // use k8 command to upload the zip file to the pod ${constants.HEDERA_HAPI_PATH}/data and use tar to extract the files + await this.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) + + this.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) + await this.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) + await this.k8.execContainer(podName, constants.ROOT_CONTAINER, + ['tar', '-xvf', `${constants.HEDERA_HAPI_PATH}/data/${path.basename(zipFile)}`, '-C', `${constants.HEDERA_HAPI_PATH}/data/saved`]) + } + }, skip) + } + identifyNetworkPods () { return new Task('Identify network pods', (ctx: any, task: ListrTaskWrapper) => { return this.taskCheckNetworkNodePods(ctx, task, ctx.config.nodeAliases) diff --git a/src/core/helpers.ts b/src/core/helpers.ts index 80d6e66a4..f1d0087de 100644 --- a/src/core/helpers.ts +++ b/src/core/helpers.ts @@ -242,9 +242,9 @@ async function getNodeState (pod: V1Pod, namespace: string, k8: K8){ if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }) } - const zipCommand = `tar -czf ${HEDERA_HAPI_PATH}/${podName}.zip -C ${HEDERA_HAPI_PATH}/data/saved .` + const zipCommand = `tar -czf ${HEDERA_HAPI_PATH}/${podName}-state.zip -C ${HEDERA_HAPI_PATH}/data/saved .` await k8.execContainer(podName, ROOT_CONTAINER, zipCommand) - await k8.copyFrom(podName, ROOT_CONTAINER, `${HEDERA_HAPI_PATH}/${podName}.zip`, targetDir) + await k8.copyFrom(podName, ROOT_CONTAINER, `${HEDERA_HAPI_PATH}/${podName}-state.zip`, targetDir) } catch (e: Error | any) { k8.logger.error(`failed to download state from pod ${podName}`, e) k8.logger.showUser(`Failed to download state from pod ${podName}` + e) diff --git a/test/e2e/commands/node_local_hedera.test.ts b/test/e2e/commands/node_local_hedera.test.ts index 8fe9f3176..6157d859e 100644 --- a/test/e2e/commands/node_local_hedera.test.ts +++ b/test/e2e/commands/node_local_hedera.test.ts @@ -22,9 +22,10 @@ import { getDefaultArgv, TEST_CLUSTER } from '../../test_util.ts' -import { getNodeLogs } from '../../../src/core/helpers.ts' -import { MINUTES } from '../../../src/core/constants.ts' +import { sleep } from '../../../src/core/helpers.ts' +import { MINUTES, SOLO_LOGS_DIR } from '../../../src/core/constants.ts' import type { K8 } from '../../../src/core/index.ts' +import path from 'path' const LOCAL_HEDERA = 'local-hedera-app' const argv = getDefaultArgv() @@ -42,12 +43,29 @@ argv[flags.localBuildPath.name] = 'node1=../hedera-services/hedera-node/data/,.. argv[flags.namespace.name] = LOCAL_HEDERA e2eTestSuite(LOCAL_HEDERA, argv, undefined, undefined, undefined, undefined, undefined, undefined, true, (bootstrapResp) => { + const nodeCmd = bootstrapResp.cmd.nodeCmd + const accountCmd = bootstrapResp.cmd.accountCmd describe('Node for hedera app should have started successfully', () => { hederaK8 = bootstrapResp.opts.k8 + it('save the state and restart the node with saved state', async function () { + // create some transactions to save more round of states + await accountCmd.create(argv) + await sleep(3) + await accountCmd.create(argv) + await sleep(3) + + // stop network and save the state + await nodeCmd.handlers.stop(argv) + await nodeCmd.handlers.states(argv) + + argv[flags.stateFile.name] = path.join(SOLO_LOGS_DIR, LOCAL_HEDERA, 'network-node1-0-state.zip') + await nodeCmd.handlers.start(argv) + }).timeout(10 * MINUTES) + it('get the logs and delete the namespace', async function () { - await getNodeLogs(hederaK8, LOCAL_HEDERA) - await hederaK8.deleteNamespace(LOCAL_HEDERA) + // await getNodeLogs(hederaK8, LOCAL_HEDERA) + // await hederaK8.deleteNamespace(LOCAL_HEDERA) }).timeout(10 * MINUTES) }) }) From 137f8a5cd5d718cb274749a23cafd1d3744dae79 Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Tue, 19 Nov 2024 20:35:12 -0600 Subject: [PATCH 05/18] revert Signed-off-by: Jeffrey Tang --- src/commands/node/configs.ts | 2 +- src/commands/node/tasks.ts | 2 -- test/e2e/commands/node_local_hedera.test.ts | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/commands/node/configs.ts b/src/commands/node/configs.ts index 50ac7b5f4..b0f017837 100644 --- a/src/commands/node/configs.ts +++ b/src/commands/node/configs.ts @@ -246,7 +246,7 @@ export const logsConfigBuilder = function (argv, ctx, task) { } export const statesConfigBuilder = function (argv, ctx, task) { - /** @type {{namespace: string, nodeAliases: NodeAliases}} */ + /** @type {{namespace: string, nodeAliases: NodeAliases, nodeAliasesUnparsed:string}} */ const config = { namespace: this.configManager.getFlag(flags.namespace), nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed)), diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 0e322ea84..e372bcb36 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -667,11 +667,9 @@ export class NodeCommandTasks { const zipFile = config.stateFile this.logger.debug(`zip file: ${zipFile}`) - await sleep(5 * SECONDS) for (const nodeAlias of ctx.config.nodeAliases) { const podName = ctx.config.podNames[nodeAlias] this.logger.debug(`Uploading state files to pod ${podName}`) - // use k8 command to upload the zip file to the pod ${constants.HEDERA_HAPI_PATH}/data and use tar to extract the files await this.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) this.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) diff --git a/test/e2e/commands/node_local_hedera.test.ts b/test/e2e/commands/node_local_hedera.test.ts index 6157d859e..24e81f844 100644 --- a/test/e2e/commands/node_local_hedera.test.ts +++ b/test/e2e/commands/node_local_hedera.test.ts @@ -64,8 +64,8 @@ e2eTestSuite(LOCAL_HEDERA, argv, undefined, undefined, undefined, undefined, und }).timeout(10 * MINUTES) it('get the logs and delete the namespace', async function () { - // await getNodeLogs(hederaK8, LOCAL_HEDERA) - // await hederaK8.deleteNamespace(LOCAL_HEDERA) + await getNodeLogs(hederaK8, LOCAL_HEDERA) + await hederaK8.deleteNamespace(LOCAL_HEDERA) }).timeout(10 * MINUTES) }) }) From fd8763f5c6203cfb8a482060dd14d8c7d0615701 Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Thu, 21 Nov 2024 08:53:20 -0600 Subject: [PATCH 06/18] update readme Signed-off-by: Jeffrey Tang --- README.md.template | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md.template b/README.md.template index ccb9f3f0d..f756851e2 100644 --- a/README.md.template +++ b/README.md.template @@ -402,6 +402,31 @@ solo node setup -i node1,node2,node3,node4 --local-build-path ../hedera-services solo node start -i node1,node2,node3,node4 -n "${SOLO_NAMESPACE}" solo node delete --node-alias node2 --debug-node-alias node3 -n "${SOLO_NAMESPACE}" ``` +## Save and reuse network state files + +With the following command you can save the network state to a file. +```bash +# must stop hedera node operation first +npm run solo-test -- node stop -i node1,node2 -n solo-e2e + +# download state file to default location at ~/.solo/logs/ +npm run solo-test -- node states -i node1,node2 -n solo-e2e +``` + +By default the state files are saved under `~/solo` directory + +```bash +└── logs + ├── solo-e2e + │   ├── network-node1-0-state.zip + │   └── network-node2-0-state.zip + └── solo.log +``` + +Later, user can use the following command to upload the state files to the network and restart hedera nodes. +```bash +npm run solo-test -- node start -i node1,node2 -n solo-e2e --state-file network-node1-0-state.zip +``` ## Support From 2638097bb8f12fff2bc3275d2db2490d988275f8 Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Thu, 21 Nov 2024 09:24:20 -0600 Subject: [PATCH 07/18] update Readme Signed-off-by: Jeffrey Tang --- README.md.template | 56 +++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/README.md.template b/README.md.template index f756851e2..0bd9e2cfb 100644 --- a/README.md.template +++ b/README.md.template @@ -18,11 +18,16 @@ An opinionated CLI tool to deploy and manage standalone test networks. * [Requirements](#requirements) * [Setup](#setup) * [Install Solo](#install-solo) -* [Setup Kubernetes cluster](#setup-kubernetes-cluster) -* [Generate Node Keys](#generate-node-keys) - * [Standard keys (.pem file)](#standard-keys-pem-file) -* [Examples](#examples) - * [Example - 1: Deploy a standalone test network (version `0.54.0-alpha.4`)](#example---1-deploy-a-standalone-test-network-version-0540-alpha4) +* [Quick Start Guide](#quick-start-guide) +* [Advanced User Guide](#advanced-user-guide) + * [Setup Kubernetes cluster](#setup-kubernetes-cluster) + * [Step by Step Instructions](#step-by-step-instructions) +* [For Hashgraph Developers](#for-hashgraph-developers) + * [For Developers Working on Hedera Service Repo](#for-developers-working-on-hedera-service-repo) + * [For Developers Working on Platform core](#for-developers-working-on-platform-core) + * [Using IntelliJ remote debug with Solo](#using-intellij-remote-debug-with-solo) + * [Retrieving Logs](#retrieving-logs) + * [Save and reuse network state files](#save-and-reuse-network-state-files) * [Support](#support) * [Contributing](#contributing) * [Code of Conduct](#code-of-conduct) @@ -53,9 +58,29 @@ nvm use lts/hydrogen * Run `npm install -g @hashgraph/solo` -## Setup Kubernetes cluster +## Quick Start Guide +User can use one of the following three command to quickly deploy a standalone test network. -### Remote cluster +```bash +# Option 1) deploy solo network with two nodes +task default + +# Option 2) deploy solo network with two nodes, and mirror node +task default-with-mirror + +# Option 3) deploy solo network with two nodes, mirror node, and JSON RPC relay +task default-with-relay +``` +To tear down the solo network +```bash +task clean +``` + +## Advanced User Guide +For those who would like to have more control or need some customized setups, here are some step by step instructions of how to setup and deploy a solo network. +### Setup Kubernetes cluster + +#### Remote cluster * You may use remote kubernetes cluster. In this case, ensure kubernetes context is set up correctly. @@ -63,7 +88,7 @@ nvm use lts/hydrogen kubectl config use-context ``` -### Local cluster +#### Local cluster * You may use [kind](https://kind.sigs.k8s.io/) or [microk8s](https://microk8s.io/) to create a cluster. In this case, ensure your Docker engine has enough resources (e.g. Memory >=8Gb, CPU: >=4). Below we show how you can use `kind` to create a cluster @@ -116,9 +141,8 @@ You may now view pods in your cluster using `k9s -A` as below: └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` -## Examples -### Example - 1: Deploy a standalone test network (version `0.54.0-alpha.4`) +### Step by Step Instructions * Initialize `solo` directories: @@ -318,8 +342,8 @@ Example output ``` $SOLO_RELAY_DEPLOY_OUTPUT ``` - -## For Developers Working on Hedera Service Repo +## For Hashgraph Developers +### For Developers Working on Hedera Service Repo First, please clone hedera service repo `https://github.com/hashgraph/hedera-services/` and build the code with `./gradlew assemble`. If need to running nodes with different versions or releases, please duplicate the repo or build directories in @@ -335,7 +359,7 @@ solo node setup -i node1,node2,node3 -n "${SOLO_NAMESPACE}" --local-build-path < # example: solo node setup -i node1,node2,node3 -n "${SOLO_NAMESPACE}" --local-build-path node1=../hedera-services/hedera-node/data/,../hedera-services/hedera-node/data,node3=../hedera-services/hedera-node/data ``` -## For Developers Working on Platform core +### For Developers Working on Platform core To deploy node with local build PTT jar files, run the following command: ``` @@ -343,12 +367,12 @@ solo node setup -i node1,node2,node3 -n "${SOLO_NAMESPACE}" --local-build-path < # example: solo node setup -i node1,node2,node3 -n "${SOLO_NAMESPACE}" --local-build-path ../hedera-services/platform-sdk/sdk/data,node1=../hedera-services/platform-sdk/sdk/data,node2=../hedera-services/platform-sdk/sdk/data --app PlatformTestingTool.jar --app-config ../hedera-services/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/resources/FCMFCQ-Basic-2.5k-5m.json ``` -## Logs +### Retrieving Logs You can find log for running solo command under the directory `~/.solo/logs/` The file `solo.log` contains the logs for the solo command. The file `hashgraph-sdk.log` contains the logs from Solo client when sending transactions to network nodes. -## Using IntelliJ remote debug with Solo +### Using IntelliJ remote debug with Solo NOTE: the hedera-services path referenced '../hedera-services/hedera-node/data' may need to be updated based on what directory you are currently in. This also assumes that you have done an assemble/build and the directory contents are up-to-date. @@ -402,7 +426,7 @@ solo node setup -i node1,node2,node3,node4 --local-build-path ../hedera-services solo node start -i node1,node2,node3,node4 -n "${SOLO_NAMESPACE}" solo node delete --node-alias node2 --debug-node-alias node3 -n "${SOLO_NAMESPACE}" ``` -## Save and reuse network state files +### Save and reuse network state files With the following command you can save the network state to a file. ```bash From f8a4506d786f658c18aa32da842f6131541c7f18 Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:36:25 -0600 Subject: [PATCH 08/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 3e2bb7573..dd65e5e24 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -663,6 +663,7 @@ export class NodeCommandTasks { } uploadStateFiles (skip: Function | boolean) { + const self = this return new Task('Upload state files network nodes', async (ctx: any, task: ListrTaskWrapper) => { const config = ctx.config From 073c6902e702cd96291fbf3b1b77d20480267748 Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:36:35 -0600 Subject: [PATCH 09/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index dd65e5e24..c525fc1c8 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -668,7 +668,7 @@ export class NodeCommandTasks { const config = ctx.config const zipFile = config.stateFile - this.logger.debug(`zip file: ${zipFile}`) + self.logger.debug(`zip file: ${zipFile}`) for (const nodeAlias of ctx.config.nodeAliases) { const podName = ctx.config.podNames[nodeAlias] this.logger.debug(`Uploading state files to pod ${podName}`) From 5469f8b6f2856c79df3f4aadad0f8a5818943c8f Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:36:45 -0600 Subject: [PATCH 10/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index c525fc1c8..0a8e99e11 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -671,7 +671,7 @@ export class NodeCommandTasks { self.logger.debug(`zip file: ${zipFile}`) for (const nodeAlias of ctx.config.nodeAliases) { const podName = ctx.config.podNames[nodeAlias] - this.logger.debug(`Uploading state files to pod ${podName}`) + self.logger.debug(`Uploading state files to pod ${podName}`) await this.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) this.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) From c90f3299bd87c6f67928b66437fa14e90bf5fb09 Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:36:55 -0600 Subject: [PATCH 11/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 0a8e99e11..6736632cc 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -672,7 +672,7 @@ export class NodeCommandTasks { for (const nodeAlias of ctx.config.nodeAliases) { const podName = ctx.config.podNames[nodeAlias] self.logger.debug(`Uploading state files to pod ${podName}`) - await this.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) + await self.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) this.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) await this.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) From 3de5297276d52660cbbfac09cf4dbd241f7747ad Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:37:22 -0600 Subject: [PATCH 12/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 6736632cc..d32865cd3 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -674,7 +674,7 @@ export class NodeCommandTasks { self.logger.debug(`Uploading state files to pod ${podName}`) await self.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) - this.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) + sef.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) await this.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) await this.k8.execContainer(podName, constants.ROOT_CONTAINER, ['tar', '-xvf', `${constants.HEDERA_HAPI_PATH}/data/${path.basename(zipFile)}`, '-C', `${constants.HEDERA_HAPI_PATH}/data/saved`]) From 9aa80a23f4c401f3978f7e9e533e9b8a6436e10f Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:37:32 -0600 Subject: [PATCH 13/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index d32865cd3..f73e51e9d 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -675,7 +675,7 @@ export class NodeCommandTasks { await self.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) sef.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) - await this.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) + await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) await this.k8.execContainer(podName, constants.ROOT_CONTAINER, ['tar', '-xvf', `${constants.HEDERA_HAPI_PATH}/data/${path.basename(zipFile)}`, '-C', `${constants.HEDERA_HAPI_PATH}/data/saved`]) } From 57a3046f4d49989b9e012f2665d3e06f52b247dd Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:37:40 -0600 Subject: [PATCH 14/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index f73e51e9d..15533e088 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -676,7 +676,7 @@ export class NodeCommandTasks { sef.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) - await this.k8.execContainer(podName, constants.ROOT_CONTAINER, + await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['tar', '-xvf', `${constants.HEDERA_HAPI_PATH}/data/${path.basename(zipFile)}`, '-C', `${constants.HEDERA_HAPI_PATH}/data/saved`]) } }, skip) From 49c4bfb5505a4e5adf41be3574d61fccd7b1c2cb Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:37:53 -0600 Subject: [PATCH 15/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 15533e088..5d9634202 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -937,6 +937,7 @@ export class NodeCommandTasks { } getNodeStateFiles () { + const self = this return new Task('Get node states', async (ctx: any, task: ListrTaskWrapper) => { for (const nodeAlias of ctx.config.nodeAliases) { await getNodeStatesFromPod(this.k8, ctx.config.namespace, nodeAlias) From c5a61e527ba960792d4d7a8cb6b83cc9cecfe45d Mon Sep 17 00:00:00 2001 From: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:38:03 -0600 Subject: [PATCH 16/18] Update src/commands/node/tasks.ts Co-authored-by: Jeromy Cannon Signed-off-by: JeffreyDallas <39912573+JeffreyDallas@users.noreply.github.com> --- src/commands/node/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 5d9634202..6e4c877fb 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -940,7 +940,7 @@ export class NodeCommandTasks { const self = this return new Task('Get node states', async (ctx: any, task: ListrTaskWrapper) => { for (const nodeAlias of ctx.config.nodeAliases) { - await getNodeStatesFromPod(this.k8, ctx.config.namespace, nodeAlias) + await getNodeStatesFromPod(self.k8, ctx.config.namespace, nodeAlias) } }) } From 9cede767fc66c50567996351b5fbe5b5c0ecd357 Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Fri, 22 Nov 2024 11:19:49 -0600 Subject: [PATCH 17/18] add logic to read back balance Signed-off-by: Jeffrey Tang --- README.md.template | 13 +++++++-- src/commands/node/tasks.ts | 2 +- test/e2e/commands/node_local_hedera.test.ts | 31 ++++++++++++++++++++- test/test_util.ts | 6 ++++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/README.md.template b/README.md.template index 0bd9e2cfb..b8984d729 100644 --- a/README.md.template +++ b/README.md.template @@ -18,7 +18,7 @@ An opinionated CLI tool to deploy and manage standalone test networks. * [Requirements](#requirements) * [Setup](#setup) * [Install Solo](#install-solo) -* [Quick Start Guide](#quick-start-guide) +* [Use the Task tool to launch Solo](#use-the-task-tool-to-launch-solo) * [Advanced User Guide](#advanced-user-guide) * [Setup Kubernetes cluster](#setup-kubernetes-cluster) * [Step by Step Instructions](#step-by-step-instructions) @@ -58,8 +58,15 @@ nvm use lts/hydrogen * Run `npm install -g @hashgraph/solo` -## Quick Start Guide -User can use one of the following three command to quickly deploy a standalone test network. +## Use the Task tool to launch Solo + +First, please use the following steps to install dependencies and build solo project. + +```bash +npm ci +npm run build +``` +Then user can use one of the following three command to quickly deploy a standalone test network. ```bash # Option 1) deploy solo network with two nodes diff --git a/src/commands/node/tasks.ts b/src/commands/node/tasks.ts index 1453538d4..5231af54a 100644 --- a/src/commands/node/tasks.ts +++ b/src/commands/node/tasks.ts @@ -686,7 +686,7 @@ export class NodeCommandTasks { self.logger.debug(`Uploading state files to pod ${podName}`) await self.k8.copyTo(podName, constants.ROOT_CONTAINER, zipFile, `${constants.HEDERA_HAPI_PATH}/data`) - sef.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) + self.logger.info(`Deleting the previous state files in pod ${podName} directory ${constants.HEDERA_HAPI_PATH}/data/saved`) await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/saved/*`]) await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['tar', '-xvf', `${constants.HEDERA_HAPI_PATH}/data/${path.basename(zipFile)}`, '-C', `${constants.HEDERA_HAPI_PATH}/data/saved`]) diff --git a/test/e2e/commands/node_local_hedera.test.ts b/test/e2e/commands/node_local_hedera.test.ts index 06d5fb971..ee2a6c3c1 100644 --- a/test/e2e/commands/node_local_hedera.test.ts +++ b/test/e2e/commands/node_local_hedera.test.ts @@ -26,6 +26,8 @@ import { getNodeLogs, sleep } from '../../../src/core/helpers.js' import { MINUTES, SOLO_LOGS_DIR } from '../../../src/core/constants.js' import type { K8 } from '../../../src/core/index.js' import path from 'path' +import { expect } from 'chai' +import { AccountBalanceQuery, AccountCreateTransaction, Hbar, HbarUnit, PrivateKey } from '@hashgraph/sdk' const LOCAL_HEDERA = 'local-hedera-app' const argv = getDefaultArgv() @@ -45,11 +47,30 @@ argv[flags.namespace.name] = LOCAL_HEDERA e2eTestSuite(LOCAL_HEDERA, argv, undefined, undefined, undefined, undefined, undefined, undefined, true, (bootstrapResp) => { const nodeCmd = bootstrapResp.cmd.nodeCmd const accountCmd = bootstrapResp.cmd.accountCmd + const accountManager = bootstrapResp.manager.accountManager describe('Node for hedera app should have started successfully', () => { hederaK8 = bootstrapResp.opts.k8 it('save the state and restart the node with saved state', async function () { - // create some transactions to save more round of states + // create an account so later we can verify its balance after restart + await accountManager.loadNodeClient(LOCAL_HEDERA) + const privateKey = PrivateKey.generate() + // get random integer between 100 and 1000 + const amount = Math.floor(Math.random() * (1000 - 100) + 100) + + const newAccount = await new AccountCreateTransaction() + .setKey(privateKey) + .setInitialBalance(Hbar.from(amount, HbarUnit.Hbar)) + .execute(accountManager._nodeClient) + + // Get the new account ID + const getReceipt = await newAccount.getReceipt(accountManager._nodeClient) + const accountInfo = { + accountId: getReceipt.accountId.toString(), + balance: amount + } + + // create more transactions to save more round of states await accountCmd.create(argv) await sleep(3) await accountCmd.create(argv) @@ -61,6 +82,14 @@ e2eTestSuite(LOCAL_HEDERA, argv, undefined, undefined, undefined, undefined, und argv[flags.stateFile.name] = path.join(SOLO_LOGS_DIR, LOCAL_HEDERA, 'network-node1-0-state.zip') await nodeCmd.handlers.start(argv) + + // check balance of accountInfo.accountId + await accountManager.loadNodeClient(LOCAL_HEDERA) + const balance = await new AccountBalanceQuery() + .setAccountId(accountInfo.accountId) + .execute(accountManager._nodeClient) + + expect(balance.hbars).to.be.eql(Hbar.from(accountInfo.balance, HbarUnit.Hbar)) }).timeout(10 * MINUTES) it('get the logs and delete the namespace', async function () { diff --git a/test/test_util.ts b/test/test_util.ts index d30e0f399..14198d2dc 100644 --- a/test/test_util.ts +++ b/test/test_util.ts @@ -116,6 +116,9 @@ interface TestOpts { interface BootstrapResponse { namespace: string, opts: TestOpts, + manager: { + accountManager: AccountManager + }, cmd: { initCmd: InitCommand, clusterCmd: ClusterCommand, @@ -182,6 +185,9 @@ export function bootstrapTestVariables ( return { namespace, opts, + manager: { + accountManager + }, cmd: { initCmd, clusterCmd, From 5425f5c50fd41c4fca2827c485cc041fd05a3b5e Mon Sep 17 00:00:00 2001 From: Jeffrey Tang Date: Fri, 22 Nov 2024 11:33:09 -0600 Subject: [PATCH 18/18] update instruction Signed-off-by: Jeffrey Tang --- README.md.template | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md.template b/README.md.template index b8984d729..f72e10f07 100644 --- a/README.md.template +++ b/README.md.template @@ -60,13 +60,17 @@ nvm use lts/hydrogen ## Use the Task tool to launch Solo -First, please use the following steps to install dependencies and build solo project. +First, install the cluster tool `kind` with this [link](https://kind.sigs.k8s.io/docs/user/quick-start#installation) + +Then, install the task tool `task` with this [link](https://taskfile.dev/#/installation) + +Then, use the following steps to install dependencies and build solo project. ```bash npm ci npm run build ``` -Then user can use one of the following three command to quickly deploy a standalone test network. +Then, user can use one of the following three commands to quickly deploy a standalone solo network. ```bash # Option 1) deploy solo network with two nodes