@hashgraph/hedera-local
Version:
Developer tooling for running Local Hedera Network (Consensus + Mirror Nodes).
401 lines • 19.3 kB
JavaScript
"use strict";
// SPDX-License-Identifier: Apache-2.0
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccountCreationState = void 0;
const path_1 = __importDefault(require("path"));
const fs_1 = require("fs");
const csv_parser_1 = __importDefault(require("csv-parser"));
const sdk_1 = require("@hashgraph/sdk");
const LoggerService_1 = require("../services/LoggerService");
const ServiceLocator_1 = require("../services/ServiceLocator");
const EventType_1 = require("../types/EventType");
const CLIService_1 = require("../services/CLIService");
const ClientService_1 = require("../services/ClientService");
const accountConfiguration_json_1 = require("../configuration/accountConfiguration.json");
const constants_1 = require("../constants");
const local_json_1 = __importDefault(require("../configuration/local.json"));
const AccountUtils_1 = require("../utils/AccountUtils");
const RetryUtils_1 = require("../utils/RetryUtils");
/**
* Represents the state of account creation.
* This class is responsible for initializing the AccountCreationState object.
* @implements {IState}
*/
class AccountCreationState {
/**
* Represents the state of account creation.
* This class is responsible for initializing the AccountCreationState object.
*/
constructor() {
this.shouldRetry = (error) => {
var _a;
return (_a = error === null || error === void 0 ? void 0 : error.toString().includes(constants_1.SDK_ERRORS.FAILED_TO_FIND_A_HEALTHY_NODE)) !== null && _a !== void 0 ? _a : false;
};
this.doOnRetry = (error) => {
this.logger.warn(`Error occurred during task execution: "${error === null || error === void 0 ? void 0 : error.toString()}"`, this.stateName);
};
this.stateName = AccountCreationState.name;
this.logger = ServiceLocator_1.ServiceLocator.Current.get(LoggerService_1.LoggerService.name);
this.cliService = ServiceLocator_1.ServiceLocator.Current.get(CLIService_1.CLIService.name);
this.clientService = ServiceLocator_1.ServiceLocator.Current.get(ClientService_1.ClientService.name);
this.nodeStartup = true;
this.logger.trace(constants_1.ACCOUNT_CREATION_STATE_INIT_MESSAGE, this.stateName);
}
/**
* Subscribes an observer to receive updates from the AccountCreationState.
* @param {IOBserver} observer The observer to subscribe.
*/
subscribe(observer) {
this.observer = observer;
}
/**
* Starts the account creation state.
*
* This method retrieves the current arguments, checks if blocklisting is enabled, and if so, gets the count of blocklisted accounts.
* It logs the start of the account creation state, retrieves the balance and number of accounts from the arguments, and sets the node startup.
* If the mode is asynchronous, it generates accounts asynchronously, otherwise, it generates ECDSA, alias ECDSA, and ED25519 accounts.
* Finally, it updates the observer with the finish event type.
*
* @returns {Promise<void>} A Promise that resolves when the state is started.
* @emits {EventType.Finish} When the state is finished.
*/
onStart() {
return __awaiter(this, void 0, void 0, function* () {
const { async, blocklisting, accounts, balance, startup } = this.cliService.getCurrentArgv();
this.nodeStartup = startup;
let blocklistedAccountsCount = 0;
if (blocklisting) {
blocklistedAccountsCount = yield this.getBlocklistedAccountsCount();
}
const mode = async ? 'asynchronous' : 'synchronous';
const blockListedMessage = blocklisting ? `with ${blocklistedAccountsCount} blocklisted accounts` : '';
this.logger.info(`${constants_1.LOADING} Starting Account Creation state in ${mode} mode ${blockListedMessage}...`, this.stateName);
const promise = this.generateAccounts(balance, accounts);
if (!async) {
yield promise;
}
this.logger.info(`${constants_1.CHECK_SUCCESS} Accounts created succefully!`, this.stateName);
this.observer.update(EventType_1.EventType.Finish);
});
}
/**
* Generates accounts.
*
* This method generates ECDSA, alias ECDSA, and ED25519 accounts.
*
* @private
* @param {number} balance - The balance for the accounts.
* @param {number} accounts - The number of accounts to generate.
* @returns {Promise<void>} - A promise that resolves when the accounts have been generated.
*/
generateAccounts(balance, accounts) {
return __awaiter(this, void 0, void 0, function* () {
yield Promise.all([
this.generateECDSA(balance, accounts),
this.generateAliasECDSA(balance, accounts),
this.generateED25519(balance, accounts)
]);
});
}
/**
* Generates ECDSA accounts.
*
* If the node is in startup mode:
* - it uses the private keys from the ECDSA private keys array to create the account.
* - otherwise, it generates new ECDSA private keys.
*
* @private
* @param {number} balance - The balance for the accounts.
* @param {number} limit - The number of accounts to generate.
* @returns {Promise<Account[]>} - A promise that resolves when all the accounts have been created.
*/
generateECDSA(balance, limit) {
return __awaiter(this, void 0, void 0, function* () {
const accountData = this.nodeStartup ?
accountConfiguration_json_1.privateKeysECDSA.map(privateKeyString => ({
balance,
privateKey: sdk_1.PrivateKey.fromStringECDSA(privateKeyString)
})) :
Array.from({ length: limit }, () => ({
balance,
privateKey: sdk_1.PrivateKey.generateECDSA()
}));
const endIndex = Math.min(accountData.length, limit);
return this.createAccounts('ECDSA', accountData.slice(0, endIndex));
});
}
/**
* Generates alias ECDSA accounts.
*
* If the node is in startup mode and the private key for the alias ECDSA account exists:
* - it uses the private key to create the account.
* - otherwise, it generates new ECDSA private keys.
*
* If the mode is asynchronous:
* - it creates the alias accounts asynchronously and returns a promise that resolves when they have been created,
* - otherwise, it creates the alias accounts synchronously and returns them in a resolved promise.
*
* @private
* @param {number} balance - The balance for the accounts.
* @param {number} accountNum - The number of accounts to generate.
* @returns {Promise<Account[]>} - A promise that resolves when all the alias accounts have been created
*/
generateAliasECDSA(balance, accountNum) {
return __awaiter(this, void 0, void 0, function* () {
const accountData = this.nodeStartup ?
accountConfiguration_json_1.privateKeysAliasECDSA.map(privateKeyString => ({
balance,
privateKey: sdk_1.PrivateKey.fromStringECDSA(privateKeyString)
})) :
Array.from({ length: accountNum }, () => ({
balance,
privateKey: sdk_1.PrivateKey.generateECDSA()
}));
const endIndex = Math.min(accountData.length, accountNum);
return this.createAliasAccounts(accountData.slice(0, endIndex));
});
}
/**
* Generates ED25519 accounts.
*
* If the node is in startup mode:
* - it uses the private keys from the ED25519 private keys array to create the account.
* - otherwise, it generates new ED25519 private keys.
*
* @param balance - The balance for the accounts.
* @param limit - The number of accounts to generate.
* @returns {Promise<Account[]>} - A promise that resolves when all the
* accounts have been created if the mode is asynchronous, otherwise void.
* @private
*/
generateED25519(balance, limit) {
return __awaiter(this, void 0, void 0, function* () {
const accountData = this.nodeStartup ?
accountConfiguration_json_1.privateKeysED25519.map(privateKeyString => ({
balance,
privateKey: sdk_1.PrivateKey.fromStringED25519(privateKeyString)
})) :
Array.from({ length: limit }, () => ({
balance,
privateKey: sdk_1.PrivateKey.generateED25519()
}));
const endIndex = Math.min(accountData.length, limit);
return this.createAccounts('ED25519', accountData.slice(0, endIndex));
});
}
/**
* Generates ED25519 accounts.
*
* @private
* @param {string} title - The title to be logged for the account list.
* @param {Array<{ balance: number, privateKey: PrivateKey}>} accountData - The data for the accounts that will be created.
* @param {number} accountData.balance - The balance of the account to create.
* @param {PrivateKey} accountData.privateKey - The private key of the account to create.
* @returns {Promise<Account[]>} - A promise that resolves when all the accounts have been created.
*/
createAccounts(title, accountData) {
return __awaiter(this, void 0, void 0, function* () {
const accountPromises = [];
accountData.forEach((account) => {
const { privateKey, balance } = account;
const client = this.clientService.getClient();
const publicKey = privateKey.publicKey;
const createAccountPromise = RetryUtils_1.RetryUtils.retryTask(() => AccountUtils_1.AccountUtils.createAccount(publicKey, balance, client), {
shouldRetry: error => this.shouldRetry(error),
doOnRetry: error => this.doOnRetry(error)
}).then((accountInfo) => {
const address = accountInfo.accountId.toSolidityAddress();
return {
accountId: accountInfo.accountId.toString(),
balance: accountInfo.balance,
privateKey,
address
};
});
accountPromises.push(createAccountPromise);
});
return Promise.all(accountPromises)
.then((accounts) => {
if (accounts) {
this.logAccountTitle(title);
accounts.forEach((account) => this.logAccount(account.accountId, account.balance, `0x${account.privateKey.toStringRaw()}`));
this.logAccountDivider();
}
return accounts;
});
});
}
/**
* Creates alias accounts.
*
* @param accountData - The data for the accounts that will be created.
* @param accountData.balance - The balance of the account to create.
* @param accountData.privateKey - The private key of the account to create.
* @returns {Promise<Account[]>} - A promise that resolves when all the alias accounts have been created
* @private
*/
createAliasAccounts(accountData) {
return __awaiter(this, void 0, void 0, function* () {
const accountPromises = [];
// eslint-disable-next-line no-plusplus
accountData.forEach(account => {
const { privateKey, balance } = account;
const client = this.clientService.getClient();
const aliasAccountId = privateKey.publicKey.toAccountId(0, 0);
const createAccountPromise = RetryUtils_1.RetryUtils.retryTask(() => AccountUtils_1.AccountUtils.createAliasedAccount(aliasAccountId, balance, client), {
shouldRetry: error => this.shouldRetry(error),
doOnRetry: error => this.doOnRetry(error)
}).then((accountInfo) => {
const address = privateKey.publicKey.toEvmAddress();
return {
accountId: accountInfo.accountId.toString(),
balance: accountInfo.balance,
privateKey,
address
};
});
accountPromises.push(createAccountPromise);
});
return Promise.all(accountPromises)
.then((accounts) => {
if (accounts) {
this.logAliasAccountTitle();
accounts.forEach((account) => this.logAliasAccount(account.accountId, account.balance, `0x${account.address}`, `0x${account.privateKey.toStringRaw()}`));
this.logAliasAccountDivider();
}
return accounts;
});
});
}
/**
* Retrieves the blocklist file name.
*
* This method searches the properties of the node configuration for the property with the key 'accounts.blocklist.path' and returns its value.
*
* @private
* @returns {string} - The blocklist file name.
*/
blockListFileName() {
var _a;
return (_a = local_json_1.default.nodeConfiguration.properties
.find((prop) => prop.key === 'accounts.blocklist.path')) === null || _a === void 0 ? void 0 : _a.value;
}
/**
* Retrieves the count of blocklisted accounts.
*
* This method creates a new promise that resolves with the count of blocklisted accounts.
* It initializes the count to 0 and constructs the file path to the blocklist file.
* It creates a read stream from the file, pipes it through a CSV parser, and increments the count for each data event.
* When the end event is emitted, it resolves the promise with the count.
*
* @private
* @returns {Promise<number>} - A promise that resolves with the count of blocklisted accounts.
*/
getBlocklistedAccountsCount() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve) => {
let count = 0;
const filepath = path_1.default.join(__dirname, constants_1.EVM_ADDRESSES_BLOCKLIST_FILE_RELATIVE_PATH, this.blockListFileName());
(0, fs_1.createReadStream)(filepath)
.pipe((0, csv_parser_1.default)())
.on('data', () => {
// eslint-disable-next-line no-plusplus
count++;
})
.on('end', () => {
resolve(count);
});
});
});
}
/**
* Logs an account.
*
* This method logs the account ID, the private key, and the balance of an account, along with the state name.
*
* @private
* @param {string} accountId - The account ID.
* @param {Hbar} balance - The balance of the account.
* @param {string} privateKey - The private key of the account.
*/
logAccount(accountId, balance, privateKey) {
this.logger.info(`| ${accountId} - ${privateKey} - ${balance} |`, this.stateName);
}
/**
* Logs an alias account.
*
* This method logs the account ID, the account address, the private key of the account, and the balance of an alias account, along with the state name.
*
* @private
* @param {string} accountId - The account ID.
* @param {number} balance - The balance of the account.
* @param {string} address - The address of the account.
* @param {string} privateKey - The private key of the account.
*/
logAliasAccount(accountId, balance, address, privateKey) {
this.logger.info(`| ${accountId} - ${address} - ${privateKey} - ${balance} |`, this.stateName);
}
/**
* Logs the title of an account.
*
* This method logs a divider, the title of the account list with the account type, another divider, the headers for the account ID, private key, and balance, and a final divider.
*
* @private
* @param {string} accountType - The type of the account.
*/
logAccountTitle(accountType) {
this.logAccountDivider();
this.logger.info(`|-----------------------------| Accounts list (${accountType} keys) |----------------------------|`, this.stateName);
this.logAccountDivider();
this.logger.info('| id | private key | balance |', this.stateName);
this.logAccountDivider();
}
/**
* Logs the title of an alias account.
*
* This method logs a divider, the title of the alias account list, another divider, the headers for the account ID, public address, private key, and balance, and a final divider.
*
* @private
*/
logAliasAccountTitle() {
this.logAliasAccountDivider();
this.logger.info('|------------------------------------------------| Accounts list (Alias ECDSA keys) |--------------------------------------------------|', this.stateName);
this.logAliasAccountDivider();
this.logger.info('| id | public address | private key | balance |', this.stateName);
this.logAliasAccountDivider();
}
/**
* Logs a divider for an account.
*
* This method logs a divider line, along with the state name.
*
* @private
*/
logAccountDivider() {
this.logger.info('|-----------------------------------------------------------------------------------------|', this.stateName);
}
/**
* Logs a divider for an alias account.
*
* This method logs a divider line, along with the state name.
*
* @private
*/
logAliasAccountDivider() {
this.logger.info('|--------------------------------------------------------------------------------------------------------------------------------------|', this.stateName);
}
}
exports.AccountCreationState = AccountCreationState;
//# sourceMappingURL=AccountCreationState.js.map