What changed, and why it matters
This commit fixes Linux-specific app crashes and UI glitches in Cake Wallet. The main functional change is making the wallet correctly store and read true/false settings as numbers (1 or 0) in its SQLite database on Linux, where storing raw booleans was causing crashes. It also disables Lightning payments on platforms that don't support them and forces the Linux app to start in a tall phone-like shape instead of a wide landscape shape. There is no clear security vulnerability being patched; it reads as a stability and UI polish fix.
Treat as a routine stability/UI fix. No immediate security response is indicated. If deploying, verify that existing Linux node records still load correctly after the boolean-to-int mapping change and that Lightning is appropriately disabled on unsupported builds.
Security signals we found
SQLite boolean serialization change may prevent type-confusion crashes on Linux
Lightning wallet availability guard added to avoid unsupported initialization paths
Null-safety casts added to reduce runtime exceptions in receive/send pages
Evidence from the diff
The diff updates Dart code in cw_core/lib/node.dart to serialize boolean fields (isPow, useSSL, trusted, isEnabledForAutoSwitching, isOfficial, isBuiltin, isDefault) to SQLite as integers 1/0 and to deserialize them robustly via a new _getBoolFromDB helper that handles bool, int, and null. It also changes SQL whereArgs from Dart bool literals to 0/1. In cw_bitcoin/lib/electrum_wallet.dart, the useLightning flag is now gated by LightningWallet.isAvailable so unsupported platforms do not attempt to initialize it. UI diffs in lib/new-ui/pages/receive_page.dart and send_page.dart add null-safety casts and optional chaining to prevent runtime exceptions. linux/my_application.cc hardcodes a 480x800 portrait startup size. No explicit security bug is described or patched.
Changed components
cw_core/lib/node.dart (node database serialization)cw_bitcoin/lib/electrum_wallet.dart (Lightning enablement logic)lib/new-ui/pages/receive_page.dartlib/new-ui/pages/send_page.dartlinux/my_application.cc (Linux window sizing)Inspect captured patch +47 / −34
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index ec549211..842fe7e3 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.dart';
+import 'package:cw_bitcoin/lightning/lightning_wallet.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
@@ -76,7 +77,7 @@ abstract class ElectrumWalletBase
ElectrumBalance? initialBalance,
CryptoCurrency? currency,
bool? alwaysScan,
- this.useLightning = true,
+ bool useLightning = true,
}) : _masterHD = getMasterHD(seedBytes, network, walletInfo.hardwareWalletType),
accountHD = getAccountHDWallet(
currency, network, seedBytes, xpub, derivationInfo, walletInfo.hardwareWalletType),
@@ -102,6 +103,7 @@ abstract class ElectrumWalletBase
this.unspentCoinsInfo = unspentCoinsInfo,
this.isTestnet = !network.isMainnet,
this._mnemonic = mnemonic,
+ _useLightning = useLightning,
super(walletInfo, derivationInfo) {
this.electrumClient = electrumClient ?? electrum.ElectrumClient();
this.walletInfo = walletInfo;
@@ -305,8 +307,13 @@ abstract class ElectrumWalletBase
@observable
bool? alwaysScan;
+ @computed
+ bool get useLightning => _useLightning && LightningWallet.isAvailable;
+
+ set useLightning(bool val) => _useLightning = val && LightningWallet.isAvailable;
+
@observable
- bool useLightning;
+ bool _useLightning;
final Bip32Slip10Secp256k1? _masterHD;
final Bip32Slip10Secp256k1 accountHD;
diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart
index 818286a2..7550fc62 100644
--- a/cw_core/lib/node.dart
+++ b/cw_core/lib/node.dart
@@ -92,15 +92,25 @@ class Node {
login = map['login'] as String?,
label = map['label'] as String?,
password = map['password'] as String?,
- isPow = (map["isPow"] != null && map['isPow'] != 0) as bool? ?? false,
- useSSL = (map['useSSL'] != 0) as bool?,
+ isPow = _getBoolFromDB(map['isPow']),
+ useSSL = _getBoolFromDB(map['useSSL']),
typeRaw = (map["typeRaw"] ?? 0) as int,
- trusted = (map['trusted'] != 0) as bool? ?? false,
+ trusted = _getBoolFromDB(map['trusted']),
socksProxyAddress = map['socksProxyAddress'] as String?,
- isEnabledForAutoSwitching = (map['isEnabledForAutoSwitching'] != 0) as bool? ?? false,
- isOfficial = (map['isOfficial'] != 0) as bool? ?? false,
- isBuiltin = (map['isBuiltin'] != 0) as bool? ?? false,
- isDefault = (map['isDefault'] != 0) as bool? ?? false;
+ isEnabledForAutoSwitching = _getBoolFromDB(map['isEnabledForAutoSwitching']),
+ isOfficial = _getBoolFromDB(map['isOfficial']),
+ isBuiltin = _getBoolFromDB(map['isBuiltin']),
+ isDefault = _getBoolFromDB(map['isDefault']);
+
+ static bool _getBoolFromDB(value, {bool? defaultValue}) {
+ if (value is bool) {
+ return value;
+ } else if (value is int) {
+ return value == 1;
+ } else {
+ return defaultValue ?? false;
+ }
+ }
Map<String, dynamic> toMap() {
return {
@@ -110,15 +120,15 @@ class Node {
'login': login,
"label": label,
'password': password,
- "isPow": isPow,
- 'useSSL': useSSL,
+ "isPow": isPow ? 1 : 0,
+ 'useSSL': (useSSL??false) ? 1 : 0,
"typeRaw": typeRaw,
- 'trusted': trusted,
+ 'trusted': trusted ? 1 : 0,
'socksProxyAddress': socksProxyAddress,
- 'isEnabledForAutoSwitching': isEnabledForAutoSwitching,
- "isOfficial": isOfficial,
- "isBuiltin": isBuiltin,
- "isDefault": isDefault
+ 'isEnabledForAutoSwitching': isEnabledForAutoSwitching ? 1 : 0,
+ "isOfficial": isOfficial ? 1 : 0,
+ "isBuiltin": isBuiltin ? 1 : 0,
+ "isDefault": isDefault ? 1 : 0
};
}
@@ -127,11 +137,11 @@ class Node {
}
static Future<int> deleteAll() async {
- return await db!.delete(tableName, where: "isPow = ?", whereArgs: [false]);
+ return await db!.delete(tableName, where: "isPow = ?", whereArgs: [0]);
}
static Future<int> deleteAllPow() async {
- return await db!.delete(tableName, where: "isPow = ?", whereArgs: [true]);
+ return await db!.delete(tableName, where: "isPow = ?", whereArgs: [1]);
}
Future<int> save() async {
@@ -173,35 +183,35 @@ class Node {
static Future<List<Node>> getAll() async {
- return selectList("isPow = ?", [false]);
+ return selectList("isPow = ?", [0]);
}
static Future<List<Node>> getAllBuiltin() async {
- return selectList("isPow = ? AND isBuiltin = ?", [false, true]);
+ return selectList("isPow = ? AND isBuiltin = ?", [0, 1]);
}
static Future<List<Node>> getAllPowBuiltin() async {
- return selectList("isPow = ? AND isBuiltin = ?", [true, true]);
+ return selectList("isPow = ? AND isBuiltin = ?", [1, 1]);
}
static Future<List<Node>> getAllForWalletType(WalletType type) async {
- return selectList("typeRaw = ? AND isPow = ?", [serializeToInt(type), false]);
+ return selectList("typeRaw = ? AND isPow = ?", [serializeToInt(type), 0]);
}
static Future<Node?> getDefaultForWalletType(WalletType type) async {
- return (await selectList("typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), false, true])).firstOrNull;
+ return (await selectList("typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), 0, 1])).firstOrNull;
}
static Future<Node?> getDefaultPowForWalletType(WalletType type) async {
- return (await selectList("typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), true, true])).firstOrNull;
+ return (await selectList("typeRaw = ? AND isPow = ? AND isDefault = ?", [serializeToInt(type), 1, 1])).firstOrNull;
}
static Future<List<Node>> getAllForWalletTypePow(WalletType type) async {
- return selectList("typeRaw = ? AND isPow = ?", [serializeToInt(type), true]);
+ return selectList("typeRaw = ? AND isPow = ?", [serializeToInt(type), 1]);
}
static Future<List<Node>> getAllPow() async {
- return selectList("isPow = ?", [true]);
+ return selectList("isPow = ?", [1]);
}
static Future<Node?> get(int id) async {
@@ -475,7 +485,7 @@ class Node {
},
),
);
-
+
final data = jsonDecode(response.body);
if (response.statusCode != 200 ||
data["error"] != null ||
diff --git a/lib/new-ui/pages/receive_page.dart b/lib/new-ui/pages/receive_page.dart
index 764393e3..9ad2d7d2 100644
--- a/lib/new-ui/pages/receive_page.dart
+++ b/lib/new-ui/pages/receive_page.dart
@@ -342,7 +342,7 @@ class _NewReceivePageState extends State<NewReceivePage> {
setState(() {
_addressItemWithLabel = widget.addressListViewModel.forceRecomputeItems.firstWhereOrNull(
(item) => (item is WalletAddressListItem && item.address == newAddress.address))
- as WalletAddressListItem;
+ as WalletAddressListItem?;
});
}
}
diff --git a/lib/new-ui/pages/send_page.dart b/lib/new-ui/pages/send_page.dart
index 1fd28fc9..c3f5da95 100644
--- a/lib/new-ui/pages/send_page.dart
+++ b/lib/new-ui/pages/send_page.dart
@@ -972,7 +972,7 @@ class _NewSendPageState extends State<NewSendPage> {
onNext: (PaymentFlowResult newResult) {
final selectedChainId = newResult.chainId;
final isCompatible =
- selectedChainId == evm!.getSelectedChainId(widget.sendViewModel.wallet);
+ selectedChainId == evm?.getSelectedChainId(widget.sendViewModel.wallet);
if (isCompatible) {
widget.sendViewModel.setSelectedCryptoCurrency(
diff --git a/linux/my_application.cc b/linux/my_application.cc
index 49f9ae13..5038b3c5 100644
--- a/linux/my_application.cc
+++ b/linux/my_application.cc
@@ -46,11 +46,7 @@ static void my_application_activate(GApplication* application) {
} else {
gtk_window_set_title(window, "Cake Wallet");
}
- if (getenv("DESKTOP_FORCE_MOBILE")) {
- gtk_window_set_default_size(window, 720, 1280);
- } else {
- gtk_window_set_default_size(window, 1280, 720);
- }
+ gtk_window_set_default_size(window, 480, 800);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
Why this scored 27/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.