Skip to content

Commit

Permalink
feat: Adding debug mode to local node.
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Stefanov <[email protected]>
  • Loading branch information
stefan-stefanooov committed Dec 19, 2023
1 parent ffb6ba3 commit d3c6c0a
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 21 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ PLATFORM_JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xlog:gc*:gc.lo
NETWORK_NODE_LOGS_ROOT_PATH=./network-logs/node
APPLICATION_ROOT_PATH=./compose-network/network-node
APPLICATION_CONFIG_PATH=./compose-network/network-node/data/config
RECORD_PARSER_ROOT_PATH=./src/record-parser
RECORD_PARSER_ROOT_PATH=./src/services/record-parser

#### Network Node Memory Limits ####
NETWORK_NODE_MEM_LIMIT=8gb
Expand Down Expand Up @@ -78,6 +78,7 @@ RELAY_RATE_LIMIT_DISABLED=true

#### Record Stream Uploader ####
STREAM_EXTENSION=rcd.gz
STREAM_SIG_EXTENSION=rcd_sig

#### ENVOY ####
ENVOY_IMAGE_PREFIX=envoyproxy/
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,17 @@ Available commands:
--balance to set starting hbar balance of the created accounts.
--async to enable or disable asynchronous creation of accounts.
--b or --blocklist to enable or disable account blocklisting. Depending on how many private keys are blocklisted, this will affect the generated on startup accounts.
-g, --enable-debug Enable or disable debugging of the local node [boolean] [default: false]
stop - Stops the local hedera network and delete all the existing data.
restart - Restart the local hedera network.
generate-accounts <n> - Generates N accounts, default 10.
options:
--h or --host to override the default host.
--balance to set starting hbar balance of the created accounts.
--async to enable or disable asynchronous creation of accounts.
debug [timestamp] - Parses and prints the contents of the record file that has been created
during the selected timestamp.
Important: Local node must be started with the -g, --enable-debug flag to enable this feature
```

Note: Generated accounts are 3 types (ECDSA, Alias ECDSA and ED25519). All of them are usable via HederaSDK. Only Alias ECDSA accounts can be imported into wallet like Metamask or used in ethers.
Expand Down
7 changes: 5 additions & 2 deletions src/Errors/LocalNodeErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
*/

export class LocalNodeErrors extends Error{
export class LocalNodeErrors extends Error {
public message: string;
public name: string;

Expand All @@ -39,5 +39,8 @@ export class LocalNodeErrors extends Error{

export const Errors = {
CONNECTION_ERROR: (port?: number) => new LocalNodeErrors("Connection Error", `Something went wrong, while trying to connect ${port ? `to port ${port}` : `to local node`}`),
CLEINT_ERROR: (msg?: string) => new LocalNodeErrors("Client Error", `Something went wrong, while trying to create SDK Client${msg ? `: ${msg}` : ``}`)
CLEINT_ERROR: (msg?: string) => new LocalNodeErrors("Client Error", `Something went wrong, while trying to create SDK Client${msg ? `: ${msg}` : ``}`),
NO_RECORD_FILE_FOUND_ERROR: () => new LocalNodeErrors('No record file found Error', 'No record file found for the given timestamp.'),
INVALID_TIMESTAMP_ERROR: () => new LocalNodeErrors('Invalid Timestamp Error', 'Invalid timestamp string. Accepted formats are: 0000000000.000000000 and 0000000000-000000000'),
DEBUG_MODE_CHECK_ERROR: () => new LocalNodeErrors('Debug Mode check Error', 'Debug mode is not enabled to use this command. Please use the --enable-debug flag to enable it.'),
}
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ export const RELAY_LABEL = "json-rpc-relay";
export const IS_WINDOWS = process.platform === "win32";
export const UNKNOWN_VERSION = "Unknown";
export const EVM_ADDRESSES_BLOCKLIST_FILE_RELATIVE_PATH = '../../compose-network/network-node'
export const RELATIVE_TMP_DIR_PATH = '../../src/services/record-parser/temp';
export const RELATIVE_RECORDS_DIR_PATH = '../../network-logs/node/recordStreams/record0.0.3';
12 changes: 12 additions & 0 deletions src/data/StateData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { NetworkPrepState } from '../state/NetworkPrepState';
import { StartState } from '../state/StartState';
import { StopState } from '../state/StopState';
import { StateConfiguration } from '../types/StateConfiguration';
import { DebugState } from '../state/DebugState';

export class StateData {

Expand All @@ -39,6 +40,8 @@ export class StateData {
return this.getStopConfiguration();
case 'accountCreation':
return this.getAccountCreationConfiguration();
case 'debug':
return this.getDebugConfiguration();
default:
return undefined;
}
Expand Down Expand Up @@ -92,4 +95,13 @@ export class StateData {
]
}
}

private getDebugConfiguration(): StateConfiguration {
return {
'stateMachineName' : 'debug',
'states' : [
new DebugState()
]
}
}
}
44 changes: 29 additions & 15 deletions src/services/CLIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class CLIService implements IService{
this.userComposeOption(yargs);
this.userComposeDirOption(yargs);
this.blocklistingOption(yargs);
this.enableDebugOption(yargs);
this.isStartup = true;
}

Expand All @@ -72,20 +73,22 @@ export class CLIService implements IService{

public getCurrentArgv(){
const argv = this.currentArgv as ArgumentsCamelCase<{}>;
const accounts: number = argv.accounts as number;
const async: any = argv.async as boolean;
const balance: number = argv.balance as number;
const detached: boolean = this.isStartup ? argv.detached as boolean : true;
const host: string = argv.host as string;
const network: NetworkType = this.resolveNetwork(argv.network as string);
const limits: boolean = argv.limits as boolean;
const devMode: boolean = argv.dev as boolean;
const fullMode: boolean = argv.full as boolean;
const multiNode: boolean = argv.multinode as boolean;
const userCompose: boolean = argv.usercompose as boolean;
const userComposeDir: string = argv.composedir as string;
const blocklisting: boolean = argv.blocklist as boolean;
const startup: boolean = this.isStartup;
const accounts = argv.accounts as number;
const async = argv.async as boolean;
const balance = argv.balance as number;
const detached = this.isStartup ? argv.detached as boolean : true;
const host = argv.host as string;
const network = this.resolveNetwork(argv.network as string);
const limits = argv.limits as boolean;
const devMode = argv.dev as boolean;
const fullMode = argv.full as boolean;
const multiNode = argv.multinode as boolean;
const userCompose = argv.usercompose as boolean;
const userComposeDir = argv.composedir as string;
const blocklisting = argv.blocklist as boolean;
const timestamp = argv.timestamp as string;
const enableDebug = argv.enableDebug as boolean;
const startup = this.isStartup;

const currentArgv: CLIOptions = {
accounts,
Expand All @@ -101,7 +104,9 @@ export class CLIService implements IService{
userCompose,
userComposeDir,
blocklisting,
startup
startup,
timestamp,
enableDebug
};

return currentArgv;
Expand Down Expand Up @@ -240,6 +245,15 @@ export class CLIService implements IService{
default: false
});
}

private enableDebugOption(yargs: Argv<{}>): void {
yargs.option('enable-debug', {
type: 'boolean',
describe: 'Enable or disable debugging of the local node',
demandOption: false,
default: false
});
}

private resolveNetwork(network: string): NetworkType {
switch (network) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/record-parser/src/Parser.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import static com.hedera.services.utils.forensics.RecordParsers.parseV6RecordStreamEntriesIn;
import static com.hedera.node.app.service.mono.utils.forensics.RecordParsers.parseV6RecordStreamEntriesIn;
public class Parser {
public static void main(String[] args) {
try {
Expand Down
1 change: 1 addition & 0 deletions src/state/CleanUpState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class CleanUpState implements IState{
const application = yaml.load(readFileSync(propertiesFilePath).toString()) as any;
delete application.hedera.mirror.importer.dataPath;
delete application.hedera.mirror.importer.downloader.sources;
delete application.hedera.mirror.importer.downloader.local

application.hedera.mirror.monitor.nodes = originalNodeConfiguration.fullNodeProperties;
writeFileSync(propertiesFilePath, yaml.dump(application, { lineWidth: 256 }));
Expand Down
86 changes: 85 additions & 1 deletion src/state/DebugState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@
*
*/

import { resolve } from 'path';
import { readdirSync, copyFileSync, unlinkSync } from 'fs';
import shell from 'shelljs';
import { IOBserver } from '../controller/IObserver';
import { LoggerService } from '../services/LoggerService';
import { ServiceLocator } from '../services/ServiceLocator';
import { IState } from './IState';
import { CLIService } from '../services/CLIService';
import readApplicationYML from '../utils/config';
import { Errors } from '../Errors/LocalNodeErrors';
import { RELATIVE_RECORDS_DIR_PATH, RELATIVE_TMP_DIR_PATH } from '../constants';

export class DebugState implements IState{
private logger: LoggerService;
Expand All @@ -41,6 +48,83 @@ export class DebugState implements IState{
}

public async onStart(): Promise<void> {
throw new Error('Method not implemented.');
try {
const { timestamp } = ServiceLocator.Current.get<CLIService>(CLIService.name).getCurrentArgv();
DebugState.checkForDebugMode();
this.logger.trace('Debug State Starting...', this.stateName);
const jsTimestampNum = DebugState.getAndValidateTimestamp(timestamp)

const tempDir = resolve(__dirname, RELATIVE_TMP_DIR_PATH);
const recordFilesDirPath = resolve(__dirname, RELATIVE_RECORDS_DIR_PATH);
this.findAndCopyRecordFileToTmpDir(jsTimestampNum, recordFilesDirPath, tempDir)
// Perform the parsing
await shell.exec(
'docker exec network-node bash /opt/hgcapp/recordParser/parse.sh'
);

DebugState.cleanTempDir(tempDir);
} catch (error: any) {
this.logger.error(error.message);
return
}
}

private static cleanTempDir(dirPath: string): void {
for (const tempFile of readdirSync(dirPath)) {
if (tempFile !== '.gitignore') {
unlinkSync(resolve(dirPath, tempFile));
}
}
}

private static checkForDebugMode(): void {
const { application } = readApplicationYML()
const { deleteAfterProcessing } = application.hedera.mirror.importer.downloader.local
if (deleteAfterProcessing) {
throw Errors.DEBUG_MODE_CHECK_ERROR();
}
}

private static getAndValidateTimestamp(timestamp: string): number {
const timestampRegEx = /^\d{10}[.-]\d{9}$/;
if (!timestampRegEx.test(timestamp)) {
throw Errors.INVALID_TIMESTAMP_ERROR();
}

// Parse the timestamp to a record file filename
let jsTimestamp = timestamp
.replace('.', '')
.replace('-', '')
.substring(0, 13);
return parseInt(jsTimestamp);
}

private findAndCopyRecordFileToTmpDir(jsTimestampNum: number, recordFilesDirPath: string, tmpDirPath: string): void {
// Copy the record file to a temp directory
const files = readdirSync(recordFilesDirPath);
const recordExt = `.${process.env.STREAM_EXTENSION}`;
for (const file of files) {
const recordFileName = file.replace(recordExt, '');
const fileTimestamp = new Date(recordFileName.replace(/_/g, ':')).getTime();
if (fileTimestamp >= jsTimestampNum) {
if (file.endsWith(recordExt)) {
this.logger.trace(`Parsing record file [${file}]\n`);
}

const sigFile = recordFileName + `.${process.env.STREAM_SIG_EXTENSION}`;
copyFileSync(
resolve(recordFilesDirPath, file),
resolve(tmpDirPath, file)
);
copyFileSync(
resolve(recordFilesDirPath, sigFile),
resolve(tmpDirPath, sigFile)
);

return
}
}

throw Errors.NO_RECORD_FILE_FOUND_ERROR();
}
}
3 changes: 3 additions & 0 deletions src/state/InitState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export class InitState implements IState{
private configureMirrorNodeProperties() {
this.logger.trace('Configuring required mirror node properties, depending on selected configuration...', this.stateName);
const turboMode = !this.cliOptions.fullMode;
const debugMode = this.cliOptions.enableDebug;

// const multiNode = this.cliOptions.multiNode;

Expand All @@ -144,6 +145,8 @@ export class InitState implements IState{
application.hedera.mirror.importer.downloader.sources = originalNodeConfiguration.turboNodeProperties.sources;
}

application.hedera.mirror.importer.downloader['local']['deleteAfterProcessing'] = !debugMode

// if (multiNode) {
// application['hedera']['mirror']['monitor']['nodes'] = originalNodeConfiguration.multiNodeProperties
// }
Expand Down
2 changes: 1 addition & 1 deletion src/state/StartState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class StartState implements IState{
}

return shell.exec(
`docker compose -f ${composeFiles.join(' -f ')} up -d 2>${this.dockerService.getNullOutput()}`
`docker compose -f ${composeFiles.join(' -f ')} up -d 2>${this.dockerService.getNullOutput()}`
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/types/CLIOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ export interface CLIOptions {
userComposeDir: string,
blocklisting: boolean,
startup: boolean,
timestamp: string,
enableDebug: boolean
}
15 changes: 15 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { join } from 'path';
import yaml from 'js-yaml';
import { readFileSync } from 'fs';

export const APPLICATION_YML_RELATIVE_PATH = '../../compose-network/mirror-node/application.yml';

export default function readApplicationYML() {
const propertiesFilePath = join(__dirname, APPLICATION_YML_RELATIVE_PATH);
const application = yaml.load(readFileSync(propertiesFilePath).toString()) as any;

return {
propertiesFilePath,
application
}
}

0 comments on commit d3c6c0a

Please sign in to comment.