Merge pull request #178 from MAGICGrants/2.1.0-release-fixes
What changed, and why it matters
This is a routine version-2.1.0 bug-fix merge for the Skylight Monero wallet. The visible changes fix small packaging and platform-detection issues, add a new automated TLS test suite, and update pinned internal library versions. There is no direct evidence in the commit of a security vulnerability being patched, but the TLS-related test additions and library bumps suggest the release is hardening how the app validates encrypted connections. The patch is best treated as a maintenance/hardening update rather than a confirmed fix for an exploitable flaw.
Review the newer wallet-core and monero_c commits for any security fixes they may contain, since the app-level diff only shows the pin updates. Run the new native TLS integration tests on all target platforms before releasing. Verify that removing the local CA asset from the app bundle does not break TLS on any platform, and confirm the Debian LD_LIBRARY_PATH change does not affect plugin loading. No urgent exploit mitigation is evident from this commit alone.
Security signals we found
New native TLS integration test workflow covering all shipped platforms
CA bundle asset handling moved into wallet-core (assets/cacert.pem removed from app asset list, copyCacertToAppDocumentsDir removed)
Debian launcher LD_LIBRARY_PATH no longer includes empty trailing entry
unlockWithPassword now uses a dedicated unlockWithTypedPassword method before opening wallet files
wallet-core and monero_c git refs updated to newer pinned commits
Release builds now enforce pubspec.lock
Evidence from the diff
The merge updates the app to 2.1.0+413 and refreshes pinned git refs for wallet-core and monero_c. Notable changes: (1) a new GitHub Actions workflow runs native TLS integration tests on Android, iOS, Linux and Windows against the shipped CA bundle and native library; (2) the Debian launcher no longer appends an empty LD_LIBRARY_PATH entry, removing a local library-search path quirk; (3) platform detection is split into isDesktop (layout) and isDesktopOS (true OS), so the iOS build running on Apple silicon Macs shows the desktop layout but keeps mobile wallet behavior (no typed password, iOS keystore/App Lock); (4) unlockWithPassword now calls a dedicated unlockWithTypedPassword method before openAll; (5) fiat rate cache clearing is centralized through FiatRateModel.clearPersistedRates; (6) release builds use –enforce-lockfile; (7) monero_c submodule fetches are restricted to monero and lwsf. No CVE, advisory, or researcher attribution is present in the supplied materials.
Changed components
GitHub Actions CI (.github/workflows/native-tls.yml, release.yml)Debian package launcher (deb/build_deb.sh)iOS/macOS app delegate and platform detection (ios/Runner/AppDelegate.swift, lib/util/platform.dart)Wallet unlock flow (lib/wallet_core_glue.dart, lib/screens/welcome.dart)Fiat rate settings (lib/screens/fiat_api_setup_screen.dart, lib/widgets/fiat_api_settings_form.dart)Native library build scripts (scripts/build-moneroc-local.sh, scripts/build-moneroc.sh, scripts/repro/build-moneroc-so.sh)Dependency pins (pubspec.yaml, pubspec.lock)Inspect captured patch +383 / −3624
### .github/workflows/native-tls.yml
@@ -0,0 +1,167 @@
+# TLS against the monero_c library this app ships, on every platform it ships
+# for: LWS and node, direct and through a SOCKS proxy, and the wallet's own
+# connect path with the CA bundle. The checks live in wallet-core
+# (`package:wallet_monero/testing.dart`) and run here as an integration test,
+# inside the real app on each platform, so they see the committed binaries and
+# the app's own asset bundle.
+#
+# Every server, proxy and certificate is local to the runner; nothing is
+# contacted over the network.
+name: Native TLS
+
+on:
+ pull_request:
+ paths:
+ - 'android/app/src/main/jniLibs/**'
+ - 'ios/Frameworks/**'
+ - 'linux/*.so'
+ - 'windows/*.dll'
+ - 'pubspec.lock'
+ - 'integration_test/**'
+ - '.github/workflows/native-tls.yml'
+ workflow_dispatch:
+
+env:
+ TEST: integration_test/native_tls_test.dart
+
+jobs:
+ flutter-version:
+ name: Flutter version
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.read.outputs.version }}
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+
+ - id: read
+ run: echo "version=$(grep -E '^\s+flutter:\s+' pubspec.yaml | head -1 | sed 's/.*flutter:\s*//')" >> "$GITHUB_OUTPUT"
+
+ android:
+ name: Android (emulator)
+ runs-on: ubuntu-latest
+ needs: flutter-version
+ steps:
+ - name: Free up disk space
+ run: |
+ sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL
+ df -h
+
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+
+ # The emulator needs hardware acceleration.
+ - name: Enable KVM
+ run: |
+ echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
+ sudo udevadm control --reload-rules
+ sudo udevadm trigger --name-match=kvm
+
+ - name: Use JDK 21, as the release builder does
+ run: echo "JAVA_HOME=$JAVA_HOME_21_X64" >> "$GITHUB_ENV"
+
+ - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
+ with:
+ channel: stable
+ flutter-version: ${{ needs.flutter-version.outputs.version }}
+
+ - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
+ with:
+ toolchain: stable
+ targets: x86_64-linux-android
+
+ - run: flutter pub get --enforce-lockfile
+
+ - uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
+ with:
+ api-level: 34
+ arch: x86_64
+ target: google_apis
+ disable-animations: true
+ script: flutter test ${{ env.TEST }} -d emulator-5554
+
+ ios:
+ name: iOS (simulator)
+ runs-on: macos-26
+ needs: flutter-version
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+
+ - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
+ with:
+ channel: stable
+ flutter-version: ${{ needs.flutter-version.outputs.version }}
+
+ - name: Install CocoaPods
+ run: |
+ brew untap aws/tap 2>/dev/null || true # runner pre-taps it; newer brew nags about untrusted taps
+ brew install cocoapods
+
+ - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
+ with:
+ toolchain: stable
+ targets: aarch64-apple-ios-sim
+
+ - run: flutter pub get --enforce-lockfile
+
+ - name: Boot a simulator
+ run: |
+ UDID=$(xcrun simctl list devices available --json | python3 -c '
+ import json, sys
+ devices = json.load(sys.stdin)["devices"]
+ for runtime in sorted(devices, reverse=True):
+ if "iOS" in runtime:
+ for device in devices[runtime]:
+ if device["name"].startswith("iPhone"):
+ print(device["udid"])
+ sys.exit()
+ sys.exit("no iPhone simulator available")
+ ')
+ xcrun simctl boot "$UDID"
+ xcrun simctl bootstatus "$UDID" -b
+ echo "SIMULATOR=$UDID" >> "$GITHUB_ENV"
+
+ - run: flutter test ${{ env.TEST }} -d "$SIMULATOR"
+
+ linux:
+ name: Linux
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+
+ # The image the Linux release is built in, plus a virtual display.
+ - name: Pull builder image
+ run: docker pull ghcr.io/magicgrants/skylight-wallet-builder:latest
+
+ - name: Native TLS checks
+ run: |
+ docker run --rm \
+ -v "$PWD:/workspace" \
+ -w /workspace \
+ ghcr.io/magicgrants/skylight-wallet-builder:latest \
+ bash -c "apt-get update && apt-get install -y --no-install-recommends xvfb xauth && flutter pub get --enforce-lockfile && bash scripts/pin-rust-toolchain.sh && xvfb-run -a flutter test $TEST -d linux"
+
+ windows:
+ name: Windows
+ runs-on: windows-2022
+ needs: flutter-version
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+
+ - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
+ with:
+ channel: stable
+ flutter-version: ${{ needs.flutter-version.outputs.version }}
+
+ # webcrypto builds BoringSSL, whose Windows assembly needs NASM.
+ - name: Install NASM
+ run: |
+ choco install nasm -y
+ echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
+
+ - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
+ with:
+ toolchain: stable
+ targets: x86_64-pc-windows-msvc
+
+ - run: flutter pub get --enforce-lockfile
+
+ - run: flutter test ${{ env.TEST }} -d windows
### .github/workflows/release.yml
@@ -190,7 +190,7 @@ jobs:
-v "$PWD:/workspace" \
-w /workspace \
ghcr.io/magicgrants/skylight-wallet-builder:latest \
- bash -c "flutter pub get && bash scripts/pin-rust-toolchain.sh && flutter build linux --release && ./deb/build_deb.sh --version ${VERSION} && ./appimage/build_appimage.sh --version ${VERSION}"
+ bash -c "flutter pub get --enforce-lockfile && bash scripts/pin-rust-toolchain.sh && flutter build linux --release && ./deb/build_deb.sh --version ${VERSION} && ./appimage/build_appimage.sh --version ${VERSION}"
mkdir -p dist
cp -v deb/skylight-wallet-*.deb dist/
@@ -222,7 +222,7 @@ jobs:
flutter-version: ${{ needs.version.outputs.flutter_version }}
- name: Install dependencies
- run: flutter pub get
+ run: flutter pub get --enforce-lockfile
- name: Install Inno Setup
run: choco install innosetup -y
@@ -332,7 +332,7 @@ jobs:
- name: Build IPA
run: |
- flutter pub get
+ flutter pub get --enforce-lockfile
flutter build ipa --dart-define=DEMO_MODE=true --release --export-options-plist=ios/ExportOptions.plist
- name: Prepare artifact
### assets/cacert.pem
[binary or diff unavailable]
### deb/build_deb.sh
@@ -93,7 +93,7 @@ Architecture: amd64
Installed-Size: $INSTALLED_SIZE
Maintainer: MAGIC Grants
Description: A light Monero wallet.
-Homepage: https://github.com/skylight-wallet/skylight-wallet
+Homepage: https://github.com/MAGICGrants/skylight-wallet
EOF
# Copy the Flutter bundle to lib directory
@@ -105,7 +105,9 @@ echo "Creating launcher script..."
cat > "$PACKAGE_DIR/usr/bin/skylight-wallet" << 'EOF'
#!/bin/bash
INSTALL_DIR="/usr/lib/skylight-wallet"
-export LD_LIBRARY_PATH="$INSTALL_DIR/lib:$LD_LIBRARY_PATH"
+# The caller's path is appended only when set: an empty entry would make the
+# loader search the current directory for libraries.
+export LD_LIBRARY_PATH="$INSTALL_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
exec "$INSTALL_DIR/skylight_wallet" "$@"
EOF
chmod 755 "$PACKAGE_DIR/usr/bin/skylight-wallet"
### integration_test/native_tls_test.dart
@@ -0,0 +1,28 @@
+import 'dart:io';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:wallet_monero/testing.dart';
+
+/// TLS against the native library this app ships, on the platform the test
+/// runs on: CI runs it on an Android emulator, an iOS simulator, Linux and
+/// Windows. The checks themselves live in wallet-core (`nativeTlsChecks`) and
+/// stand up their own loopback servers, so nothing leaves the machine.
+///
+/// `flutter test integration_test/native_tls_test.dart -d DEVICE`
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ testWidgets('the shipped CA bundle reaches the app directory', (_) => checkShippedCaBundle());
+
+ for (final check in nativeTlsChecks()) {
+ testWidgets(check.name, (_) async {
+ final dir = await Directory.systemTemp.createTemp('native_tls');
+ try {
+ await check.run(dir);
+ } finally {
+ await dir.delete(recursive: true);
+ }
+ });
+ }
+}
### ios/Runner/AppDelegate.swift
@@ -7,6 +7,8 @@ import workmanager_apple
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
private var secureClipboardChannel: FlutterMethodChannel?
private var storeReviewChannel: FlutterMethodChannel?
+ private var hostPlatformChannel: FlutterMethodChannel?
+ private var sceneConnectObserver: NSObjectProtocol?
override func application(
_ application: UIApplication,
@@ -36,6 +38,19 @@ import workmanager_apple
// a Tor bootstrap before syncing.
WorkmanagerPlugin.registerBGProcessingTask(withIdentifier: "\(bundleId).processing")
+ // On a Mac this build shows the desktop layout, which needs the same
+ // minimum window as the macOS build (MainFlutterWindow.swift).
+ if ProcessInfo.processInfo.isiOSAppOnMac {
+ sceneConnectObserver = NotificationCenter.default.addObserver(
+ forName: UIScene.willConnectNotification,
+ object: nil,
+ queue: .main
+ ) { note in
+ (note.object as? UIWindowScene)?.sizeRestrictions?.minimumSize =
+ CGSize(width: 900, height: 640)
+ }
+ }
+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
@@ -92,6 +107,25 @@ import workmanager_apple
}
storeReviewChannel = channel
}
+
+ let hostRegistrar = engineBridge.pluginRegistry.registrar(forPlugin: "HostPlatform")
+ if let messenger = hostRegistrar?.messenger() {
+ // App-neutral name shared with wallet-core's HostPlatform. The App Store
+ // offers this build on Apple silicon Macs, where Dart still reports iOS.
+ let channel = FlutterMethodChannel(
+ name: "org.magicgrants.wallet/host_platform",
+ binaryMessenger: messenger
+ )
+ channel.setMethodCallHandler { call, reply in
+ switch call.method {
+ case "isIosAppOnMac":
+ reply(ProcessInfo.processInfo.isiOSAppOnMac)
+ default:
+ reply(FlutterMethodNotImplemented)
+ }
+ }
+ hostPlatformChannel = channel
+ }
}
/// Hands the request to StoreKit's own prompt, which decides whether to show
### ios/Runner/Info.plist
@@ -33,6 +33,8 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>ITSAppUsesNonExemptEncryption</key>
+ <false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
### lib/main.dart
@@ -44,17 +44,17 @@ import 'package:skylight_wallet/periodic_tasks.dart';
import 'package:skylight_wallet/services/foreground_sync_service.dart';
import 'package:skylight_wallet/util/dirs.dart';
import 'package:skylight_wallet/util/logging.dart';
-import 'package:skylight_wallet/util/cacert.dart';
+import 'package:skylight_wallet/util/platform.dart';
import 'package:skylight_wallet/wallet_core_glue.dart';
-
-final isDesktop = Platform.isLinux || Platform.isWindows || Platform.isMacOS;
-final isMobile = Platform.isAndroid || Platform.isIOS;
+import 'package:wallet_infra/wallet_infra.dart' show HostPlatform;
void main() async {
// Catch all uncaught async errors
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
+ // Before the first frame: the layout reads it synchronously.
+ await HostPlatform.init();
installWalletCore();
BrandColors.install(skylightPalette);
@@ -82,7 +82,6 @@ void main() async {
}
if (Platform.isAndroid) {
- copyCacertToAppDocumentsDir();
registerPeriodicTasks();
startForegroundSyncIfEnabled();
NotificationService().init();
@@ -284,8 +283,9 @@ class _AppRootState extends State<_AppRoot> with WidgetsBindingObserver {
final appLockEnabled =
sharedPreferences.getBool(SharedPreferencesKeys.appLockEnabled) ?? false;
+ // A desktop OS asks for the typed password at every launch.
final initialRoute = walletExists
- ? appLockEnabled || isDesktop
+ ? appLockEnabled || isDesktopOS
? '/unlock'
: '/wallet_home'
: '/welcome';
@@ -305,7 +305,7 @@ class _AppRootState extends State<_AppRoot> with WidgetsBindingObserver {
// Desktop has no background isolate to announce incoming txs, so
// the foreground announces on tx-history growth. Mobile announces
// from its background isolates.
- if (isDesktop) {
+ if (isDesktopOS) {
_announceWallet = appWalletOf(context, listen: false)
..addListener(_announceNewTxsOnGrowth);
}
### lib/screens/desktop/connection_view.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:skylight_wallet/screens/desktop/onboarding_steps.dart';
import 'package:skylight_wallet/widgets/connection_settings_form.dart';
import 'package:skylight_wallet/widgets/ui/ui.dart';
@@ -50,7 +51,7 @@ class _DesktopConnectionViewState extends State<DesktopConnectionView> {
title: widget.title,
description: widget.description,
step: 3,
- totalSteps: 6,
+ totalSteps: desktopOnboardingSteps,
continueLabel: widget.saveButtonLabel,
continueEnabled: canSave,
onBack: widget.onBack,
### lib/screens/desktop/fiat_setup_view.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:wallet_fiat/wallet_fiat.dart';
+import 'package:skylight_wallet/screens/desktop/onboarding_steps.dart';
import 'package:skylight_wallet/widgets/ui/ui.dart';
/// Desktop Step 2 of 6 — price-display choice (Tor-Only / Clearnet / Disabled)
@@ -43,7 +44,7 @@ class DesktopFiatSetupView extends StatelessWidget {
title: labels.title,
description: labels.subtitle,
step: 2,
- totalSteps: 6,
+ totalSteps: desktopOnboardingSteps,
continueLabel: labels.continueText,
onBack: onBack,
onContinue: onContinue,
### lib/screens/desktop/onboarding_steps.dart
@@ -0,0 +1,6 @@
+import 'package:skylight_wallet/util/platform.dart';
+
+/// How many steps the desktop onboarding numbers. The password is the last one,
+/// and only a desktop OS sets it: the iOS build on a Mac shows this layout but
+/// keeps the mobile wallet password, so its onboarding ends at the seed.
+int get desktopOnboardingSteps => isDesktopOS ? 6 : 5;
### lib/screens/desktop/tor_choice_view.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:skylight_wallet/screens/desktop/onboarding_steps.dart';
import 'package:skylight_wallet/widgets/tor_settings_form.dart'
show TorPortField, TorTestChip, TorTestStatus;
import 'package:skylight_wallet/widgets/ui/ui.dart';
@@ -69,7 +70,7 @@ class _DesktopTorChoiceViewState extends State<DesktopTorChoiceView> {
title: l.title,
description: l.subtitle,
step: 1,
- totalSteps: 6,
+ totalSteps: desktopOnboardingSteps,
continueLabel: l.continueText,
continueEnabled: _canContinue,
onBack: widget.onBack,
### lib/screens/desktop/wallet_setup_view.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:skylight_wallet/screens/desktop/onboarding_steps.dart';
import 'package:skylight_wallet/widgets/ui/ui.dart';
/// Desktop Step 4 of 6 — create-new vs restore. Selection then Continue (rather
@@ -46,7 +47,7 @@ class _DesktopWalletSetupViewState extends State<DesktopWalletSetupView> {
title: l.title,
description: l.subtitle,
step: 4,
- totalSteps: 6,
+ totalSteps: desktopOnboardingSteps,
continueLabel: widget.continueText,
continueEnabled: _selected != null,
onBack: widget.onBack,
### lib/screens/fiat_api_setup_screen.dart
@@ -33,7 +33,7 @@ class _FiatApiSetupScreenState extends State<FiatApiSetupScreen> {
// A manual choice is definitive; don't let a later Tor re-enable override it.
await SharedPreferencesService.remove(SharedPreferencesKeys.fiatAutoDisabledByTor);
await SharedPreferencesService.set<String>(SharedPreferencesKeys.fiatCurrency, _fiatCurrency);
- await SharedPreferencesService.remove(SharedPreferencesKeys.fiatRate);
+ await FiatRateModel.clearPersistedRates();
if (!mounted) return;
Navigator.pushNamed(context, '/connection_setup');
### lib/screens/generate_seed.dart
@@ -7,6 +7,7 @@ import 'package:wallet_domain/wallet_domain.dart' show SeedSource;
import 'package:skylight_wallet/l10n/app_localizations.dart';
import 'package:skylight_wallet/models/fiat_rate_model.dart';
import 'package:skylight_wallet/screens/create_wallet_password.dart';
+import 'package:skylight_wallet/screens/desktop/onboarding_steps.dart';
import 'package:skylight_wallet/util/logging.dart';
import 'package:skylight_wallet/util/platform.dart';
import 'package:skylight_wallet/util/secure_screen.dart';
@@ -35,13 +36,14 @@ class _GenerateSeedScreenState extends State<GenerateSeedScreen> with SecureScre
_seed = generated.seed.mnemonic.split(' ');
}
- /// Desktop adds a password step (password-last flow). Mobile has no password
- /// screen — it's guarded by the device app lock — so it commits the wallet
- /// directly from here.
+ /// A desktop OS adds a password step (password-last flow). Mobile has no
+ /// password screen — it's guarded by the device app lock — so it commits the
+ /// wallet directly from here. That includes the iOS build on a Mac, which
+ /// shows the desktop layout but keeps the mobile wallet password.
void _continue() {
final generated = _generated;
if (generated == null) return;
- if (isDesktop) {
+ if (isDesktopOS) {
Navigator.pushNamed(
context,
'/create_wallet_password',
@@ -97,7 +99,7 @@ class _GenerateSeedScreenState extends State<GenerateSeedScreen> with SecureScre
return DesktopGenerateSeedView(
logo: SvgPicture.asset('assets/logo_nobg.svg', height: 52),
step: 5,
- totalSteps: 6,
+ totalSteps: desktopOnboardingSteps,
title: i18n.generateSeedTitle,
description: i18n.generateSeedSubtitleRevealed,
seedWords: _seed ?? const [],
@@ -109,9 +111,10 @@ class _GenerateSeedScreenState extends State<GenerateSeedScreen> with SecureScre
).format(_generated!.restoreDate)
: null,
confirmLabel: i18n.generateSeedConfirm,
- passwordNote: i18n.onboardingSeedNotePassword,
+ passwordNote: isDesktopOS ? i18n.onboardingSeedNotePassword : null,
revealLabel: i18n.generateSeedReveal,
continueText: i18n.continueText,
+ loading: _committing,
onContinue: _continue,
onBack: () => Navigator.pop(context),
);
### lib/screens/restore_wallet.dart
@@ -10,6 +10,7 @@ import 'package:provider/provider.dart';
import 'package:skylight_wallet/l10n/app_localizations.dart';
import 'package:skylight_wallet/models/fiat_rate_model.dart';
import 'package:skylight_wallet/screens/create_wallet_password.dart';
+import 'package:skylight_wallet/screens/desktop/onboarding_steps.dart';
import 'package:skylight_wallet/util/get_height_by_date.dart';
import 'package:skylight_wallet/util/logging.dart';
import 'package:skylight_wallet/util/platform.dart';
@@ -123,9 +124,10 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> with SecureSc
}
final restoreHeight = _restoreHeight;
- // Desktop adds a password step; mobile has no password screen (the device
- // app lock guards it), so it restores the wallet directly.
- if (isDesktop) {
+ // A desktop OS adds a password step; mobile has no password screen (the
+ // device app lock guards it), so it restores the wallet directly. That
+ // includes the iOS build on a Mac, in the desktop layout.
+ if (isDesktopOS) {
Navigator.pushNamed(
context,
'/create_wallet_password',
@@ -256,9 +258,10 @@ class _RestoreWalletScreenState extends State<RestoreWalletScreen> with SecureSc
title: i18n.restoreWalletTitle,
description: i18n.restoreWalletDescription,
step: 5,
- totalSteps: 6,
+ totalSteps: desktopOnboardingSteps,
continueLabel: i18n.restoreWalletRestoreButton,
continueEnabled: valid,
+ loading: _committing,
onBack: () => Navigator.pop(context),
onContinue: _restoreWalletController.restore,
content: view,
### lib/screens/welcome.dart
@@ -25,7 +25,12 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
Future<void> _pushHomeIfWalletExists() async {
if (await openExistingWallet(context) && mounted) {
- Navigator.pushNamedAndRemoveUntil(context, '/wallet_home', (route) => false);
+ // A desktop OS opens nothing until the user types the password.
+ Navigator.pushNamedAndRemoveUntil(
+ context,
+ isDesktopOS ? '/unlock' : '/wallet_home',
+ (route) => false,
+ );
}
}
### lib/util/cacert.dart
@@ -1,4 +0,0 @@
-// cacert handling lives in wallet-core (wallet_infra); the asset itself is
-// bundled by this app (assets/cacert.pem). Kept under this path so call sites
-// are unchanged.
-export 'package:wallet_infra/wallet_infra.dart' show copyCacertToAppDocumentsDir, getCacertFile;
### lib/util/platform.dart
@@ -1,7 +1,18 @@
import 'dart:io';
-/// True on the desktop platforms (Linux/Windows/macOS).
-final bool isDesktop = Platform.isLinux || Platform.isWindows || Platform.isMacOS;
+import 'package:wallet_ui/wallet_ui.dart' show isDesktopLayout;
-/// True on the mobile platforms (Android/iOS).
+/// True when the app shows its desktop layout: on Linux/Windows/macOS, and for
+/// the iOS build running on a Mac (see [isDesktopLayout]). Layout only.
+bool get isDesktop => isDesktopLayout;
+
+/// True when running natively on a desktop OS (Linux/Windows/macOS).
+///
+/// What follows the OS rather than the layout keys off this: the wallet
+/// password typed at every launch, and announcing incoming transactions from
+/// the foreground. The iOS build on a Mac is not one of these. It keeps iOS's
+/// keystore-held password, App Lock and background sync.
+final bool isDesktopOS = Platform.isLinux || Platform.isWindows || Platform.isMacOS;
+
+/// True on the mobile platforms (Android/iOS), the iOS build on a Mac included.
final bool isMobile = Platform.isAndroid || Platform.isIOS;
### lib/wallet_core_glue.dart
@@ -222,19 +222,23 @@ Future<bool> armAppLockRelock(BuildContext context) =>
Provider.of<WalletManager>(context, listen: false).armAppLockRelock();
/// Opens an already-existing wallet (used by the welcome safety-net). Returns
-/// false when there is none. Mobile only — desktop unlocks with a password.
+/// false when there is none. Opens only on mobile: on a desktop OS the wallet
+/// waits for the typed password, and the caller shows the unlock screen.
Future<bool> openExistingWallet(BuildContext context) async {
final manager = Provider.of<WalletManager>(context, listen: false);
if (!await manager.hasAnyExistingWallet()) return false;
- manager.openWalletFilesAndSync();
+ if (_isMobile) manager.openWalletFilesAndSync();
return true;
}
/// Opens the wallet with a desktop-entered password, then syncs. Throws on a
/// wrong password (the unlock screen shows the error).
Future<void> unlockWithPassword(BuildContext context, String password) async {
final manager = Provider.of<WalletManager>(context, listen: false);
- await manager.openAll(password: password);
+ if (!await manager.unlockWithTypedPassword(password)) {
+ throw Exception('Incorrect wallet password.');
+ }
+ await manager.openAll();
manager.syncInBackground();
}
### lib/widgets/fiat_api_settings_form.dart
@@ -52,7 +52,7 @@ class _FiatApiSettingsFormState extends State<FiatApiSettingsForm> {
// no longer overrides it.
await SharedPreferencesService.remove(SharedPreferencesKeys.fiatAutoDisabledByTor);
await SharedPreferencesService.set<String>(SharedPreferencesKeys.fiatCurrency, _currency);
- await SharedPreferencesService.remove(SharedPreferencesKeys.fiatRate);
+ await FiatRateModel.clearPersistedRates();
await widget.onSaved();
}
### pubspec.lock
@@ -255,6 +255,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_driver:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
flutter_foreground_task:
dependency: "direct main"
description:
@@ -414,6 +419,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
+ fuchsia_remote_debug_protocol:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
gpt_markdown:
dependency: "direct main"
description:
@@ -534,6 +544,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.2"
+ integration_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
intl:
dependency: "direct main"
description:
@@ -666,8 +681,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
- ref: "3ca8418f5888883e1bb6e3a6a919260e71a49647"
- resolved-ref: "3ca8418f5888883e1bb6e3a6a919260e71a49647"
+ ref: e31cbe2cc595fa9d640b35ffb45dc3afa91ec7b0
+ resolved-ref: e31cbe2cc595fa9d640b35ffb45dc3afa91ec7b0
url: "https://github.com/magicgrants/monero_c"
source: git
version: "0.0.0"
@@ -855,6 +870,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.3"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ sha256: "4242ba3508d37e01808bdf71ad1d5bb93a8d671bf2e7450e6b1b353fb0808891"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.6"
provider:
dependency: "direct main"
description:
@@ -1084,6 +1107,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
+ sync_http:
+ dependency: transitive
+ description:
+ name: sync_http
+ sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
term_glyph:
dependency: transitive
description:
@@ -1233,62 +1264,62 @@ packages:
dependency: "direct main"
description:
path: "packages/wallet_background"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
wallet_domain:
dependency: "direct main"
description:
path: "packages/wallet_domain"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
wallet_fiat:
dependency: "direct main"
description:
path: "packages/wallet_fiat"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
wallet_infra:
dependency: "direct main"
description:
path: "packages/wallet_infra"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
wallet_monero:
dependency: "direct main"
description:
path: "packages/wallet_monero"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
wallet_openalias:
dependency: "direct main"
description:
path: "packages/wallet_openalias"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
wallet_ui:
dependency: "direct main"
description:
path: "packages/wallet_ui"
- ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
- resolved-ref: "3584c83fc791dfb498dd6a31afb16ebb48b25bf6"
+ ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
+ resolved-ref: "548255131912abcfd4b43e273bc4722c160c8ccf"
url: "https://github.com/MAGICGrants/wallet-core"
source: git
version: "0.1.0"
@@ -1308,6 +1339,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.0"
+ webdriver:
+ dependency: transitive
+ description:
+ name: webdriver
+ sha256: "28b82ec894fed45dd71c23ba62d1af973ed97dd59a4f5790a4d38b0b13e5657e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.0"
win32:
dependency: transitive
description:
### pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 2.1.0+412
+version: 2.1.0+413
environment:
sdk: 3.11.5
@@ -57,45 +57,45 @@ dependencies:
monero:
git:
url: https://github.com/magicgrants/monero_c
- ref: 3ca8418f5888883e1bb6e3a6a919260e71a49647
+ ref: e31cbe2cc595fa9d640b35ffb45dc3afa91ec7b0
path: impls/monero.dart
# wallet-core packages; SHA-pinned for release, redirected to the local sibling
# clone by the gitignored pubspec_overrides.yaml for dev.
wallet_infra:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_infra
wallet_domain:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_domain
wallet_monero:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_monero
wallet_openalias:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_openalias
wallet_background:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_background
wallet_fiat:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_fiat
wallet_ui:
git:
url: https://github.com/MAGICGrants/wallet-core
- ref: 3584c83fc791dfb498dd6a31afb16ebb48b25bf6
+ ref: 548255131912abcfd4b43e273bc4722c160c8ccf
path: packages/wallet_ui
bip39:
@@ -138,6 +138,10 @@ dependency_overrides:
dev_dependencies:
flutter_test:
sdk: flutter
+ # integration_test/native_tls_test.dart: TLS against the shipped native
+ # library, on a device or desktop.
+ integration_test:
+ sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
@@ -160,7 +164,6 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/icons/
- - assets/cacert.pem
- assets/app_icon.ico
- assets/launcher_icon.png
- assets/logo.svg
### scripts/build-moneroc-local.sh
@@ -82,7 +82,9 @@ for ARCH in "${ARCHS[@]}"; do
cd "$work"
git fetch --quiet origin "$REF" || true
git checkout --quiet "$REF"
- git submodule update --init --recursive --force
+ # Only the submodules a Monero build uses; the others are for other coins,
+ # and fetching them fails the build whenever one of their hosts is down.
+ git submodule update --init --recursive --force -- monero lwsf
# Deterministic version hash (git am committer date) as in the repro build.
export SOURCE_DATE_EPOCH="$(git log -1 --format=%ct)"
### scripts/build-moneroc.sh
@@ -27,7 +27,9 @@ git config --global user.email 'info@magicgrants.org'
rm -rf /tmp/monero_c
git clone "$SRC" /tmp/monero_c
git -C /tmp/monero_c checkout "$COMMIT"
-git -C /tmp/monero_c submodule update --init --recursive --force
+# Only the submodules a Monero build uses; the others are for other coins,
+# and fetching them fails the build whenever one of their hosts is down.
+git -C /tmp/monero_c submodule update --init --recursive --force -- monero lwsf
cd /tmp/monero_c
# Pin the git-am committer date (baked into Monero's version string) + __DATE__/__TIME__.
### scripts/repro/build-moneroc-so.sh
@@ -68,7 +68,9 @@ docker run --rm \
git clone --quiet https://github.com/magicgrants/monero_c.git "$work"
cd "$work"
git checkout --quiet "$REF"
- git submodule update --init --recursive --force --quiet
+ # Only the submodules a Monero build uses; the others are for other coins,
+ # and fetching them fails the build whenever one of their hosts is down.
+ git submodule update --init --recursive --force --quiet -- monero lwsf
# Pin timestamps BEFORE patching. apply_patches.sh runs `git am`, whose commit
# SHA depends on the committer date; Monero bakes that short-hash into its
# version string (0.18.4.0-<hash>). Fix the date so the hash is deterministic.
### windows/CMakeLists.txt
@@ -32,6 +32,8 @@ set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
+add_compile_definitions(_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS)
+
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function byWhy this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.