mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
feat(demo): funded-wallet restore + build-arg fixes
Adds DEMO_WALLET_SEED/DEMO_WALLET_HEIGHT support so a demo build restores a
fixed (throwaway, funded) Wownero wallet instead of creating a fresh one, via
WalletRestoreViewModel.create. Balance lives on-chain against the seed, so it
survives any emulator wipe and is reproducible.
Three real bugs fixed on the way:
- build-apk.sh built the --dart-define args as a word-split string; the 16-word
seed's internal spaces split into separate argv tokens and flutter treated a
seed word as a build target ("Target file '<word>' not found"). Now a bash
array keeps each --dart-define one token.
- WalletRestoreViewModel.getCredentials reads name/passphrase from the options
MAP (not the vm field) and casts name as non-null String, so the restore
options must include 'name' and 'passphrase'.
- forward DEMO_WALLET_SEED/HEIGHT into the build container; source demo.env.
Verified end to end: demo build restores the wallet, its address matches the
funded one exactly (QR-decoded), it syncs at height 861800, and the dashboard
shows the real 13.37 WOW balance + tx history. Send shows Max 13.37.
The seed lives only in gitignored tools/demo/demo.env (never committed).
docs/internal-notes.md (gitignored) documents the FailedDecryption gotcha.
This commit is contained in:
@@ -244,3 +244,6 @@ scripts/windows/setup-windows-runner.ps1
|
||||
# committed template is tools/demo/demo.env.example.
|
||||
tools/demo/demo.env
|
||||
__pycache__/
|
||||
|
||||
# Internal engineering notes (not for the public repo)
|
||||
docs/internal-notes.md
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:hash_wallet/entities/preferences_key.dart';
|
||||
import 'package:hash_wallet/entities/seed_type.dart';
|
||||
import 'package:hash_wallet/store/authentication_store.dart';
|
||||
import 'package:hash_wallet/view_model/wallet_new_vm.dart';
|
||||
import 'package:hash_wallet/view_model/wallet_restore_view_model.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Whether this build was launched in capture/demo mode.
|
||||
@@ -34,6 +35,17 @@ const String _demoPin = String.fromEnvironment('DEMO_PIN', defaultValue: '0801')
|
||||
/// Name of the throwaway wallet the demo build creates on first launch.
|
||||
const String _demoWalletName = 'Demo Wallet';
|
||||
|
||||
/// Optional fixed seed. When set, the demo build RESTORES this exact wallet
|
||||
/// instead of creating a fresh one — so a funded throwaway wallet is
|
||||
/// reproducible and its balance (which lives on-chain against the seed)
|
||||
/// survives any emulator wipe. THROWAWAY only; never a real wallet. Supplied
|
||||
/// via --dart-define, kept out of git (tools/demo/demo.env).
|
||||
const String _demoSeed = String.fromEnvironment('DEMO_WALLET_SEED');
|
||||
|
||||
/// Restore height for the seed above. Set it near the wallet's creation block
|
||||
/// so sync is fast; 0 scans from genesis (slow).
|
||||
const int _demoHeight = int.fromEnvironment('DEMO_WALLET_HEIGHT', defaultValue: 0);
|
||||
|
||||
/// Bring the app up straight into a usable, unlocked wallet so the capture
|
||||
/// harness never has to drive onboarding or the PIN pad (whose synthetic taps
|
||||
/// are unreliable under the headless emulator).
|
||||
@@ -65,6 +77,30 @@ Future<void> maybeSetupDemoWallet() async {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_demoSeed.trim().isNotEmpty) {
|
||||
// Reproducible funded wallet: restore the fixed seed. WalletRestoreView
|
||||
// Model shares WalletCreationVM.create, so it builds walletInfo/paths and
|
||||
// finalizes (changeCurrentWallet + allowedCreate) just like the new-wallet
|
||||
// flow.
|
||||
final vm = getIt.get<WalletRestoreViewModel>(
|
||||
param1: WalletType.wownero, param2: null);
|
||||
vm.name = _demoWalletName;
|
||||
// getCredentials() reads name/passphrase FROM the options map (not the vm
|
||||
// field), and casts name as a non-null String — so it must be present.
|
||||
await vm.create(options: <String, dynamic>{
|
||||
'name': _demoWalletName,
|
||||
'seed': _demoSeed.trim(),
|
||||
'height': _demoHeight,
|
||||
'passphrase': '',
|
||||
});
|
||||
if (vm.state is! ExecutedSuccessfullyState) {
|
||||
throw Exception('wallet restore did not finish (${vm.state.runtimeType})');
|
||||
}
|
||||
printV('[demo] restored Wownero wallet from DEMO_WALLET_SEED '
|
||||
'(height $_demoHeight)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Fresh Wownero wallet via the real create flow. WalletNewVM.create builds
|
||||
// the WalletInfo, derivation, and paths, creates + saves the wallet, calls
|
||||
// AppStore.changeCurrentWallet (persisting currentWalletName/type) and
|
||||
|
||||
+23
-6
@@ -19,6 +19,10 @@
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
# Pull in local capture config (DEMO_WALLET_SEED/HEIGHT/PIN). Gitignored.
|
||||
_DEMO_ENV="$(dirname "${BASH_SOURCE[0]}")/demo.env"
|
||||
if [[ -f "$_DEMO_ENV" ]]; then set -a; . "$_DEMO_ENV"; set +a; fi
|
||||
IMAGE="${CI_IMAGE:-ghcr.io/cake-tech/cake_wallet:debian13-flutter3.32.0-ndkr28-go1.24.1-ruststablenightly}"
|
||||
BUILD_MODE="${BUILD_MODE:-debug}"
|
||||
ABI="${ABI:-x86_64}"
|
||||
@@ -47,6 +51,8 @@ docker run --rm \
|
||||
-e TARGET_PLATFORM="$TARGET_PLATFORM" \
|
||||
-e DEMO="${DEMO:-0}" \
|
||||
-e DEMO_PIN="${DEMO_PIN:-0801}" \
|
||||
-e DEMO_WALLET_SEED="${DEMO_WALLET_SEED:-}" \
|
||||
-e DEMO_WALLET_HEIGHT="${DEMO_WALLET_HEIGHT:-0}" \
|
||||
-e HOST_UID="$HOST_UID" \
|
||||
-e HOST_GID="$HOST_GID" \
|
||||
"$IMAGE" bash -c '
|
||||
@@ -214,17 +220,28 @@ step "compile svg assets"
|
||||
./compile_graphics.sh
|
||||
|
||||
step "build apk ($BUILD_MODE / ${TARGET_PLATFORM:-universal})"
|
||||
EXTRA=""
|
||||
[[ -n "$TARGET_PLATFORM" ]] && EXTRA="--target-platform=$TARGET_PLATFORM"
|
||||
# DEMO=1 bakes in DEMO_MODE so the app boots straight to an unlocked demo
|
||||
# wallet (kDebugMode-gated, impossible in release). Lets capture skip the
|
||||
# onboarding/PIN flow entirely.
|
||||
DEMO_DEFINES=""
|
||||
# Use an ARRAY, not a string: the seed contains spaces (16 words), so a
|
||||
# word-split string turns "--dart-define=DEMO_WALLET_SEED=word1 word2 ..."
|
||||
# into separate argv tokens and flutter treats "unlock" as a target file
|
||||
# ("Target file 'unlock' not found."). Array elements preserve the spaces.
|
||||
DEMO_DEFINES=()
|
||||
if [[ "${DEMO:-0}" == "1" ]]; then
|
||||
DEMO_DEFINES="--dart-define=DEMO_MODE=true --dart-define=DEMO_PIN=${DEMO_PIN:-0801}"
|
||||
echo "DEMO_MODE baked in"
|
||||
DEMO_DEFINES+=(--dart-define=DEMO_MODE=true "--dart-define=DEMO_PIN=${DEMO_PIN:-0801}")
|
||||
if [[ -n "${DEMO_WALLET_SEED:-}" ]]; then
|
||||
DEMO_DEFINES+=("--dart-define=DEMO_WALLET_SEED=${DEMO_WALLET_SEED}")
|
||||
DEMO_DEFINES+=("--dart-define=DEMO_WALLET_HEIGHT=${DEMO_WALLET_HEIGHT:-0}")
|
||||
echo "DEMO_MODE baked in (restore from seed, height ${DEMO_WALLET_HEIGHT:-0})"
|
||||
else
|
||||
echo "DEMO_MODE baked in (fresh wallet)"
|
||||
fi
|
||||
fi
|
||||
flutter build apk --dart-define-from-file=env.json --$BUILD_MODE $EXTRA $DEMO_DEFINES
|
||||
EXTRA_ARGS=()
|
||||
[[ -n "$TARGET_PLATFORM" ]] && EXTRA_ARGS+=("--target-platform=$TARGET_PLATFORM")
|
||||
flutter build apk --dart-define-from-file=env.json --$BUILD_MODE \
|
||||
"${EXTRA_ARGS[@]}" "${DEMO_DEFINES[@]+"${DEMO_DEFINES[@]}"}"
|
||||
|
||||
echo
|
||||
echo "==> artifacts:"
|
||||
|
||||
Reference in New Issue
Block a user