Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/komodo_coins/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies:
komodo_defi_types: ^0.3.2+1
logging: ^1.3.0
path: ^1.9.1
path_provider: ^2.1.4
path_provider: ^2.1.5

dev_dependencies:
build_runner: ^2.4.14
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ class KdfOperationsLocalExecutable implements IKdfOperations {
Duration startupTimeout = const Duration(seconds: 30),
KdfExecutableFinder? executableFinder,
this.executableName = 'kdf',
}) : _startupTimeout = startupTimeout,
_executableFinder =
executableFinder ?? KdfExecutableFinder(logCallback: _logCallback);
}) : _startupTimeout = startupTimeout,
_executableFinder =
executableFinder ?? KdfExecutableFinder(logCallback: _logCallback);

factory KdfOperationsLocalExecutable.create({
required void Function(String) logCallback,
Expand Down Expand Up @@ -72,10 +72,9 @@ class KdfOperationsLocalExecutable implements IKdfOperations {
static final Uri _url = Uri.parse('http://127.0.0.1:7783');

Future<Process> _startKdf(JsonMap params) async {
final executablePath =
(await _executableFinder.findExecutable(executableName: executableName))
?.absolute
.path;
final executablePath = (await _executableFinder.findExecutable(
executableName: executableName,
))?.absolute.path;
if (executablePath == null) {
throw KdfException(
'KDF executable not found in any of the expected locations. '
Expand Down Expand Up @@ -115,11 +114,9 @@ class KdfOperationsLocalExecutable implements IKdfOperations {
final environment = Map<String, String>.of(Platform.environment)
..['MM_COINS_PATH'] = coinsConfigFile.path;

final newProcess = await Process.start(
executablePath,
[sensitiveArgs.toJsonString()],
environment: environment,
);
final newProcess = await Process.start(executablePath, [
sensitiveArgs.toJsonString(),
], environment: environment);

_logCallback('Launched executable: $executablePath');
_attachProcessListeners(newProcess, coinsTempDir);
Expand Down Expand Up @@ -195,11 +192,9 @@ class KdfOperationsLocalExecutable implements IKdfOperations {
}

final coinsCount = params.valueOrNull<List<dynamic>>('coins')?.length;
_logCallback('Starting KDF with parameters: ${{
...params,
'coins': '{{OMITTED $coinsCount ITEMS}}',
'log_level': logLevel ?? 3,
}.censored().toJsonString()}');
_logCallback(
'Starting KDF with parameters: ${{...params, 'coins': '{{OMITTED $coinsCount ITEMS}}', 'log_level': logLevel ?? 3}.censored().toJsonString()}',
);

try {
_process = await _startKdf(params);
Expand Down Expand Up @@ -247,9 +242,9 @@ class KdfOperationsLocalExecutable implements IKdfOperations {
Future<StopStatus> kdfStop() async {
var stopStatus = StopStatus.ok;
try {
stopStatus = await _kdfRemote
.kdfStop()
.catchError((_) => StopStatus.errorStopping);
stopStatus = await _kdfRemote.kdfStop().catchError(
(_) => StopStatus.errorStopping,
);

if (_process == null || _process?.pid == 0) {
_logCallback('Process is not running, skipping shutdown.');
Expand Down Expand Up @@ -302,4 +297,39 @@ class KdfOperationsLocalExecutable implements IKdfOperations {
);
}
}

@override
void dispose() {
// Cancel and clean up subscriptions
stdoutSub?.cancel().ignore();
stdoutSub = null;
stderrSub?.cancel().ignore();
stderrSub = null;

// Gracefully stop the process if running
final capturedProcess = _process;
if (capturedProcess != null) {
_kdfRemote.kdfStop().timeout(const Duration(seconds: 3)).ignore();
unawaited(_gracefulProcessShutdown(capturedProcess));
}

// Clean up remote resources
_kdfRemote.dispose();
}

Future<void> _gracefulProcessShutdown(Process capturedProcess) async {
try {
await capturedProcess.exitCode
.timeout(const Duration(seconds: 5))
.catchError((_) {
capturedProcess.kill();
return -1; // Return an int to match Future<int>
});
} finally {
// Only set _process = null if it still equals the captured instance
if (_process == capturedProcess) {
_process = null;
}
}
}
}
2 changes: 1 addition & 1 deletion packages/komodo_defi_framework/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencies:
logging: ^1.3.0
mutex: ^3.1.0
path: ^1.9.1
path_provider: ^2.1.4
path_provider: ^2.1.5
plugin_platform_interface: ^2.0.2
web: ^1.1.0

Expand Down
3 changes: 3 additions & 0 deletions packages/komodo_defi_sdk/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ analyzer:
errors:
use_if_null_to_convert_nulls_to_bools: ignore
omit_local_variable_types: ignore

# Required to use jsonserializable with freezed
invalid_annotation_target: ignore
5 changes: 4 additions & 1 deletion packages/komodo_defi_sdk/build.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
targets:
$default:
sources:
- lib/**
- pubspec.yaml
builders:
hive_ce_generator:
enabled: true
generate_for:
- lib/src/activation_config/hive_adapters.dart
- lib/src/**.dart
4 changes: 2 additions & 2 deletions packages/komodo_defi_sdk/example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ android {
ndkVersion = flutter.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

defaultConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import 'package:kdf_sdk_example/widgets/assets/instance_assets_list.dart';
import 'package:kdf_sdk_example/widgets/common/private_keys_display_widget.dart';
import 'package:kdf_sdk_example/widgets/common/security_warning_dialog.dart';
import 'package:kdf_sdk_example/widgets/instance_manager/kdf_instance_state.dart';
import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart';
import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:kdf_sdk_example/widgets/instance_manager/zhtlc_config_dialog.dart';
import 'package:komodo_defi_types/komodo_defi_type_utils.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';

Expand Down Expand Up @@ -217,7 +216,12 @@ class _LoggedInViewWidgetState extends State<LoggedInViewWidget> {
final existing = await sdk.activationConfigService
.getSavedZhtlc(asset.id);
if (existing == null && mounted) {
final config = await _showZhtlcConfigDialog(context, asset);
final config =
await ZhtlcConfigDialogHandler.handleZhtlcConfigDialog(
context,
asset,
);
if (!mounted) return;
if (config != null) {
await sdk.activationConfigService.saveZhtlcConfig(
asset.id,
Expand All @@ -236,164 +240,4 @@ class _LoggedInViewWidgetState extends State<LoggedInViewWidget> {
],
);
}

Future<ZhtlcUserConfig?> _showZhtlcConfigDialog(
BuildContext context,
Asset asset,
) async {
final zcashPathController = TextEditingController();
final blocksPerIterController = TextEditingController(text: '1000');
final intervalMsController = TextEditingController(text: '0');

String syncType = 'date'; // earliest | height | date
final syncValueController = TextEditingController(
text:
(DateTime.now()
.subtract(const Duration(days: 2))
.millisecondsSinceEpoch ~/
1000)
.toString(),
);

ZhtlcUserConfig? result;

await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) {
return StatefulBuilder(
builder: (context, setInnerState) {
return AlertDialog(
title: Text('Configure ${asset.id.name}'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: zcashPathController,
decoration: const InputDecoration(
labelText: 'Zcash parameters path',
helperText: 'Folder containing sapling params',
),
),
const SizedBox(height: 12),
TextField(
controller: blocksPerIterController,
decoration: const InputDecoration(
labelText: 'Blocks per iteration',
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 12),
TextField(
controller: intervalMsController,
decoration: const InputDecoration(
labelText: 'Scan interval (ms)',
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 12),
Row(
children: [
const Text('Start sync from:'),
const SizedBox(width: 12),
DropdownButton<String>(
value: syncType,
items: const [
DropdownMenuItem(
value: 'earliest',
child: Text('Earliest (sapling)'),
),
DropdownMenuItem(
value: 'height',
child: Text('Block height'),
),
DropdownMenuItem(
value: 'date',
child: Text('Unix timestamp'),
),
],
onChanged: (v) {
if (v == null) return;
setInnerState(() => syncType = v);
},
),
const SizedBox(width: 8),
if (syncType != 'earliest')
Expanded(
child: TextField(
controller: syncValueController,
decoration: InputDecoration(
labelText: syncType == 'height'
? 'Block height'
: 'Unix timestamp (sec)',
),
keyboardType: TextInputType.number,
),
),
],
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () {
final path = zcashPathController.text.trim();
if (path.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Zcash params path is required'),
),
);
return;
}

ZhtlcSyncParams? syncParams;
if (syncType == 'earliest') {
syncParams = ZhtlcSyncParams.earliest();
} else {
final v = int.tryParse(syncValueController.text.trim());
if (v == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
syncType == 'height'
? 'Enter a valid height'
: 'Enter a valid unix timestamp (seconds)',
),
),
);
return;
}
syncParams = syncType == 'height'
? ZhtlcSyncParams.height(v)
: ZhtlcSyncParams.date(v);
}

result = ZhtlcUserConfig(
zcashParamsPath: path,
scanBlocksPerIteration:
int.tryParse(blocksPerIterController.text) ?? 1000,
scanIntervalMs:
int.tryParse(intervalMsController.text) ?? 0,
syncParams: syncParams,
);
Navigator.of(context).pop();
},
child: const Text('Save'),
),
],
);
},
);
},
);

return result;
}
}
Loading
Loading