Skip to content

Commit

Permalink
feat: fix rescan & stop, new card
Browse files Browse the repository at this point in the history
  • Loading branch information
rafael-xmr committed Nov 24, 2024
1 parent b7ff9ab commit e16a218
Show file tree
Hide file tree
Showing 41 changed files with 401 additions and 173 deletions.
94 changes: 54 additions & 40 deletions cw_bitcoin/lib/bitcoin_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ part 'bitcoin_wallet.g.dart';
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;

abstract class BitcoinWalletBase extends ElectrumWallet with Store {
@observable
bool nodeSupportsSilentPayments = true;
@observable
bool silentPaymentsScanningActive = false;
@observable
bool allowedToSwitchNodesForScanning = false;

BitcoinWalletBase({
required String password,
required WalletInfo walletInfo,
Expand Down Expand Up @@ -307,63 +314,68 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}

Future<bool> getNodeIsElectrs() async {
if (node?.uri.host.contains("electrs") ?? false) {
return true;
if (node?.isElectrs != null) {
return node!.isElectrs!;
}

final isNamedElectrs = node?.uri.host.contains("electrs") ?? false;
if (isNamedElectrs) {
node!.isElectrs = true;
}

final version = await sendWorker(ElectrumWorkerGetVersionRequest());
final isNamedFulcrum = node!.uri.host.contains("fulcrum");
if (isNamedFulcrum) {
node!.isElectrs = false;
}

if (version is List<String> && version.isNotEmpty) {
final server = version[0];
if (node!.isElectrs == null) {
final version = await sendWorker(ElectrumWorkerGetVersionRequest());

if (server.toLowerCase().contains('electrs')) {
if (version is List<String> && version.isNotEmpty) {
final server = version[0];

if (server.toLowerCase().contains('electrs')) {
node!.isElectrs = true;
}
} else if (version is String && version.toLowerCase().contains('electrs')) {
node!.isElectrs = true;
node!.save();
return node!.isElectrs!;
} else {
node!.isElectrs = false;
}
} else if (version is String && version.toLowerCase().contains('electrs')) {
node!.isElectrs = true;
node!.save();
return node!.isElectrs!;
}

node!.isElectrs = false;
node!.save();
return node!.isElectrs!;
}

Future<bool> getNodeSupportsSilentPayments() async {
// TODO: handle disconnection on check
// TODO: use cached values
if (node == null) {
return false;
}

final isFulcrum = node!.uri.host.contains("fulcrum");
if (isFulcrum) {
return false;
if (node?.supportsSilentPayments != null) {
return node!.supportsSilentPayments!;
}

// As of today (august 2024), only ElectrumRS supports silent payments
if (!(await getNodeIsElectrs())) {
return false;
final isElectrs = await getNodeIsElectrs();
if (!isElectrs) {
node!.supportsSilentPayments = false;
}

try {
final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String;
final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson(
json.decode(workerResponse) as Map<String, dynamic>,
);
final supportsScanning = tweaksResponse.result == true;
if (node!.supportsSilentPayments == null) {
try {
final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String;
final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson(
json.decode(workerResponse) as Map<String, dynamic>,
);
final supportsScanning = tweaksResponse.result == true;

if (supportsScanning) {
node!.supportsSilentPayments = true;
node!.save();
return node!.supportsSilentPayments!;
if (supportsScanning) {
node!.supportsSilentPayments = true;
} else {
node!.supportsSilentPayments = false;
}
} catch (_) {
node!.supportsSilentPayments = false;
}
} catch (_) {}

node!.supportsSilentPayments = false;
}
node!.save();
return node!.supportsSilentPayments!;
}
Expand Down Expand Up @@ -437,8 +449,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
@action
Future<void> setSilentPaymentsScanning(bool active) async {
silentPaymentsScanningActive = active;
final nodeSupportsSilentPayments = await getNodeSupportsSilentPayments();
final isAllowedToScan = nodeSupportsSilentPayments || allowedToSwitchNodesForScanning;

if (active) {
if (active && isAllowedToScan) {
syncStatus = AttemptingScanSyncStatus();

final tip = currentChainTip!;
Expand Down Expand Up @@ -730,8 +744,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
await walletInfo.updateRestoreHeight(height);
}
}

await save();
}

@action
Expand Down Expand Up @@ -765,6 +777,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
.map((addr) => addr.labelIndex)
.toList(),
isSingleScan: doSingleScan ?? false,
shouldSwitchNodes:
!(await getNodeSupportsSilentPayments()) && allowedToSwitchNodesForScanning,
),
).toJson(),
);
Expand Down
5 changes: 0 additions & 5 deletions cw_bitcoin/lib/electrum_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,6 @@ abstract class ElectrumWalletBase
@override
bool isTestnet;

@observable
bool nodeSupportsSilentPayments = true;
@observable
bool silentPaymentsScanningActive = false;

bool _isTryingToConnect = false;

Completer<SharedPreferences> sharedPrefs = Completer();
Expand Down
93 changes: 44 additions & 49 deletions cw_bitcoin/lib/electrum_worker/electrum_worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@ import 'package:cw_bitcoin/electrum_worker/electrum_worker_methods.dart';
import 'package:cw_bitcoin/electrum_worker/electrum_worker_params.dart';
import 'package:cw_bitcoin/electrum_worker/methods/methods.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/http.dart' as http;
import 'package:rxdart/rxdart.dart';
import 'package:sp_scanner/sp_scanner.dart';

class ElectrumWorker {
final SendPort sendPort;
ElectrumApiProvider? _electrumClient;
BasedUtxoNetwork? _network;
bool _isScanning = false;
bool _stopScanRequested = false;
BehaviorSubject<Map<String, dynamic>>? _scanningStream;

ElectrumWorker._(this.sendPort, {ElectrumApiProvider? electrumClient})
: _electrumClient = electrumClient;
Expand All @@ -47,7 +45,7 @@ class ElectrumWorker {
}

void handleMessage(dynamic message) async {
print("Worker received message: $message");
print("Worker: received message: $message");

try {
Map<String, dynamic> messageJson;
Expand Down Expand Up @@ -105,27 +103,15 @@ class ElectrumWorker {
);
break;
case ElectrumWorkerMethods.stopScanningMethod:
print("Worker: received message: $message");
await _handleStopScanning(
ElectrumWorkerStopScanningRequest.fromJson(messageJson),
);
break;
case ElectrumRequestMethods.tweaksSubscribeMethod:
if (_isScanning) {
_stopScanRequested = false;
}

if (!_stopScanRequested) {
await _handleScanSilentPayments(
ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson),
);
} else {
_stopScanRequested = false;
_sendResponse(
ElectrumWorkerTweaksSubscribeResponse(
result: TweaksSyncResponse(syncStatus: SyncedSyncStatus()),
),
);
}
await _handleScanSilentPayments(
ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson),
);

break;
case ElectrumRequestMethods.estimateFeeMethod:
Expand Down Expand Up @@ -550,28 +536,25 @@ class ElectrumWorker {
}

Future<void> _handleStopScanning(ElectrumWorkerStopScanningRequest request) async {
_stopScanRequested = true;
_scanningStream?.close();
_scanningStream = null;
_sendResponse(
ElectrumWorkerStopScanningResponse(result: true, id: request.id),
);
}

Future<void> _handleScanSilentPayments(ElectrumWorkerTweaksSubscribeRequest request) async {
_isScanning = true;
final scanData = request.scanData;

// TODO: confirmedSwitch use new connection
// final _electrumClient = await ElectrumApiProvider.connect(
// ElectrumTCPService.connect(
// Uri.parse("tcp://electrs.cakewallet.com:50001"),
// onConnectionStatusChange: (status) {
// _sendResponse(
// ElectrumWorkerConnectionResponse(status: status, id: request.id),
// );
// },
// ),
// );
var scanningClient = _electrumClient;

if (scanData.shouldSwitchNodes) {
scanningClient = await ElectrumApiProvider.connect(
ElectrumTCPService.connect(
Uri.parse("tcp://electrs.cakewallet.com:50001"),
),
);
}
int syncHeight = scanData.height;
int initialSyncHeight = syncHeight;

Expand All @@ -587,6 +570,15 @@ class ElectrumWorker {
},
);

int getCountPerRequest(int syncHeight) {
if (scanData.isSingleScan) {
return 1;
}

final amountLeft = scanData.chainTip - syncHeight + 1;
return amountLeft;
}

// Initial status UI update, send how many blocks in total to scan
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
result: TweaksSyncResponse(
Expand All @@ -597,17 +589,16 @@ class ElectrumWorker {

final req = ElectrumTweaksSubscribe(
height: syncHeight,
count: 1,
count: getCountPerRequest(syncHeight),
historicalMode: false,
);

final stream = await _electrumClient!.subscribe(req);
_scanningStream = await scanningClient!.subscribe(req);

void listenFn(Map<String, dynamic> event, ElectrumTweaksSubscribe req) {
final response = req.onResponse(event);
if (_stopScanRequested || response == null) {
_stopScanRequested = false;
_isScanning = false;

if (response == null || _scanningStream == null) {
return;
}

Expand All @@ -623,10 +614,10 @@ class ElectrumWorker {
final nextHeight = syncHeight + 1;

if (nextHeight <= scanData.chainTip) {
final nextStream = _electrumClient!.subscribe(
final nextStream = scanningClient!.subscribe(
ElectrumTweaksSubscribe(
height: nextHeight,
count: 1,
count: getCountPerRequest(nextHeight),
historicalMode: false,
),
);
Expand Down Expand Up @@ -710,6 +701,7 @@ class ElectrumWorker {
receivingOutputAddress,
labelIndex: 1, // TODO: get actual index/label
isUsed: true,
// TODO: use right wallet
spendKey: scanData.silentPaymentsWallets.first.b_spend.tweakAdd(
BigintUtils.fromBytes(BytesUtils.fromHexString(t_k)),
),
Expand Down Expand Up @@ -753,24 +745,27 @@ class ElectrumWorker {
),
);

stream?.close();
_scanningStream?.close();
_scanningStream = null;
return;
}
}

stream?.listen((event) => listenFn(event, req));
_isScanning = false;
_scanningStream?.listen((event) => listenFn(event, req));
}

Future<void> _handleGetVersion(ElectrumWorkerGetVersionRequest request) async {
_sendResponse(ElectrumWorkerGetVersionResponse(
result: (await _electrumClient!.request(
_sendResponse(
ElectrumWorkerGetVersionResponse(
result: await _electrumClient!.request(
ElectrumVersion(
clientName: "",
protocolVersion: ["1.4"],
protocolVersion: "1.4",
),
)),
id: request.id));
),
id: request.id,
),
);
}
}

Expand Down
5 changes: 5 additions & 0 deletions cw_bitcoin/lib/electrum_worker/methods/tweaks_subscribe.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class ScanData {
final Map<String, String> labels;
final List<int> labelIndexes;
final bool isSingleScan;
final bool shouldSwitchNodes;

ScanData({
required this.silentPaymentsWallets,
Expand All @@ -19,6 +20,7 @@ class ScanData {
required this.labels,
required this.labelIndexes,
required this.isSingleScan,
required this.shouldSwitchNodes,
});

factory ScanData.fromHeight(ScanData scanData, int newHeight) {
Expand All @@ -31,6 +33,7 @@ class ScanData {
labels: scanData.labels,
labelIndexes: scanData.labelIndexes,
isSingleScan: scanData.isSingleScan,
shouldSwitchNodes: scanData.shouldSwitchNodes,
);
}

Expand All @@ -44,6 +47,7 @@ class ScanData {
'labels': labels,
'labelIndexes': labelIndexes,
'isSingleScan': isSingleScan,
'shouldSwitchNodes': shouldSwitchNodes,
};
}

Expand All @@ -60,6 +64,7 @@ class ScanData {
labels: json['labels'] as Map<String, String>,
labelIndexes: (json['labelIndexes'] as List).map((e) => e as int).toList(),
isSingleScan: json['isSingleScan'] as bool,
shouldSwitchNodes: json['shouldSwitchNodes'] as bool,
);
}
}
Expand Down
Loading

0 comments on commit e16a218

Please sign in to comment.