mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
Bundles wownerod (aarch64-linux-android, built via monero depends) as jniLibs libwownerod.so; EmbeddedNode.java execs it from nativeLibraryDir (the exec-exempt path), binds RPC on 127.0.0.1:34568 for the wallet, pruned mode by default, capped concurrency for phone thermals. Dart EmbeddedNode service (start/stop/status/waitUntilReady) drives it over a method channel. Verified: builds into the APK (binary bundled, arm64), and the daemon executes on real hardware (moto g stylus) — inits core, binds RPC, connects peers, and syncs blocks. UI toggle + foreground-service lifecycle are the next layer; binary is provisioned like the wallet libs (gitignored).
102 lines
3.2 KiB
Dart
102 lines
3.2 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// Drives the on-device Wownero node daemon (wownerod) bundled in the APK.
|
|
///
|
|
/// Android only for now — the native side execs the daemon from the app's
|
|
/// nativeLibraryDir. On any other platform every call is a no-op returning a
|
|
/// not-running state, so callers can use it unconditionally.
|
|
class EmbeddedNode {
|
|
EmbeddedNode._();
|
|
|
|
static const _channel = MethodChannel('cash.hashbags/embedded_node');
|
|
|
|
static bool get supported => Platform.isAndroid;
|
|
|
|
/// The wallet connects to the local node at this address once it's up.
|
|
static Future<String> address() async {
|
|
final port = supported ? (await _channel.invokeMethod<int>('rpcPort') ?? 34568) : 34568;
|
|
return '127.0.0.1:$port';
|
|
}
|
|
|
|
/// Start the daemon. Pruned mode (~3.5GB) is the on-phone default; full is
|
|
/// ~11GB. Returns true if the node is (now) running.
|
|
static Future<bool> start({bool pruned = true}) async {
|
|
if (!supported) return false;
|
|
return await _channel.invokeMethod<bool>('start', {'pruned': pruned}) ?? false;
|
|
}
|
|
|
|
static Future<void> stop() async {
|
|
if (!supported) return;
|
|
await _channel.invokeMethod<void>('stop');
|
|
}
|
|
|
|
static Future<EmbeddedNodeStatus> status() async {
|
|
if (!supported) return const EmbeddedNodeStatus.stopped();
|
|
final m = await _channel.invokeMapMethod<String, dynamic>('progress');
|
|
if (m == null) return const EmbeddedNodeStatus.stopped();
|
|
return EmbeddedNodeStatus(
|
|
running: m['running'] == true,
|
|
height: (m['height'] as num?)?.toInt() ?? 0,
|
|
targetHeight: (m['targetHeight'] as num?)?.toInt() ?? 0,
|
|
synced: m['synced'] == true,
|
|
);
|
|
}
|
|
|
|
static Future<String> logs() async {
|
|
if (!supported) return '';
|
|
return await _channel.invokeMethod<String>('logs') ?? '';
|
|
}
|
|
|
|
/// Poll status until the RPC is reachable (or [timeout]); lets a caller wait
|
|
/// before pointing the wallet at the local node.
|
|
static Future<bool> waitUntilReady(
|
|
{Duration timeout = const Duration(seconds: 90)}) async {
|
|
final deadline = DateTime.now().add(timeout);
|
|
while (DateTime.now().isBefore(deadline)) {
|
|
final s = await status();
|
|
if (!s.running) return false;
|
|
if (await _rpcReachable()) return true;
|
|
await Future<void>.delayed(const Duration(seconds: 2));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static Future<bool> _rpcReachable() async {
|
|
try {
|
|
final port = supported ? (await _channel.invokeMethod<int>('rpcPort') ?? 34568) : 34568;
|
|
final socket = await Socket.connect('127.0.0.1', port,
|
|
timeout: const Duration(seconds: 2));
|
|
socket.destroy();
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
class EmbeddedNodeStatus {
|
|
final bool running;
|
|
final int height;
|
|
final int targetHeight;
|
|
final bool synced;
|
|
|
|
const EmbeddedNodeStatus({
|
|
required this.running,
|
|
required this.height,
|
|
required this.targetHeight,
|
|
required this.synced,
|
|
});
|
|
|
|
const EmbeddedNodeStatus.stopped()
|
|
: running = false,
|
|
height = 0,
|
|
targetHeight = 0,
|
|
synced = false;
|
|
|
|
double get progress =>
|
|
targetHeight > 0 ? (height / targetHeight).clamp(0.0, 1.0) : 0.0;
|
|
}
|