UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

128 lines 6.9 kB
// SPDX-License-Identifier: Apache-2.0 import { AccountId, PrivateKey, PublicKey } from '@hiero-ledger/sdk'; import { GenesisNetworkNodeDataWrapper } from './genesis-network-node-data-wrapper.js'; import * as constants from '../constants.js'; import { GenesisNetworkRosterEntryDataWrapper } from './genesis-network-roster-entry-data-wrapper.js'; import { Templates } from '../templates.js'; import { SoloError } from '../errors/solo-error.js'; import { Flags as flags } from '../../commands/flags.js'; import { PathEx } from '../../business/utils/path-ex.js'; /** * Used to construct the nodes data and convert them to JSON */ export class GenesisNetworkDataConstructor { consensusNodes; keyManager; accountManager; keysDirectory; networkNodeServiceMap; adminPublicKeyMap; domainNamesMapping; nodes = {}; rosters = {}; initializationPromise; constructor(consensusNodes, keyManager, accountManager, keysDirectory, networkNodeServiceMap, adminPublicKeyMap, domainNamesMapping) { this.consensusNodes = consensusNodes; this.keyManager = keyManager; this.accountManager = accountManager; this.keysDirectory = keysDirectory; this.networkNodeServiceMap = networkNodeServiceMap; this.adminPublicKeyMap = adminPublicKeyMap; this.domainNamesMapping = domainNamesMapping; this.initializationPromise = (async () => { for (const consensusNode of consensusNodes) { let adminPublicKey; const networkNodeService = this.networkNodeServiceMap.get(consensusNode.name); const accountId = AccountId.fromString(networkNodeService.accountId); const namespace = networkNodeService.namespace; if (adminPublicKeyMap.has(consensusNode.name)) { try { if (PublicKey.fromStringED25519(adminPublicKeyMap.get(consensusNode.name))) { adminPublicKey = PublicKey.fromStringED25519(adminPublicKeyMap.get(consensusNode.name)); } } catch { // Ignore error } } try { // not found existing one, generate a new key, and save to k8s secret if (!adminPublicKey) { const newKey = PrivateKey.generateED25519(); adminPublicKey = newKey.publicKey; try { await this.accountManager.createOrReplaceAccountKeySecret(newKey, accountId, false, namespace); } catch { throw new SoloError(`failed to create secret for admin key of: ${accountId.toString()}`); } } const nodeDataWrapper = new GenesisNetworkNodeDataWrapper(+networkNodeService.nodeId, adminPublicKey, consensusNode.name); this.nodes[consensusNode.name] = nodeDataWrapper; nodeDataWrapper.accountId = accountId; const rosterDataWrapper = new GenesisNetworkRosterEntryDataWrapper(+networkNodeService.nodeId); this.rosters[consensusNode.name] = rosterDataWrapper; rosterDataWrapper.weight = this.nodes[consensusNode.name].weight = constants.HEDERA_NODE_DEFAULT_STAKE_AMOUNT; const externalPort = +constants.HEDERA_NODE_EXTERNAL_GOSSIP_PORT; // Add gossip endpoints nodeDataWrapper.addGossipEndpoint(networkNodeService.externalAddress, externalPort); rosterDataWrapper.addGossipEndpoint(networkNodeService.externalAddress, externalPort); const domainName = domainNamesMapping?.[consensusNode.name]; // Add service endpoints nodeDataWrapper.addServiceEndpoint(domainName ?? networkNodeService.externalAddress, constants.GRPC_PORT); } catch (error) { throw new SoloError(error.message, error); } } })(); } static async initialize(consensusNodes, keyManager, accountManager, keysDirectory, networkNodeServiceMap, adminPublicKeys, domainNamesMapping) { const adminPublicKeyMap = new Map(); let adminPublicKeyIsDefaultValue = true; for (const publicKey of adminPublicKeys) { if (publicKey !== flags.adminPublicKeys.definition.defaultValue) { adminPublicKeyIsDefaultValue = false; } } // If admin keys are passed and if it is not the default value from flags then validate and build the adminPublicKeyMap if (adminPublicKeys.length > 0 && !adminPublicKeyIsDefaultValue) { if (adminPublicKeys.length !== consensusNodes.length) { throw new SoloError(`Provide a comma separated list of DER encoded ED25519 public keys for each node, adminPublicKeys.length=${adminPublicKeys.length} does not match consensusNodes.length=${consensusNodes.length}`); } for (const [index, key] of adminPublicKeys.entries()) { adminPublicKeyMap.set(consensusNodes[index].name, key); } } const instance = new GenesisNetworkDataConstructor(consensusNodes, keyManager, accountManager, keysDirectory, networkNodeServiceMap, adminPublicKeyMap, domainNamesMapping); await instance.load(); return instance; } /** * Loads the gossipCaCertificate and grpcCertificateHash */ async load() { await this.initializationPromise; await Promise.all(this.consensusNodes.map(async (consensusNode) => { const signingCertFile = Templates.renderGossipPemPublicKeyFile(consensusNode.name); const signingCertFullPath = PathEx.joinWithRealPath(this.keysDirectory, signingCertFile); const derCertificate = this.keyManager.getDerFromPemCertificate(signingCertFullPath); //* Assign the DER formatted certificate this.rosters[consensusNode.name].gossipCaCertificate = this.nodes[consensusNode.name].gossipCaCertificate = Buffer.from(derCertificate).toString('base64'); //* Generate the SHA-384 hash this.nodes[consensusNode.name].grpcCertificateHash = ''; })); } toJSON() { const nodeMetadata = []; for (const nodeAlias of Object.keys(this.nodes)) { nodeMetadata.push({ node: this.nodes[nodeAlias].toObject(), rosterEntry: this.rosters[nodeAlias].toObject(), }); } return JSON.stringify({ nodeMetadata: nodeMetadata }); } } //# sourceMappingURL=genesis-network-data-constructor.js.map