Skip to content

Commit

Permalink
Add functionality of initialize node in the background (#67)
Browse files Browse the repository at this point in the history
* chore: add `native_resources` directory to `gitignore`

* fix: fix conflict at using import files of  `fluent_ui.dart` and `material.dart`

- flutter version upgrade to `3.27.3`
- refactor customized_widget/screens by replacing fluent_ui widgets
- remove duplicate folder `splash_screen`

* fix: fix conflict at using import files of  `fluent_ui.dart` and `material.dart`

- flutter version upgrade to `3.27.3`
- refactor customized_widget/screens by replacing fluent_ui widgets
- remove duplicate folder `splash_screen`

* docs: update `CHANGELOG.md` & `pubspec.yaml` files

* ci: update `ci.yml` & `release.yml` files

* update: add/update `native_resources` directory to `gitignore`

* fix: fixing some issues on toolbar logo widget tests

* chore: add some packages/libraries

- `file_selector` for call select file and folder by native ui
- `bip39_mnemonic` for create wallet seeds
- `crypto` , `bcrypt` and `convert` used for secure layer
- `process_run` for call commands on daemon files

* feat: create `SeedGenerator` tools for create secure and random seeds

* update: modify `RestorationSeedPage` to include sample seed generation

- fix color of text in `WelcomePage`
- add seed generation 12 & 24 modes to  `RestorationSeedPage`

* update: modify `ValidatorConfigPage` to add directory selector for initial node address setup

* chore: update some packages & libraries

* chore: update native modules (macos/windows/linux)

* feat: add logic code in  background for  initialized node

* docs: update `CHANGELOG.md` & `pubspec.yaml` files

* docs: update docs for `DaemonCubit` class

* docs: update docs for `NodeConfigData` class

---------

Co-authored-by: PouriaMoradi021 <[email protected]>
Co-authored-by: = <=>
  • Loading branch information
esmaeil-ahmadipour and PouriaMoradi021 authored Jan 31, 2025
1 parent e43458b commit abc9ccc
Show file tree
Hide file tree
Showing 19 changed files with 529 additions and 78 deletions.
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;

/// [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 {}

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 {
// 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

0 comments on commit abc9ccc

Please sign in to comment.