Commit Graph
3985 Commits
Author SHA1 Message Date
jwinterm 25acfe656b ci(windows): pin Flutter 3.32.0 like every other platform
Windows took whatever Flutter the runner happened to have. On 2026-09-19 that
was 3.38.5 with Dart 3.10.4, and Dart 3.10 removed the _macros package, so pub
could not solve hive_generator against build_resolvers:

  macros >=0.1.3-main.0 depends on _macros from sdk which doesn't exist
  So, because hash_wallet depends on both build_resolvers ^2.4.4 and
  hive_generator ^2.0.1, version solving failed.

The build died at exit 69 before compiling anything, on the same commit that
Android and Linux built successfully minutes earlier. They differ only in
that both run inside the cake_wallet debian13-flutter3.32.0 image, and iOS
installs 3.32.0 explicitly. Windows was the only platform whose dependency
resolution was decided by a machine somebody had updated.

It now checks for 3.32.0 and installs it when absent, the same two steps iOS
already uses. No app code changes; hash-bags-ios-1.0.3 stays at 0697f41c7.
2026-09-19 14:09:26 -04:00
jwinterm d703455ef2 ci(windows): export the Rust env for the step that checks it
The detection branch found the toolchain, wrote RUSTUP_HOME and CARGO_HOME to
$GITHUB_ENV, and then verified with `rustc --version` in the same step.
$GITHUB_ENV only reaches later steps, so that verification ran with neither
variable set and the rustup shim fell back to %USERPROFILE%\.rustup. On this
runner that path is a file, so it failed with os error 183 while a perfectly
good toolchain sat at C:\Rust.

The step's own comment already explained why the shims need RUSTUP_HOME. It
just never exported it for its own use. The install branch does export before
its check, which is why only the detection path broke, and why it broke on a
runner that already had Rust rather than on a fresh one.

No app code changes, so hash-bags-ios-1.0.3 stays where it is: Android, Linux
and iOS are building from that commit and the artifacts are unaffected.
2026-09-19 12:07:18 -04:00
jwinterm 0697f41c7e fix(brand): stop shipping Cake artwork, and 1.0.3+13
Audit of every surface that could show somebody else's logo. Most of it was
already ours: the transaction success screen draws hash_bag.png and the
hashbag_sym_* glyphs, cake_logo_light/dark.svg and the Android icon set were
redrawn from the Hash Bags mark in May and only kept their old filenames, no
user-visible string contains "Cake", and every cupcake reference is a
HardwareWalletType icon sitting next to Ledger and Trezor, which is interop
rather than branding.

Two places were not ours.

The Flatpak manifest installed cakewallet_icon_180.png as the Linux desktop
icon, and that file is upstream Cake artwork from 2025 that the rebrand never
touched. So the Linux build has been shipping a Cake Wallet icon. It now uses
the Hash Bags mark. The macOS icon script had the same fault, latent only
because no workflow builds macOS; it gets a proper Hash Bags set at all seven
sizes rather than a landmine for whoever turns that build on.

Deleted the Cake artwork nothing needs: cake_logo.png, cakepay.png, the three
cakewallet_icon_* files and the macOS set. assets/images/ is bundled whole, so
unreferenced files were shipping inside the binary regardless of whether any
code could draw them.

Also fixes a bug the audit turned up rather than a branding one. The theme
picker chose its preview icon by matching theme.title, which is built from the
accent colour's display name, so renaming an accent silently changed the icon.
It had: the list still named Cake Primary, Tron Red and Frosting Purple, none
of which ship, while our own Such Pink and Such Blue were absent and fell
through to the plain dark icon. It asks the theme what it is now, not what it
is called.

Version to 1.0.3+13. pubspec_description.yaml is the single source for all five
platforms; the checked-in pubspec.yaml is a build artifact.

Still there, reported not removed: cake_pay_icon.png, reachable only through
the Cake Pay order provider, which is the only provider, has an empty API key
and cannot produce an order in this app. Removing it means removing the orders
feature, which is not a branding change.
hash-bags-ios-1.0.3
2026-09-18 19:45:17 -04:00
jwinterm 75a7193da6 fix(reports): say which build produced the error
An error report that cannot be tied to a binary tells us a bug exists and
nothing about whether we already fixed it. Of the ten reports received since
May, five carry the Wownero addresses crash that shipped fixed in 1.0.2, and we
could only tell by reading timestamps buried in the traces themselves.

Two gaps, both closed here. The body carried "App Version: 1.0.0" without the
build number, and 1.0.0 shipped as Play builds 9 and 10 which encrypted under
different keys, so the version alone cannot identify the binary that wrote a
wallet we are being asked to heal.

And the body is the part of this flow we do not control. It reaches us only if
the user pastes what we put on their clipboard. On 2026-09-17 somebody typed a
sentence into the trace box instead, and the report arrived with no version, no
platform and no stack at all. So the app now also puts its version and platform
in the query string of the link it opens, where the form reads it rather than
the user. Someone who reaches the page without the app simply reports nothing,
which is honest and stays empty rather than guessing.
2026-09-18 13:09:54 -04:00
jwinterm 8e45de5928 ci(windows): Authenticode-sign HashWallet.exe with the hardware token
The Windows lane already runs on the machine that physically holds the Sectigo
EV code-signing token, but the runner there is NT SERVICE\act_runner, which
cannot use it: SafeNet holds the token login per user and an operator
establishes that login interactively. So the build queues the launcher for the
signing broker that Fleet installs (such-fleet roles/windows_code_signing) and
waits for it to come back signed.

CI never holds the private key -- it is non-exportable on FIPS 140-2 Level 2
hardware -- and never holds the token password. Sectigo allows three
consecutive failed password attempts before the token needs vendor unlocking,
and a job that never supplies a password cannot spend one.

Only HashWallet.exe is signed. The bundled MSVC runtime DLLs are already
Microsoft-signed and must not be re-signed, and the plugin DLLs are loaded by
an already-trusted process; it is the launcher the user double-clicks that
SmartScreen judges.

The signature, its RFC 3161 timestamp and the expected signer are verified on
the exe that actually goes into the zip, and any of the three failing fails the
build. If the broker's circuit breaker is tripped the job says so and stops
rather than shipping an unsigned binary.

Procedure: docs platform/windows-hardware-token-signing.md.
2026-08-31 18:00:18 -04:00
jwinterm 3a60b921f4 ui: fix grey Wownero coin logo + lift too-dark dashboard background
- res/pictures/balance_card_icons/wownero.svg had an opaque background rect
  (+#FFF5FD fill), so the ChainIcon's srcIn colorFilter flooded the whole
  circle solid grey. Replaced with a clean alpha glyph (white W-in-circle on
  transparent, like monero.svg) so the dark bg shows through the negative
  space. Source in res/pictures → compile_graphics.sh regenerates the .vec.
- dark theme surface #0B1F14 → #10281B (a bit less dark, per feedback)
2026-08-30 10:36:50 -04:00
jwinterm 6a41701656 feat(android): embedded Wownero node — run a full node on the phone
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).
2026-08-30 10:17:11 -04:00
jwinterm acccba9210 fix: remove duplicate useSSL key in polygon_node_list.yml (runtime crash)
package:yaml (used by the app) throws 'Duplicate mapping key' on the two
useSSL entries; PyYAML tolerated it so it slipped through. Adds a test that
parses every bundled YAML asset with package:yaml to prevent regression.
2026-08-30 04:55:28 -04:00
jwinterm 24face9432 fix: harden legacy wallet-password validation to exact generateKey() shape + test
- validate legacy-decoded wallet passwords are real generateKey() output
  (696 chars, base64 of 512-byte key + 8-byte iv) so a wrong AES key that
  unpads to printable bytes can't be re-saved as a bogus password
- test/legacy_decode_test.dart: proves 1.0.0->1.0.2 recovers PIN+password
  and 16k wrong keys yield zero false-accepts
hash-bags-ios-1.0.2
2026-08-29 15:08:40 -04:00
jwinterm 7fcef4e4ce fix: add 1.0.0 (build 9/10) legacy secret candidates recovered from the Play AAB
Cross-referencing both shipped binaries' string tables also separates
per-build randoms from shared library constants, trimming false candidates.
2026-08-29 14:58:58 -04:00
jwinterm d8aab70fa7 fix: 1.0.2 hotfix — heal wallets broken by per-build random crypto secrets
CI ran tool/generate_new_secrets.dart unpinned, so every release binary
shipped different AES keys/salts for the stored PIN and wallet passwords.
Updating the app then failed to decrypt what the previous build stored:
'Invalid or corrupted pad block' at PIN entry and the 'Corrupted seeds'
dialog, with wallet files fully intact.

- entities/encrypt.dart: legacy-key fallback decode for PIN and wallet
  password blobs (candidate sets recovered from the shipped 1.0.1 binary;
  output validated to reject PKCS7 false positives)
- key_service/auth_service: on successful legacy decode, re-encrypt under
  the pinned key — installs self-heal at first unlock
- all build workflows: inject the eight pinned CRYPTO_* repo secrets after
  secret generation and hard-fail if any is missing, so crypto salts can
  never silently rotate again (values pinned via
  ~keys/set-gitea-crypto-secrets.sh)
- bump 1.0.2+12
2026-08-29 14:53:18 -04:00
jwinterm 8db45c5842 ios: raise MinimumOSVersion to 15.0
App Store Connect warning 90068 on the last two uploads: from Spring
2027 all iOS apps must set MinimumOSVersion 15.0 or later. Raising from
13.0 costs almost no devices (iOS 15 still covers iPhone 6s and SE 1).
Rides the next iOS build.
2026-08-29 05:22:17 -04:00
jwinterm d19e52d308 ci(test): generate models and localization before analyzing
generated/i18n.dart and several .g.dart parts are untracked generation
outputs too; without them a fresh checkout analyzes with thousands of
undefined_identifier errors. Run model_generator.sh and
generate_localization exactly as the build lanes do.
2026-08-21 05:25:36 -04:00
jwinterm a144d037d5 ci(test): generate the untracked workspace before resolving
pubspec.yaml, the cw_* dependency wiring, wallet_types.g.dart, and the
.secrets.g.dart files are generated and untracked, so the workflow's
bare pub get failed on every push since the workflow landed. Mirror the
build lanes: description -> generate_pubspec -> coin configure ->
generate_new_secrets (empty defaults), then analyze and test. This also
makes the CI resolve identical to release builds, so descriptor drift
like the cake_backup ref conflict fails here in minutes instead of at
release-build time.
2026-08-21 05:00:41 -04:00
jwinterm 78bd866fa6 fix(deps): pin cake_backup and bech32 refs in cw_core to match root
Pub requires identical git descriptors across the graph: the root's
pinned SHAs (pubspec_base) conflicted with cw_core's ref:main /
unpinned declarations, failing CI's pub resolve after the configure
step regenerates pubspec.yaml. Local resolution had masked it because
the untracked working-tree pubspec.yaml predated the pins. Pins now
identical everywhere (audit script confirms no remaining multi-ref git
deps); sibling lockfiles re-locked.
2026-08-18 11:07:17 -04:00
jwinterm ae824d6455 fix: WOW crash on Receive -> Addresses (AccountPreviewHeader)
User report 05ebf86e: monero?.getCurrentAccount on a Wownero wallet. The
null-aware operator guards plugin presence, not wallet type, so the
Monero bridge always ran and CWMonero's cast threw before the ?? chain
could reach the wownero branch. Dispatch by wallet.type instead. Swept
lib for the same anti-pattern: account_customizer already dispatches
(fixed 2026-08-07) and the settings_store call sites take no wallet
argument; this was the last live instance.
2026-08-18 08:28:25 -04:00
jwinterm ca771f51d5 prices: non-USD fiat display works now; update endpoint comment
Server-side forex (ECB daily reference rates) shipped in
hash-wallet-prices; the wallet needed no code change. Comment updated so
the file stops claiming USD-only.
2026-08-16 00:09:23 -04:00
jwinterm 0b548ed8ca nodes: own node as Wownero default; fix Arbitrum RPC; drop keyless NOWNodes
- Wownero default becomes node.wownero.org:443 (SSL, ours) instead of a
  third-party plaintext host
- Arbitrum's default was arbitrum.nownodes.io, which requires an API key
  we don't ship (nowNodesApiKey is empty) — every fresh install had a dead
  default RPC; now arbitrum-one-rpc.publicnode.com
- remove the other NOWNodes entries from node lists and the EVM chain
  registry for the same reason; historical migration maps keep their
  entries so old users still get migrated off
2026-08-15 19:37:34 -04:00
jwinterm 1bd91f7ab3 exchange: curate the swap picker to an explicit allowlist
Deposit side: natives of shipped chains (WOW, XMR, BTC, BCH, DOGE, XNO,
ETH, ETH-Base, ETH-Arb, BNB, POL) plus major stables (USDT/USDC variants,
DAI). Receive side: same minus BNB (inherited upstream restriction) plus
LTC as a receive-only target. ZEC, DASH, SOL, AVAX, Zano, Haven, ADA,
EOS, XLM, XRP, TRX, TON and the rest disappear from the swap UI; their
CryptoCurrency entries stay defined because the raw ids back Hive-stored
trade history.
2026-08-15 19:37:34 -04:00
jwinterm 47680c1162 Merge gitea-builds/dev: sync CI remote merge history into dev 2026-08-15 16:30:21 -04:00
jwinterm 599dcd13d5 chore: bump version to 1.0.1+11 for the post-launch hardening release 2026-08-15 16:29:20 -04:00
jwinterm 21587d0c88 refactor(cw): dedupe mnemonic wordlists into cw_core
The 10 language wordlists were byte-identical between cw_monero and
cw_wownero (16,291 duplicated lines). A single copy now lives in
cw_core/lib/mnemonics/, and the configure.dart code-gen templates import
from there.
2026-08-15 16:29:07 -04:00
jwinterm adece963c3 refactor: delete dead Cake Pay/buy/exchange code; split di setup()
- Cake Pay: 36 files (~5.3k LOC) plus DI registrations, routes, and UI
  branches (feature was hard-disabled and unreachable)
- unreachable buy providers: robinhood, dfx, onramper, meld, kryptonim
  (~1.7k LOC); MoonPay untouched
- exchange providers that only existed as commented-out wiring:
  changenow, sideshift, chainflip, exolix, swaptrade, letsexchange,
  stealthex, xoswap, swapsxyz, thorchain, simpleswap (~5.2k LOC);
  Trocador untouched; ExchangeProviderDescription values and Hive
  adapters kept so stored trades and orders still deserialize
- di.dart setup() split from one 1,452-line function into 10 sequential
  chunk functions; registration execution order verified identical
  before and after (251 register calls)
2026-08-15 16:29:03 -04:00
jwinterm 71706314fb privacy: only domain-anchored address resolvers; drop Cake endpoints
Address-field input no longer leaves the device except toward
infrastructure implied by the typed name itself:
- remove Twitter, Mastodon, Nostr, Yat, Unstoppable Domains, FIO,
  ThorChain-name, zcash.me, and Zano alias lookups (Nostr and FIO ran
  with no setting gate at all; Zano is not even a supported chain)
- keep OpenAlias, BIP-353, .well-known, LNURL, and ENS, each toggleable
  in Settings -> Domain lookups
- BTC fee/block/restore-height queries move from mempool.cakewallet.com
  to public mempool.space (also fixes two /api/v1 paths that 404 on
  standard mempool deployments)
- PRIVACY.md updated to disclose the remaining flows (mempool.space,
  name lookups, opt-in silent payments and payjoin); issue templates and
  the translation prompt de-Caked
2026-08-15 16:28:54 -04:00
jwinterm 77ae91e695 ci: run analyze + tests on every push/PR; make the suite real
- delete the stock Flutter counter widget_test (always failed) and the
  lightning invoice tests (network-dependent, and the feature is off)
- fix rotted assertions: removed-chain wallet types (solana, tron, zano,
  decred) and mainnet-only BTC/LTC pattern expectations
- new test.yml workflow: pub get + analyze + test on push/PR to dev/main
- analysis_options: drop excludes for deleted paths, stop excluding the
  five live coin facades, enable empty_catches and 7 other correctness
  lints (still 0 analyzer errors)
- integration_test_runner: clean hash_wallet data dirs, not cake_wallet
2026-08-15 16:28:44 -04:00
jwinterm 08fb925cea fix: wownero P0s, fail-closed RNG, iOS scheme, dep pins
- resetToDefault omitted Wownero: the reset button null-crashed and left
  WOW wallets with an empty node list
- accountLabel was missing a return and rendered blank for WOW wallets
- sec_random now throws instead of returning empty bytes when the native
  entropy channel fails or under-delivers
- iOS builds no longer register the leftover cakewallet:// URL scheme
- WOW fee estimate now fetched from the connected daemon's
  get_fee_estimate RPC and cached; the inherited XMR constant table
  remains only as a pre-fetch fallback
- pin cake_backup, flutter_local_authentication, and bech32 git deps to
  their locked SHAs for reproducible builds
2026-08-15 16:28:39 -04:00
jwinterm 94bf0e8c41 ci(windows): enable Developer Mode for plugin symlink support
flutter build windows materialises plugins under
windows/flutter/ephemeral/.plugin_symlinks/ as NTFS symlinks. Flutter gates
this on the AppModelUnlock registry key rather than the actual privilege, so
the build failed with "Building with plugins requires symlink support".

Sets AllowDevelopmentWithoutDevLicense=1 in HKLM (what the Settings toggle
does); LocalSystem can write it, so no interactive login. Idempotent.
2026-08-07 12:38:36 -04:00
jwinterm 7ea02dcfda nodes: add node.wownero.org and wow.smirk.cash on :443 2026-08-07 12:33:55 -04:00
such-gitea 47be14ca03 Merge pull request 'ci: Windows build — ensure Rust toolchain is on PATH' (#40) from github-such-software/hash-wallet:dev into dev
Reviewed-on: Builds/hash-wallet#40
2026-08-07 11:56:12 -04:00
jwinterm 64642818f8 ci: update Windows build workflow 2026-08-07 10:29:15 -04:00
such-gitea d15da0d1ea merge upstream 2026-08-07 06:53:18 -04:00
jwinterm 05bef0c14c chore: bump build number to 1.0.0+10 for TestFlight/Play (replaces build 9) 2026-08-07 05:12:47 -04:00
jwinterm 852f8d536d tools(demo): store-screenshot composers (App Store, 13" iPad, Play 2:1)
storeshots.py (website-dark theme), storeshots_ipad.py (2048x2732), and
storeshots_play.py (1290x2580, Play's 2:1 cap). Compose the funded demo captures
onto the Hash Bags brand background for the store listings.
2026-08-07 03:31:55 -04:00
jwinterm 4818cdf017 brand: de-cake new-ui asset filenames + drop unused cake art
Rename the new-ui assets (source in res/pictures + compiled .svg.vec in
assets/new-ui) and their 3 code refs. Content was already Hash Bags; only the
filenames were cake:
- cake_squircle_icon      -> hash_squircle_icon   (About page)
- cakewallet-wordmark(.light) -> hash-wordmark(.light) (receive QR)
- cake-setting            -> hash-setting          (settings)

Remove unused cake assets (0 refs): by-cakelabs, cakelabs-icon, new-ui cupcake
duplicate, cake_icon.png, cake_wordmark.png. (assets/images/cupcake* kept — that
is Cake's Cupcake hardware-signer device name, a real integration, not branding.)
2026-08-07 03:31:32 -04:00
jwinterm 2e33f8324e feat(send): animated hash-bag on the "Transaction Sent" screen
Replace the static success illustration with HashBagSuccessAnimation: the money
sack sits at the bottom and #/$/₿ glyphs erupt from its mouth, rising and fading
on a loop. Glyphs are PNG assets (the app bundles no font with a ₿ glyph).

InfoPage gains an overridable buildIllustration() hook (default unchanged:
static HashImageWidget); only TransactionSuccessPage overrides it, so every
other InfoPage is untouched.

NOTE: written without a local Flutter build (Dart pub can't resolve here) —
verify on the next CI/device build. Self-contained and revertible; the static
hash_bag.png remains the fallback via imageLightPath/imageDarkPath.
2026-08-07 03:02:37 -04:00
jwinterm 7e368f92e6 brand: de-cake the app (hash-bag success art, asset + widget renames, filter, icon padding)
Send-success art: replace the Cake birthday cake with a Hash Bags money sack
that has a # on it and erupts #/$/₿ (assets/images/hash_bag.png). Used by the
success page, send-confirm sheet, exchange-trade, and dEURO savings.

Asset renames (files + refs):
- birthday_cake.png            -> hash_bag.png
- restore_cupcake(.png/_dark)  -> restore_hashbag.png
- qr-cake.png                  -> qr-hashbag.png
- arrow_bottom_cake_green.png  -> arrow_bottom_green.png (+ 2.0x/3.0x)

Identifier renames:
- CakeImageWidget -> HashImageWidget (widget + file + 93 refs)
- typography _cakeRegular/_cakeBold/_cakeSemiBold -> _hash*

Transaction filter: drop the Cake Pay "Orders" row and every non-Trocador
trade provider; the swap gate is already Trocador-only, so this just aligns the
filter UI with reality.

Android launcher: add breathing room to the adaptive-foreground # (all
densities, monochrome included); iOS icon unchanged.

About page: swap the Cake easter eggs for Hash Bags ones.

Deferred (needs a build to verify): new-ui .svg.vec filename renames
(cake_squircle_icon, cakewallet-wordmark, cake-setting) — already Hash Bags in
content, only the filenames are cake, and they route through the .vec precompile.
2026-08-07 02:57:29 -04:00
jwinterm 6dc3da4621 feat(demo): App Store screenshot composer (storeshots.py)
Composes App-Store-ready 1290x2796 (6.9" iPhone) screenshots from the raw
1080x1920 harness captures: crops the mobile status bar (per-screen — deeper
for bottom-sheet modals), frames each capture with rounded corners + soft
shadow on the Hash Bags brand gradient, and burns a headline + subhead.

Copy is factual and benefit-driven (no price/return/"investment" claims) to
stay inside App Store Review 3.1.5 and the no-misleading-claims rules. Reads
brand colors/fonts from packs/hash/pack.json; needs Pillow.

Output (funded demo wallet, real 13.37 WOW): dashboard / receive / send / swap
under ~/Build/hash-wallet/demo/appstore/. Same source works for the Play
listing; frame + resize for other required device sizes as needed.
2026-08-03 15:56:12 -04:00
such-gitea 52f926ef35 Merge pull request 'Dev' (#39) from github-such-software/hash-wallet:dev into dev
Reviewed-on: Builds/hash-wallet#39
2026-08-03 15:46:56 -04:00
jwinterm 985cbf0064 fix(ios): recover wallet password across Keychain param changes; no crash
getWalletPassword force-unwrapped the secure-storage read, so a null iOS
Keychain result (item written by an older build under different Keychain
params, e.g. after a TestFlight update) hard-crashed the app and the wallet
showed "Corrupted seeds" — reported by a tester whose older Wownero and BTC
wallets broke on update.

Now it:
- falls back to the legacy readNoIOptions path (queries the OTHER iOS Keychain
  accessibility), which can AUTO-RECOVER such wallets without a seed restore;
- falls back to the #__<name>_bak__# backup password the Monero migration
  writes before re-encrypting;
- throws a catchable error instead of a null-check crash, so the loader can
  offer seed/restore recovery rather than the app dying.

Only changes behavior on the previously-crashing null path — strictly safer.

Not the file cipher (iOS is always Salsa20) nor the Monero migration
(Wownero/BTC don't run it): the wallet files are intact, only the Keychain
password was unreadable. flutter_secure_storage stays pinned (ca897a08) and the
accessibility stays first_unlock; those must not change without a migration
(see docs/internal-notes.md).
2026-08-03 14:48:29 -04:00
jwinterm 2afcf010e1 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.
2026-08-03 13:53:13 -04:00
jwinterm 4dd15704b0 feat(demo): honor SCREENSHOT_MODE as alias for the iOS UITest path
The iOS fastlane snapshot UITest already passes --dart-define=SCREENSHOT_MODE=
true; accept it as an alias for DEMO_MODE so the Mac screenshot path drives the
same demo-wallet boot without changing the UITest. Trivial const OR; validated
on next build.
2026-08-03 08:36:30 -04:00
jwinterm 4be2424486 feat(demo): DEMO_MODE — boot straight to an unlocked wallet for capture
Adds a debug-only capture mode so screenshots/video never depend on the
onboarding/PIN flow, whose synthetic taps are unreliable under the headless
emulator (both flutter_test and adb input drop taps on the PIN confirm screen).

lib/entities/demo_mode.dart: on launch, when kDebugMode AND
--dart-define=DEMO_MODE=true, create a fresh throwaway Wownero wallet through
the real WalletNewVM.create() (so walletInfo/derivation/paths/persistence and
changeCurrentWallet all happen exactly as in the UI), then set the PIN and
allow auth — landing on the dashboard. Reuses the wallet on later launches.
Both halves are compile-time const/kDebugMode, so this is tree-shaken out of
release: DEMO_MODE cannot be enabled in a store binary.

lib/main.dart: call maybeSetupDemoWallet() during bootstrap and force the
initial route to the dashboard in demo mode.

tools/demo/{build-apk,capture}.sh: DEMO=1 bakes the define into a local build;
flutter drive passes it so scenarios start at the dashboard.

Verified end to end: demo build boots to a live Wownero wallet and captured a
clean 1080x1920 store set (dashboard/receive/send/swap/settings) with no PIN.
This also implements the SCREENSHOT_MODE the iOS UITest scaffold already
references (store/SCREENSHOTS.md Path B).

Note: the API-35 emulator needs -gpu host (real GPU) for deep-page rendering;
-gpu software hard-crashes qemu on the Receive page. Stills use screencap
(screenrecord's encoder is unrelated).
2026-08-03 08:33:22 -04:00
jwinterm 731adb84f1 fix(demo): API35 capture recipe, reown unshallow, deferred recording, device match
Hardening from the first real end-to-end runs against the app:
- build-apk.sh: guard bitbox on the api.aar artifact (not the dir); unshallow
  the reown_flutter tarball so prepare_reown.sh can checkout its pinned commit
  (the tarball ships a shallow repo -> "unable to read tree"); generate reown
  uniffi bindings when absent.
- capture.sh boot: API 35 + `-gpu software -feature -Vulkan`. API 36.1's guest
  encoder is broken (screenrecord err=-38, screencap DMA assert) under both
  swiftshader_indirect and -gpu host; API 35 works.
- capture.sh: defer screenrecord until the app is foregrounded (flutter drive
  builds for minutes; the 180s cap must cover the scenario, not the build);
  match the target device by AVD name so a co-running emulator isn't hijacked;
  record to /data/local/tmp (headless /sdcard FUSE is flaky).
- README: document the API-35 requirement and reasoning.
2026-08-02 21:29:40 -04:00
jwinterm f64ee19b0c fix(integration_test): repair compile after chain removal; robust key taps
integration_test/ has not compiled since 784a082f1 removed SOL/TRX/ZEC/DCR/
ZANO/Haven: common_test_flows.dart and common_test_constants.dart still
referenced WalletType.solana/.tron/.zano/.decred/.zcash/.haven and secrets that
were never generated, and wallet_keys_robot branched on .solana/.tron. Any
`flutter drive` over these components failed at kernel compile.

- common_test_flows.dart: drop removed chains from both wallet-type switches.
- common_test_constants.dart: default test wallet SOL -> WOW (Wownero-first;
  SOL no longer exists), with a real WOW address.
- wallet_keys_robot.dart: EVM-only branch (solana/tron gone).
- common_test_cases.dart tapItemByKey: wait for the widget (up to 20s) instead
  of pumping a flat 500ms then asserting. Screens behind an async transition
  (PIN-setup success button) were a coin flip; this makes the failure honest
  ("not there after 20s") and the pass reliable. Pump loop, not pumpAndSettle,
  since the dashboard sync animation never settles.
2026-08-02 21:29:40 -04:00
jwinterm 44661ce592 fix(ios): correct Apple Team ID to D8PL9F7X33
The Appfile and Runner.xcodeproj carried 32J6BB6VUS (a stale leftover). The
real Such Software LLC team is D8PL9F7X33. This did not affect CI signing:
build-ios-testflight.yml rewrites DEVELOPMENT_TEAM to the APPLE_TEAM_ID secret
before archiving, so TestFlight builds already signed with the correct team.
This aligns local Xcode and the fastlane snapshot path (which are NOT rewritten
by CI) with what CI actually uses.

Team IDs are public identifiers (embedded in every distributed app's
provisioning profile), not secrets.
2026-08-02 15:03:04 -04:00
such-gitea 47c32b8f67 Merge pull request 'Dev' (#38) from github-such-software/hash-wallet:dev into dev
Reviewed-on: Builds/hash-wallet#38
2026-08-01 08:44:43 -04:00
jwinterm c396e94b77 Add store capture harness: record real wallet flows into screenshots + video
Drives the real app through real flows on a device while recording the screen,
then cuts ONE recording into both deliverables — store screenshots and
marketing video — so a listing can't drift out of sync with its own trailer.

integration_test/demo/
  DemoSession adds what a passing test lacks: pacing (beat/hold/typeSlowly, so
  footage is watchable rather than a strobe), timing marks reported through
  binding.reportData, and seed guards. Scenarios reuse the existing 25 page
  robots rather than reimplementing navigation:
    core_store_set     dashboard, receive, send (composed, NEVER broadcast),
                       wallets, security — the store screenshot set
    onboarding_create  welcome -> PIN -> seed -> dashboard
    restore_from_seed  restore -> syncing
    swap_and_buy       Trocador quote, stopping BEFORE creating a trade; Buy
                       skipped when no provider key, matching what reviewers see

tools/demo/
  capture.sh   orchestrates boot/record/cut/shots/reel
  marks.py     maps timing marks onto the recording; cuts, extracts frames,
               builds manifests
  reel.py      branded master; reuses such_graphics video_transitions/video_qa
               when ~/src/such-graphics is present, degrades cleanly when not
               (this repo is public and must build without it)
  build-apk.sh local APK via the CI container image

Seed safety — the demo wallet is a real wallet with real funds:
  - seed comes from Vaultwarden at capture time, never the repo
  - guardNoSeedOnScreen() fails the run on a visible mnemonic, catching both
    the known seed and the generic shape of one (so freshly generated seeds
    are caught too)
  - unavoidable seed screens are wrapped in blindfold(); those spans are
    excised from the video with 0.6s padding, and screenshots honour the same
    padding
  - marks.py refuses to build a publishable master if any clip was cut without
    marks, since blindfold spans could not have been excised

Apple App Preview gets no touch indicators, captions, cards or music, and
reel.py enforces the 15-30s window rather than emitting an out-of-spec file.
Everything else keeps taps on, drawn by SurfaceFlinger via show_touches rather
than composited, so they look real.

Play sizing: the harness builds its own 1080x1920 AVD. The stock Medium Phone
AVD is 1080x2400 = 2.22:1 and Play rejects phone screenshots above 2:1.

Also gitignores android/app/upload-keystore.jks (and *.jks/*.keystore): the
Android workflow decodes ANDROID_KEYSTORE_BASE64 to exactly that path, so the
Play upload signing key was one `git add -A` away from being committed.
2026-07-31 15:25:29 -04:00
jwinterm 4be0eb9613 Bump build number to 1.0.0+9
TestFlight run #118 succeeded on 2026-07-09, and that workflow ends with
`xcrun altool --upload-app` — so 1.0.0+8 is already uploaded and App Store
Connect will reject a redundant binary at the same build number. Re-running at
+8 would have burned a full signed archive before failing at the last step.

Both toolchains read this single source (scripts/{ios,android}/app_env.sh parse
pubspec_description.yaml), verified reporting build=9.

Note there is still no auto-bump: build-ios-testflight.yml is workflow_dispatch
only and its own comment flags this as a TODO, so every TestFlight run needs a
manual bump until that is wired up.
2026-07-31 15:25:07 -04:00
jwinterm c8abee8b15 Fix builds after Wownero's migration off Codeberg
Wownero left Codeberg in July 2026 (Codeberg announced it would ban crypto
projects) and remigrated to github.com/wownero/wownero. monero_c's .gitmodules
still points at the dead Codeberg URLs, which now fail with "remote: Bye" /
HTTP 403. That broke the iOS TestFlight build on 2026-07-31 during "Fetch
prebuilt monero_c bundle".

The clone never needed to happen. Every workflow (android/ios-sim/
ios-testflight/linux/windows) consumes PREBUILT .so/.a files from
release-bundle.zip; nothing outside release/ references the monero/wownero
source trees. The only thing needed from the checkout is `git describe --tags`
to resolve the release/<tag>/ path. prepare_moneroc.sh was cloning ~1GB of
source plus deep transitive submodules purely to discard it.

So skip the submodules by default. Source checkout is now opt-in behind
MONERO_C_BUILD_FROM_SOURCE=1, and when requested it redirects the dead
Codeberg URLs:
  wownero   -> github.com/wownero/wownero    (canonical after the migration)
  randomwow -> github.com/mrcyjanek/randomwow (what monero_c's apply_patches.sh
               already switches the remote to; NOT wownero/RandomWOW, which is
               a tevador/RandomX fork on a different lineage without the
               pinned commit)

Both submodules stay pinned to exact SHAs, so git verifies the object hash on
fetch and a different host cannot substitute content.

Verified clean-room with Codeberg still 403ing: prebuilt mode exits 0 with
`git describe --tags` = v0.18.4.6-RC1-2-gbc8d1a0 and zero Codeberg access;
source mode lands randomwow at the pinned cd137b1ea.
2026-07-31 15:24:59 -04:00
jwinterm fa49834820 Fix build: repin ledger_usb_plus to pub.dev ^1.0.4 (dead git commit)
The dependency_override pinned ledger_usb_plus to
konstantinullrich/ledger-usb-plus@dad482c847..., a commit that was force-pushed
away and no longer exists — so 'flutter pub get' fails ('bad object') on any
cold runner (the Android/Linux containers). It passed on iOS only because the
Mac-mini runner had a warm pub cache. cake-tech's ledger-flutter-plus fork
depends on the published ledger_usb_plus ^1.0.4, so pin the override to that.
2026-07-10 07:29:31 -04:00