fix: date on silent payments transactions (#2740)
What changed, and why it matters
This commit fixes two practical bugs in Cake Wallet's Bitcoin 'silent payments' feature. First, it corrects the source URL used to look up the date of a silent-payment transaction, switching from an old/invalid Cake mempool endpoint to the current one. Second, it clears stale silent-payment address records during wallet startup and only uses the first generated silent address when building the address list. There is no direct evidence in the commit of a security vulnerability being exploited; the changes are bug fixes and cleanup.
Treat as a routine bug-fix commit. Reviewers may want to confirm that the new mempool endpoint is trustworthy and that clearing silentAddresses does not risk losing funds or transaction history. No urgent security response is indicated by the commit itself.
Security signals we found
Network endpoint changed from mempool.cakewallet.com to cake.mempool.space
Tor instance initialized before silent payment scan
Silent payment address cache cleared when derived address mismatches stored record
Address-list display reduced to first silent address record only
Evidence from the diff
The diff modifies cw_bitcoin/lib/electrum_wallet.dart and cw_bitcoin/lib/electrum_wallet_addresses.dart. In electrum_wallet.dart, it initializes a CakeTor singleton before scanning silent payments and changes the block-height/block lookup URLs from https://mempool.cakewallet.com/api/v1/… to https://cake.mempool.space/api/…, fixing a likely broken date fetch. It also removes a TODO about fetching block data. In electrum_wallet_addresses.dart, it adds logic to clear the silentAddresses list if the stored initial address no longer matches the newly derived silent address, and streamlines address-list generation to use only the first silent address record. These are correctness/maintenance fixes rather than patches for an identified exploit.
Changed components
cw_bitcoin/lib/electrum_wallet.dartcw_bitcoin/lib/electrum_wallet_addresses.dartSilent Payments scanning and transaction date resolutionSilent Payment address derivation/cachingInspect captured patch +17 / −19
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index 298ccc5d..08ca9f23 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -40,6 +40,7 @@ import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/socket_health_logger.dart';
+import 'package:cw_core/utils/tor/abstract.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
@@ -3009,6 +3010,7 @@ class SyncResponse {
Future<void> _handleScanSilentPayments(ScanData scanData) async {
final shouldUpdateSyncStatus = scanData.rescanHeights == null || scanData.rescanHeights!.isEmpty;
final hasForcedRescanHeights = !shouldUpdateSyncStatus;
+ CakeTor.instance = await CakeTorInstance.getInstance();
var node = Uri.parse("tcp://electrs.cakewallet.com:50001");
@@ -3204,19 +3206,12 @@ Future<void> _handleScanSilentPayments(ScanData scanData) async {
// So, if blockDate exists, reuse
if (isDateNow) {
try {
+ final rootURL = "https://cake.mempool.space";
final tweakBlockHash = await ProxyWrapper()
- .get(
- clearnetUri: Uri.parse(
- "https://mempool.cakewallet.com/api/v1/block-height/$tweakHeight",
- ),
- )
+ .get(clearnetUri: Uri.parse("$rootURL/api/block-height/$tweakHeight"))
.timeout(Duration(seconds: 15));
final blockResponse = await ProxyWrapper()
- .get(
- clearnetUri: Uri.parse(
- "https://mempool.cakewallet.com/api/v1/block/${tweakBlockHash.body}",
- ),
- )
+ .get(clearnetUri: Uri.parse("$rootURL/api/block/${tweakBlockHash.body}"))
.timeout(Duration(seconds: 15));
if (blockResponse.statusCode == 200 &&
@@ -3242,7 +3237,6 @@ Future<void> _handleScanSilentPayments(ScanData scanData) async {
fee: 0,
direction: TransactionDirection.incoming,
isReplaced: false,
- // TODO: fetch block data and get the date from it
date: scanData.network == BitcoinNetwork.mainnet
? (isDateNow ? getDateByBitcoinHeight(tweakHeight) : blockDate)
: DateTime.now(),
diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart
index e9142871..d4927e7a 100644
--- a/cw_bitcoin/lib/electrum_wallet_addresses.dart
+++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart
@@ -83,6 +83,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
network: network,
);
+ // Clean the Silent Payment Addresses if the initial addresses are the old SP Addresses
+ if (!silentAddresses
+ .any((addr) => addr.index == 0 && addr.address == silentAddress.toString())) {
+ silentAddresses.clear();
+ }
+
if (!silentAddresses.any((addr) => addr.index == 0 && addr.isHidden == false))
silentAddresses.add(BitcoinSilentPaymentAddressRecord(
silentAddress.toString(),
@@ -457,19 +463,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
addressesMap[address] = 'Active - P2WSH';
}
- silentAddresses.forEach((addressRecord) {
- if (addressRecord.type != SilentPaymentsAddresType.p2sp || addressRecord.isHidden) {
- return;
- }
+ final firstSilentAddressRecord = silentAddresses.firstOrNull;
+ if (firstSilentAddressRecord != null) {
- if (addressRecord.address != address) {
- addressesMap[addressRecord.address] = addressRecord.name.isEmpty
+ if (firstSilentAddressRecord.address != address) {
+ addressesMap[firstSilentAddressRecord.address] = firstSilentAddressRecord.name.isEmpty
? "Silent Payments"
- : "Silent Payments - " + addressRecord.name;
+ : "Silent Payments - ${firstSilentAddressRecord.name}";
} else {
addressesMap[address] = 'Active - Silent Payments';
}
- });
+ }
}
void addLitecoinAddressTypes() {
Why this scored 23/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.