Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality of initialize node in the background #67

Merged
merged 20 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
33e1191
chore: add `native_resources` directory to `gitignore`
esmaeil-ahmadipour Jan 23, 2025
17b2610
fix: fix conflict at using import files of `fluent_ui.dart` and `mat…
esmaeil-ahmadipour Jan 24, 2025
ebf4458
fix: fix conflict at using import files of `fluent_ui.dart` and `mat…
esmaeil-ahmadipour Jan 24, 2025
80d9be8
docs: update `CHANGELOG.md` & `pubspec.yaml` files
esmaeil-ahmadipour Jan 24, 2025
4ecd0f8
ci: update `ci.yml` & `release.yml` files
esmaeil-ahmadipour Jan 24, 2025
fa8ef91
Merge branch 'fix/import-conflicts-material-fluent' of https://github…
esmaeil-ahmadipour Jan 24, 2025
fd2fb79
update: add/update `native_resources` directory to `gitignore`
esmaeil-ahmadipour Jan 24, 2025
c567375
fix: fixing some issues on toolbar logo widget tests
PouriaMoradi021 Jan 24, 2025
b16a45c
Merge branch 'fix/import-conflicts-material-fluent' of https://github…
esmaeil-ahmadipour Jan 24, 2025
a86fdfc
chore: add some packages/libraries
esmaeil-ahmadipour Jan 28, 2025
0d26618
feat: create `SeedGenerator` tools for create secure and random seeds
esmaeil-ahmadipour Jan 28, 2025
2eda73a
update: modify `RestorationSeedPage` to include sample seed generation
esmaeil-ahmadipour Jan 28, 2025
b10f09e
update: modify `ValidatorConfigPage` to add directory selector for in…
esmaeil-ahmadipour Jan 28, 2025
a94e262
chore: update some packages & libraries
esmaeil-ahmadipour Jan 30, 2025
331c751
chore: update native modules (macos/windows/linux)
esmaeil-ahmadipour Jan 30, 2025
7cd1b30
feat: add logic code in background for initialized node
esmaeil-ahmadipour Jan 30, 2025
136d6a1
Merge branch 'develop' into feat/initialize-node-background
esmaeil-ahmadipour Jan 30, 2025
3d90633
docs: update `CHANGELOG.md` & `pubspec.yaml` files
Jan 30, 2025
0e3c3f3
docs: update docs for `DaemonCubit` class
Jan 31, 2025
6f6a81f
docs: update docs for `NodeConfigData` class
Jan 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ analysis_benchmark.json
.pub-preload-cache/
.pub-cache/
.pub/
lib/src/core/native_resources
lib/src/core/native_resources/windows
lib/src/core/native_resources/macos
lib/src/core/native_resources/linux
build/
flutter_*.png
linked_*.ds
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# 1.13.0+15

- [Fix] : Fix conflicts between `fluent_ui.dart` and `material.dart` imports. [#67](https://github.com/pactus-project/pactus-gui/pull/67)
- Flutter version upgraded to `3.27.3`.
- Refactor `customized_widget/screens` by replacing `fluent_ui` widgets.
- Remove duplicate folder `splash_screen`.

- [Feat] : Create `SeedGenerator` tools for generating secure and random seeds. [#67](https://github.com/pactus-project/pactus-gui/pull/67)

- [Update] : Modify `RestorationSeedPage` to include sample seed generation. [#67](https://github.com/pactus-project/pactus-gui/pull/67)
- Fix color of text in `WelcomePage`.
- Add 12 & 24 seed generation modes to `RestorationSeedPage`.
- Modify `ValidatorConfigPage` to add directory selector for initial node address setup.

- [Chore] : Add some packages/libraries. [#67](https://github.com/pactus-project/pactus-gui/pull/67)
- `file_selector` for file and folder selection via native UI.
- `bip39_mnemonic` for wallet seed generation.
- `crypto`, `bcrypt`, and `convert` for secure layer.
- `process_run` for running commands on daemon files.
- Add `native_resources` directory to `.gitignore`.

# 1.12.0+14

- [Fix] : Resolved conflicts between `fluent_ui.dart` and `material.dart` imports. [#66](https://github.com/pactus-project/pactus-gui/pull/66)
Expand Down
11 changes: 10 additions & 1 deletion lib/src/core/router/registration_routes.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:gui/src/core/utils/daemon_manager/bloc/daemon_cubit.dart';
import 'package:gui/src/features/confirmation_seed/presentation/screen/confirmation_seed_page.dart';
import 'package:gui/src/features/dashboard/presentation/screen/dashboard_page.dart';
import 'package:gui/src/features/finish/presentation/screen/finish_page.dart';
Expand Down Expand Up @@ -52,7 +54,14 @@ final List<GoRoute> registrationRoutes = [
GoRoute(
path: AppRoute.finish.path,
name: AppRoute.finish.name,
builder: (context, state) => const FinishPage(),
builder: (context, state) => MultiBlocProvider(
providers: [
BlocProvider<DaemonCubit>(
create: (_) => DaemonCubit(),
),
],
child: const FinishPage(),
),
routes: [
GoRoute(
path: AppRoute.password.path,
Expand Down
63 changes: 63 additions & 0 deletions lib/src/core/utils/daemon_manager/bloc/daemon_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import 'dart:io';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gui/src/core/utils/daemon_manager/bloc/daemon_state.dart';
import 'package:path/path.dart' show dirname;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add document here.

@esmaeil-ahmadipour

/// [DaemonCubit] Documentation:
/// `DaemonCubit` manages the execution of the Pactus daemon process.
/// It extends `Cubit<DaemonState>` and provides methods to run the daemon
/// while handling its execution state.
///
/// ## Features:
/// - Runs the Pactus daemon using a given command and arguments.
/// - Emits different states (`DaemonLoading`, `DaemonSuccess`, `DaemonError`)
/// based on the execution outcome.
/// - Handles standard output and errors from the process.
///
/// ## Usage:
/// ```dart
/// final daemonCubit = DaemonCubit();
/// daemonCubit.runPactusDaemon(command: 'pactusd', arguments: ['--start']);
/// ```
///
/// ## Notes:
/// - The working directory is set to a Linux-specific path.
/// - Needs adaptation for other operating systems.
/// - Handles exceptions and errors gracefully.
class DaemonCubit extends Cubit<DaemonState> {
DaemonCubit() : super(DaemonInitial());

/// [runPactusDaemon] Documentation:
/// Runs the Pactus daemon process.
///
/// - [command]: The command to execute (e.g., "pactusd").
/// - [arguments]: A list of arguments to pass to the command.
///
/// Emits:
/// - `DaemonLoading` before execution starts.
/// - `DaemonSuccess` if the process runs successfully.
/// - `DaemonError` if an error occurs.
Future<void> runPactusDaemon({
// required String workingDirectory,
required String command,
required List<String> arguments,
}) async {
emit(DaemonLoading());

try {
final scriptDir = dirname(Platform.script.toFilePath());
// TODO(Esmaeil): this part need handled for another os
final targetPath = '$scriptDir/lib/src/core/native_resources/linux/';

final result =
await Process.run(command, arguments, workingDirectory: targetPath);
if (result.exitCode == 0) {
emit(DaemonSuccess('${result.stdout}'));
} else {
emit(DaemonError('${result.stderr}'));
}
} on Exception catch (e) {
emit(DaemonError('Exception occurred: $e'));
}
}
}
15 changes: 15 additions & 0 deletions lib/src/core/utils/daemon_manager/bloc/daemon_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
abstract class DaemonState {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add document here.

@esmaeil-ahmadipour

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add fully docs for this part. @PouriaMoradi021


class DaemonInitial extends DaemonState {}

class DaemonLoading extends DaemonState {}

class DaemonSuccess extends DaemonState {
DaemonSuccess(this.output);
final String output;
}

class DaemonError extends DaemonState {
DaemonError(this.error);
final String error;
}
55 changes: 55 additions & 0 deletions lib/src/core/utils/daemon_manager/node_config_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// [NodeConfigData] Documentation:
/// `NodeConfigData` is a singleton class that stores and manages
/// configuration data related to a Pactus blockchain node initialization .
///
/// ## Features:
/// - Implements the Singleton pattern to ensure a single instance.
/// - Provides getters and setters for essential configuration properties.
/// - Stores working directory, restoration seed, password, and validator
/// quantity.
///
/// ## Usage:
/// ```dart
/// final config = NodeConfigData.instance;
/// config.workingDirectory = "/path/to/dir";
/// print(config.workingDirectory);
/// ```
///
/// ## Notes:
/// - Default values are empty strings to prevent null issues.
class NodeConfigData {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add document here.

@esmaeil-ahmadipour

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add fully docs for this part. @PouriaMoradi021

// Private constructor to enforce the Singleton pattern
NodeConfigData._internal();

/// The single instance of [NodeConfigData]
static final NodeConfigData instance = NodeConfigData._internal();

// Private variables
String? _workingDirectory;
String? _restorationSeed;
String? _password;
String? _validatorQty;

// Getters - return empty strings if values are null
String get workingDirectory => _workingDirectory ?? '';
String get restorationSeed => _restorationSeed ?? '';
String get password => _password ?? '';
String get validatorQty => _validatorQty ?? '';

// Setters - update private variables
set workingDirectory(String value) {
_workingDirectory = value;
}

set restorationSeed(String value) {
_restorationSeed = value;
}

set password(String value) {
_password = value;
}

set validatorQty(String value) {
_validatorQty = value;
}
}
46 changes: 46 additions & 0 deletions lib/src/core/utils/daemon_manager/seed_generator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:bcrypt/bcrypt.dart';
import 'package:bip39_mnemonic/bip39_mnemonic.dart';
import 'package:convert/convert.dart';

class SeedGenerator {
Mnemonic? generateSeed(int wordCount) {
Mnemonic? mnemonic;
try {
// Determine the entropy length based on the word count
final entropyLength = (wordCount == 12)
? 128
: (wordCount == 24)
? 256
: 128;

// Generate secure random bytes for entropy
final secureRandomBytes = generateSecureRandomBytes(entropyLength ~/ 8);

// Generate a secure passphrase
final securePassphrase = generateSecurePassphrase(secureRandomBytes);

// Generate the mnemonic based on the entropy and passphrase
mnemonic = Mnemonic.generate(
Language.english,
passphrase: securePassphrase,
entropyLength: entropyLength,
);
} on Exception catch (_) {
throw Exception('Error generating seed!');
}
return mnemonic;
}

Uint8List generateSecureRandomBytes(int length) {
final random = Random.secure();
final randomBytes = List<int>.generate(length, (_) => random.nextInt(256));
return Uint8List.fromList(randomBytes);
}

String generateSecurePassphrase(Uint8List randomBytes) {
final bcryptHash = BCrypt.hashpw(hex.encode(randomBytes), BCrypt.gensalt());
return bcryptHash;
}
}
84 changes: 79 additions & 5 deletions lib/src/features/finish/presentation/screen/finish_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:gui/src/core/common/colors/app_colors.dart';
import 'package:gui/src/core/router/route_name.dart';
import 'package:gui/src/core/utils/daemon_manager/bloc/daemon_cubit.dart';
import 'package:gui/src/core/utils/daemon_manager/bloc/daemon_state.dart';
import 'package:gui/src/core/utils/daemon_manager/node_config_data.dart';
import 'package:pactus_gui_widgetbook/app_styles.dart';

class FinishPage extends StatelessWidget {
Expand All @@ -20,11 +25,80 @@ class FinishPage extends StatelessWidget {
),
),
content: Center(
child: Button(
onPressed: () {
context.goNamed(AppRoute.password.name);
},
child: Text('Navigate to ${AppRoute.password.name}'),
child: Column(
children: [
Text(
'password: ${NodeConfigData.instance.password}',
style: TextStyle(color: AppColors.primaryDark),
),
Text(
'validatorQty: ${NodeConfigData.instance.validatorQty}',
style: TextStyle(color: AppColors.primaryDark),
),
Text(
'workingDirectory:${NodeConfigData.instance.workingDirectory}',
style: TextStyle(color: AppColors.primaryDark),
),
Text(
'restorationSeed: ${NodeConfigData.instance.restorationSeed}',
style: TextStyle(color: AppColors.primaryDark),
),
Button(
onPressed: () async {
final daemonCubit = context.read<DaemonCubit>();

await daemonCubit.runPactusDaemon(
command: './pactus-daemon',
arguments: [
'init',
'--working-dir',
NodeConfigData.instance.workingDirectory,
'--restore',
NodeConfigData.instance.restorationSeed,
'--password',
NodeConfigData.instance.password,
'--val-num',
NodeConfigData.instance.validatorQty,
],
);
},
child: Text('Run Node'),
),
SizedBox(
height: 150,
child: BlocBuilder<DaemonCubit, DaemonState>(
builder: (context, state) {
if (state is DaemonLoading) {
return Center(child: ProgressRing());
} else if (state is DaemonSuccess) {
return SingleChildScrollView(
child: Text(
state.output,
style: TextStyle(fontSize: 16),
),
);
} else if (state is DaemonError) {
return SingleChildScrollView(
child: Text(
state.error,
style: TextStyle(fontSize: 16, color: Colors.red),
),
);
} else {
return Center(
child: Text('Press the button to run the daemon.'),
);
}
},
),
),
Button(
onPressed: () {
context.goNamed(AppRoute.password.name);
},
child: Text('Navigate to ${AppRoute.password.name}'),
),
],
),
),
);
Expand Down
Loading
Loading