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 18 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
38 changes: 38 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,38 @@
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

class DaemonCubit extends Cubit<DaemonState> {
DaemonCubit() : super(DaemonInitial());

Future<void> runPactusDaemon({
// required String workingDirectory,
required String command,
required List<String> arguments,
}) async {
emit(DaemonLoading());

try {
// Get the directory of the script
final scriptDir = dirname(Platform.script.toFilePath());

final targetPath = '$scriptDir/lib/src/core/native_resources/linux/';

// print('scriptDir: $targetPath');

final result =
await Process.run(command, arguments, workingDirectory: targetPath);
if (result.exitCode == 0) {
// دستور با موفقیت اجرا شد
esmaeil-ahmadipour marked this conversation as resolved.
Show resolved Hide resolved
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;
}
35 changes: 35 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,35 @@
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
NodeConfigData._internal();
// Singleton instance
static final NodeConfigData instance = NodeConfigData._internal();

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

// Getters
String get workingDirectory => _workingDirectory ?? '';
String get restorationSeed => _restorationSeed ?? '';
String get password => _password ?? '';
String get validatorQty => _validatorQty ?? '';

// Setters
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
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:go_router/go_router.dart';
import 'package:gui/src/core/router/route_name.dart';
import 'package:gui/src/core/utils/daemon_manager/node_config_data.dart';
import 'package:pactus_gui_widgetbook/app_styles.dart';

class MasterPasswordPage extends StatelessWidget {
class MasterPasswordPage extends StatefulWidget {
const MasterPasswordPage({super.key});

@override
State<MasterPasswordPage> createState() => _MasterPasswordPageState();
}

class _MasterPasswordPageState extends State<MasterPasswordPage> {
TextEditingController directoryController = TextEditingController();

@override
Widget build(BuildContext context) {
return NavigationView(
Expand All @@ -20,11 +28,34 @@ class MasterPasswordPage extends StatelessWidget {
),
),
content: Center(
child: Button(
onPressed: () {
context.goNamed(AppRoute.validatorConfig.name);
},
child: Text('Navigate to ${AppRoute.validatorConfig.name}'),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 16,
children: [
SizedBox(
width: 220,
child: ExcludeSemantics(
child: TextBox(
controller: directoryController,
placeholder: 'Password',
autofocus: true,
decoration: WidgetStateProperty.all(
BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(5),
),
),
),
),
),
Button(
onPressed: () {
NodeConfigData.instance.password = directoryController.text;
context.goNamed(AppRoute.validatorConfig.name);
},
child: Text('Navigate to ${AppRoute.validatorConfig.name}'),
),
],
),
),
);
Expand Down
Loading