UNPKG

@hiero-ledger/sdk

Version:
173 lines (163 loc) 5.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _tls = _interopRequireDefault(require("tls")); var _grpcJs = require("@grpc/grpc-js"); var _Channel = _interopRequireDefault(require("./Channel.cjs")); var _GrpcServiceError = _interopRequireDefault(require("../grpc/GrpcServiceError.cjs")); var _GrpcStatus = _interopRequireDefault(require("../grpc/GrpcStatus.cjs")); var _ClientConstants = require("../constants/ClientConstants.cjs"); var _version = require("../version.cjs"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // SPDX-License-Identifier: Apache-2.0 /** @type {{ [key: string]: Client }} */ const clientCache = {}; class NodeChannel extends _Channel.default { /** * @internal * @param {string} address * @param {number=} maxExecutionTime */ constructor(address, maxExecutionTime) { super(); /** @type {Client | null} */ this._client = null; this.address = address; this.maxExecutionTime = maxExecutionTime; const { ip, port } = this.parseAddress(address); this.nodeIp = ip; this.nodePort = port; } /** * Convert certificate bytes to PEM format * @param {Buffer} certBytes * @returns {string} */ bytesToPem(certBytes) { const base64Cert = certBytes.toString("base64"); const lines = base64Cert.match(/.{1,64}/g)?.join("\n") || ""; return `-----BEGIN CERTIFICATE-----\n${lines}\n-----END CERTIFICATE-----`; } /** * Validates and parses an address in the "IP:Port" format. * @param {string} address * @returns {{ ip: string, port: string }} */ parseAddress(address) { const [ip, port] = address.split(":"); if (!ip || !port) { throw new Error("Invalid address format. Expected format: 'IP:Port'"); } return { ip, port }; } /** * Retrieve the server's certificate dynamically. * @returns {Promise<string>} */ async _retrieveCertificate() { return new Promise((resolve, reject) => { const socket = _tls.default.connect({ host: this.nodeIp, port: Number(this.nodePort), rejectUnauthorized: false }, () => { try { const cert = socket.getPeerCertificate(); if (cert && cert.raw) { resolve(this.bytesToPem(cert.raw)); } else { reject(new Error("No certificate retrieved.")); } } catch (err) { reject(err); } finally { socket.end(); } }); socket.on("error", reject); }); } /** * Initialize the gRPC client * @returns {Promise<void>} */ async _initializeClient() { if (clientCache[this.address]) { this._client = clientCache[this.address]; return; } let security; const options = { "grpc.ssl_target_name_override": "127.0.0.1", "grpc.default_authority": "127.0.0.1", "grpc.http_connect_creds": "0", "grpc.keepalive_time_ms": 100000, "grpc.keepalive_timeout_ms": 10000, "grpc.keepalive_permit_without_calls": 1, "grpc.enable_retries": 1 }; // If the port is 50212, use TLS if (this.nodePort === "50212") { const certificate = Buffer.from(await this._retrieveCertificate()); security = _grpcJs.credentials.createSsl(certificate); } else { security = _grpcJs.credentials.createInsecure(); } this._client = new _grpcJs.Client(this.address, security, options); clientCache[this.address] = this._client; } /** * @override * @returns {void} */ close() { if (this._client) { this._client.close(); delete clientCache[this.address]; } } /** * @override * @protected * @param {string} serviceName * @returns {import("protobufjs").RPCImpl} */ _createUnaryClient(serviceName) { return (method, requestData, callback) => { this._initializeClient().then(() => { const deadline = new Date(); const milliseconds = this.maxExecutionTime ? this.maxExecutionTime : 10000; deadline.setMilliseconds(deadline.getMilliseconds() + milliseconds); this._client?.waitForReady(deadline, err => { if (err) { callback(new _GrpcServiceError.default(_GrpcStatus.default.Timeout, // Added colons to the IP address to resolve a SonarCloud IP issue. _ClientConstants.ALL_NETWORK_IPS[`${this.nodeIp}:`])); } else { // Create metadata with user agent const metadata = new _grpcJs.Metadata(); metadata.set("x-user-agent", `${_version.SDK_NAME}/${_version.SDK_VERSION}`); this._client?.makeUnaryRequest(`/proto.${serviceName}/${method.name}`, value => value, value => value, Buffer.from(requestData), metadata, (e, r) => { callback(e, r); }); } }); }).catch(err => { if (err instanceof Error) { callback(err); } else { callback(new Error("An unexpected error occurred")); } }); }; } } exports.default = NodeChannel;