mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 03:59:23 +00:00
feat: restore & scan imp
This commit is contained in:
parent
9bb3d9f35b
commit
c35dec0d09
13 changed files with 448 additions and 172 deletions
|
@ -108,6 +108,53 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
int initialSilentAddressIndex = 0,
|
int initialSilentAddressIndex = 0,
|
||||||
required bool mempoolAPIEnabled,
|
required bool mempoolAPIEnabled,
|
||||||
}) async {
|
}) async {
|
||||||
|
List<int>? seedBytes = null;
|
||||||
|
final Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets = {};
|
||||||
|
|
||||||
|
if (walletInfo.isRecovery) {
|
||||||
|
for (final derivation in walletInfo.derivations ?? <DerivationInfo>[]) {
|
||||||
|
if (derivation.description?.contains("SP") ?? false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (derivation.derivationType == DerivationType.bip39) {
|
||||||
|
seedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||||
|
hdWallets[CWBitcoinDerivationType.bip39] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
seedBytes = ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||||
|
hdWallets[CWBitcoinDerivationType.electrum] = Bip32Slip10Secp256k1.fromSeed(seedBytes);
|
||||||
|
} catch (e) {
|
||||||
|
print("electrum_v2 seed error: $e");
|
||||||
|
|
||||||
|
try {
|
||||||
|
seedBytes = ElectrumV1SeedGenerator(mnemonic).generate();
|
||||||
|
hdWallets[CWBitcoinDerivationType.electrum] =
|
||||||
|
Bip32Slip10Secp256k1.fromSeed(seedBytes);
|
||||||
|
} catch (e) {
|
||||||
|
print("electrum_v1 seed error: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdWallets[CWBitcoinDerivationType.bip39] != null) {
|
||||||
|
hdWallets[CWBitcoinDerivationType.old_bip39] = hdWallets[CWBitcoinDerivationType.bip39]!;
|
||||||
|
}
|
||||||
|
if (hdWallets[CWBitcoinDerivationType.electrum] != null) {
|
||||||
|
hdWallets[CWBitcoinDerivationType.old_electrum] =
|
||||||
|
hdWallets[CWBitcoinDerivationType.electrum]!;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seedBytes = walletInfo.derivationInfo?.derivationType == DerivationType.electrum
|
||||||
|
? ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase)
|
||||||
|
: Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
return BitcoinWallet(
|
return BitcoinWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
passphrase: passphrase ?? "",
|
passphrase: passphrase ?? "",
|
||||||
|
@ -119,9 +166,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialSilentAddressIndex: initialSilentAddressIndex,
|
initialSilentAddressIndex: initialSilentAddressIndex,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
encryptionFileUtils: encryptionFileUtils,
|
encryptionFileUtils: encryptionFileUtils,
|
||||||
seedBytes: walletInfo.derivationInfo?.derivationType == DerivationType.electrum
|
seedBytes: seedBytes,
|
||||||
? ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase)
|
hdWallets: hdWallets,
|
||||||
: Bip39SeedGenerator.generateFromString(mnemonic, passphrase),
|
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
addressPageType: addressPageType,
|
addressPageType: addressPageType,
|
||||||
|
@ -253,9 +299,13 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> getNodeIsElectrs() async {
|
Future<bool> getNodeIsElectrs() async {
|
||||||
final version = await sendWorker(ElectrumWorkerGetVersionRequest()) as List<String>;
|
if (node?.uri.host.contains("electrs") ?? false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (version.isNotEmpty) {
|
final version = await sendWorker(ElectrumWorkerGetVersionRequest());
|
||||||
|
|
||||||
|
if (version is List<String> && version.isNotEmpty) {
|
||||||
final server = version[0];
|
final server = version[0];
|
||||||
|
|
||||||
if (server.toLowerCase().contains('electrs')) {
|
if (server.toLowerCase().contains('electrs')) {
|
||||||
|
@ -263,6 +313,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
node!.save();
|
node!.save();
|
||||||
return node!.isElectrs!;
|
return node!.isElectrs!;
|
||||||
}
|
}
|
||||||
|
} else if (version is String && version.toLowerCase().contains('electrs')) {
|
||||||
|
node!.isElectrs = true;
|
||||||
|
node!.save();
|
||||||
|
return node!.isElectrs!;
|
||||||
}
|
}
|
||||||
|
|
||||||
node!.isElectrs = false;
|
node!.isElectrs = false;
|
||||||
|
@ -271,33 +325,39 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> getNodeSupportsSilentPayments() async {
|
Future<bool> getNodeSupportsSilentPayments() async {
|
||||||
return true;
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// As of today (august 2024), only ElectrumRS supports silent payments
|
// As of today (august 2024), only ElectrumRS supports silent payments
|
||||||
// if (!(await getNodeIsElectrs())) {
|
if (!(await getNodeIsElectrs())) {
|
||||||
// return false;
|
return false;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (node == null) {
|
try {
|
||||||
// return false;
|
final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String;
|
||||||
// }
|
final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson(
|
||||||
|
json.decode(workerResponse) as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
final supportsScanning = tweaksResponse.result == true;
|
||||||
|
|
||||||
// try {
|
if (supportsScanning) {
|
||||||
// final tweaksResponse = await electrumClient.getTweaks(height: 0);
|
node!.supportsSilentPayments = true;
|
||||||
|
node!.save();
|
||||||
|
return node!.supportsSilentPayments!;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
// if (tweaksResponse != null) {
|
node!.supportsSilentPayments = false;
|
||||||
// node!.supportsSilentPayments = true;
|
node!.save();
|
||||||
// node!.save();
|
return node!.supportsSilentPayments!;
|
||||||
// return node!.supportsSilentPayments!;
|
|
||||||
// }
|
|
||||||
// } on RequestFailedTimeoutException catch (_) {
|
|
||||||
// node!.supportsSilentPayments = false;
|
|
||||||
// node!.save();
|
|
||||||
// return node!.supportsSilentPayments!;
|
|
||||||
// } catch (_) {}
|
|
||||||
|
|
||||||
// node!.supportsSilentPayments = false;
|
|
||||||
// node!.save();
|
|
||||||
// return node!.supportsSilentPayments!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LedgerConnection? _ledgerConnection;
|
LedgerConnection? _ledgerConnection;
|
||||||
|
@ -383,16 +443,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
if (tip > walletInfo.restoreHeight) {
|
if (tip > walletInfo.restoreHeight) {
|
||||||
_setListeners(walletInfo.restoreHeight);
|
_setListeners(walletInfo.restoreHeight);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (syncStatus is! SyncedSyncStatus) {
|
||||||
alwaysScan = false;
|
await sendWorker(ElectrumWorkerStopScanningRequest());
|
||||||
|
await startSync();
|
||||||
// _isolate?.then((value) => value.kill(priority: Isolate.immediate));
|
|
||||||
|
|
||||||
// if (rpc!.isConnected) {
|
|
||||||
// syncStatus = SyncedSyncStatus();
|
|
||||||
// } else {
|
|
||||||
// syncStatus = NotConnectedSyncStatus();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,9 +618,16 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
messageJson = message as Map<String, dynamic>;
|
messageJson = message as Map<String, dynamic>;
|
||||||
}
|
}
|
||||||
final workerMethod = messageJson['method'] as String;
|
final workerMethod = messageJson['method'] as String;
|
||||||
|
final workerError = messageJson['error'] as String?;
|
||||||
|
|
||||||
switch (workerMethod) {
|
switch (workerMethod) {
|
||||||
case ElectrumRequestMethods.tweaksSubscribeMethod:
|
case ElectrumRequestMethods.tweaksSubscribeMethod:
|
||||||
|
if (workerError != null) {
|
||||||
|
print(messageJson);
|
||||||
|
// _onConnectionStatusChange(ConnectionStatus.failed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
final response = ElectrumWorkerTweaksSubscribeResponse.fromJson(messageJson);
|
final response = ElectrumWorkerTweaksSubscribeResponse.fromJson(messageJson);
|
||||||
onTweaksSyncResponse(response.result);
|
onTweaksSyncResponse(response.result);
|
||||||
break;
|
break;
|
||||||
|
@ -651,9 +711,16 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
syncStatus = SyncingSyncStatus(newSyncStatus.blocksLeft, newSyncStatus.ptc);
|
syncStatus = SyncingSyncStatus(newSyncStatus.blocksLeft, newSyncStatus.ptc);
|
||||||
} else {
|
} else {
|
||||||
syncStatus = newSyncStatus;
|
syncStatus = newSyncStatus;
|
||||||
|
|
||||||
|
if (newSyncStatus is SyncedSyncStatus) {
|
||||||
|
silentPaymentsScanningActive = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await walletInfo.updateRestoreHeight(result.height!);
|
final height = result.height;
|
||||||
|
if (height != null) {
|
||||||
|
await walletInfo.updateRestoreHeight(height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await save();
|
await save();
|
||||||
|
@ -801,6 +868,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
case SyncingSyncStatus:
|
case SyncingSyncStatus:
|
||||||
return;
|
return;
|
||||||
case SyncedTipSyncStatus:
|
case SyncedTipSyncStatus:
|
||||||
|
silentPaymentsScanningActive = false;
|
||||||
|
|
||||||
// Message is shown on the UI for 3 seconds, then reverted to synced
|
// Message is shown on the UI for 3 seconds, then reverted to synced
|
||||||
Timer(Duration(seconds: 3), () {
|
Timer(Duration(seconds: 3), () {
|
||||||
if (this.syncStatus is SyncedTipSyncStatus) this.syncStatus = SyncedSyncStatus();
|
if (this.syncStatus is SyncedTipSyncStatus) this.syncStatus = SyncedSyncStatus();
|
||||||
|
|
|
@ -361,6 +361,15 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@action
|
||||||
|
void updateHiddenAddresses() {
|
||||||
|
super.updateHiddenAddresses();
|
||||||
|
this.hiddenAddresses.addAll(silentPaymentAddresses
|
||||||
|
.where((addressRecord) => addressRecord.isHidden)
|
||||||
|
.map((addressRecord) => addressRecord.address));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = super.toJson();
|
final json = super.toJson();
|
||||||
json['silentPaymentAddresses'] =
|
json['silentPaymentAddresses'] =
|
||||||
|
|
|
@ -125,7 +125,7 @@ abstract class ElectrumWalletBase
|
||||||
workerSendPort!.send(json);
|
workerSendPort!.send(json);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return completer.future.timeout(Duration(seconds: 5));
|
return completer.future.timeout(Duration(seconds: 30));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_errorCompleters.addAll({messageId: e});
|
_errorCompleters.addAll({messageId: e});
|
||||||
_responseCompleters.remove(messageId);
|
_responseCompleters.remove(messageId);
|
||||||
|
@ -146,13 +146,8 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
final workerMethod = messageJson['method'] as String;
|
final workerMethod = messageJson['method'] as String;
|
||||||
final workerError = messageJson['error'] as String?;
|
final workerError = messageJson['error'] as String?;
|
||||||
|
|
||||||
if (workerError != null) {
|
|
||||||
print('Worker error: $workerError');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final responseId = messageJson['id'] as int?;
|
final responseId = messageJson['id'] as int?;
|
||||||
|
|
||||||
if (responseId != null && _responseCompleters.containsKey(responseId)) {
|
if (responseId != null && _responseCompleters.containsKey(responseId)) {
|
||||||
_responseCompleters[responseId]!.complete(message);
|
_responseCompleters[responseId]!.complete(message);
|
||||||
_responseCompleters.remove(responseId);
|
_responseCompleters.remove(responseId);
|
||||||
|
@ -160,6 +155,11 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
switch (workerMethod) {
|
switch (workerMethod) {
|
||||||
case ElectrumWorkerMethods.connectionMethod:
|
case ElectrumWorkerMethods.connectionMethod:
|
||||||
|
if (workerError != null) {
|
||||||
|
_onConnectionStatusChange(ConnectionStatus.failed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
final response = ElectrumWorkerConnectionResponse.fromJson(messageJson);
|
final response = ElectrumWorkerConnectionResponse.fromJson(messageJson);
|
||||||
_onConnectionStatusChange(response.result);
|
_onConnectionStatusChange(response.result);
|
||||||
break;
|
break;
|
||||||
|
@ -214,6 +214,7 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
bool? alwaysScan;
|
bool? alwaysScan;
|
||||||
bool mempoolAPIEnabled;
|
bool mempoolAPIEnabled;
|
||||||
|
bool _updatingHistories = false;
|
||||||
|
|
||||||
final Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets;
|
final Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets;
|
||||||
Bip32Slip10Secp256k1 get bip32 => walletAddresses.hdWallet;
|
Bip32Slip10Secp256k1 get bip32 => walletAddresses.hdWallet;
|
||||||
|
@ -323,7 +324,8 @@ abstract class ElectrumWalletBase
|
||||||
List<String> scripthashesListening;
|
List<String> scripthashesListening;
|
||||||
|
|
||||||
bool _chainTipListenerOn = false;
|
bool _chainTipListenerOn = false;
|
||||||
bool _isInitialSync = true;
|
// TODO: improve this
|
||||||
|
int _syncedTimes = 0;
|
||||||
|
|
||||||
void Function(FlutterErrorDetails)? _onError;
|
void Function(FlutterErrorDetails)? _onError;
|
||||||
Timer? _autoSaveTimer;
|
Timer? _autoSaveTimer;
|
||||||
|
@ -348,9 +350,11 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
syncStatus = SynchronizingSyncStatus();
|
syncStatus = SynchronizingSyncStatus();
|
||||||
|
|
||||||
// INFO: FIRST: Call subscribe for headers, wait for completion to update currentChainTip (needed for other methods)
|
// INFO: FIRST (always): Call subscribe for headers, wait for completion to update currentChainTip (needed for other methods)
|
||||||
await sendWorker(ElectrumWorkerHeadersSubscribeRequest());
|
await sendWorker(ElectrumWorkerHeadersSubscribeRequest());
|
||||||
|
|
||||||
|
_syncedTimes = 0;
|
||||||
|
|
||||||
// INFO: SECOND: Start loading transaction histories for every address, this will help discover addresses until the unused gap limit has been reached, which will help finding the full balance and unspents next
|
// INFO: SECOND: Start loading transaction histories for every address, this will help discover addresses until the unused gap limit has been reached, which will help finding the full balance and unspents next
|
||||||
await updateTransactions();
|
await updateTransactions();
|
||||||
|
|
||||||
|
@ -365,13 +369,14 @@ abstract class ElectrumWalletBase
|
||||||
_updateFeeRateTimer ??=
|
_updateFeeRateTimer ??=
|
||||||
Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
||||||
|
|
||||||
_isInitialSync = false;
|
if (_syncedTimes == 3) {
|
||||||
syncStatus = SyncedSyncStatus();
|
syncStatus = SyncedSyncStatus();
|
||||||
|
}
|
||||||
|
|
||||||
await save();
|
await save();
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
print(stacktrace);
|
|
||||||
print("startSync $e");
|
print("startSync $e");
|
||||||
|
print(stacktrace);
|
||||||
syncStatus = FailedSyncStatus();
|
syncStatus = FailedSyncStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,8 +394,10 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> onFeesResponse(TransactionPriorities result) async {
|
Future<void> onFeesResponse(TransactionPriorities? result) async {
|
||||||
feeRates = result;
|
if (result != null) {
|
||||||
|
feeRates = result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Node? node;
|
Node? node;
|
||||||
|
@ -400,8 +407,6 @@ abstract class ElectrumWalletBase
|
||||||
Future<void> connectToNode({required Node node}) async {
|
Future<void> connectToNode({required Node node}) async {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
|
||||||
if (syncStatus is ConnectingSyncStatus) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
|
|
||||||
|
@ -416,6 +421,7 @@ abstract class ElectrumWalletBase
|
||||||
_workerIsolate = await Isolate.spawn<SendPort>(ElectrumWorker.run, receivePort!.sendPort);
|
_workerIsolate = await Isolate.spawn<SendPort>(ElectrumWorker.run, receivePort!.sendPort);
|
||||||
|
|
||||||
_workerSubscription = receivePort!.listen((message) {
|
_workerSubscription = receivePort!.listen((message) {
|
||||||
|
print('Main: received message: $message');
|
||||||
if (message is SendPort) {
|
if (message is SendPort) {
|
||||||
workerSendPort = message;
|
workerSendPort = message;
|
||||||
workerSendPort!.send(
|
workerSendPort!.send(
|
||||||
|
@ -1159,15 +1165,11 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> updateAllUnspents() async {
|
Future<void> updateAllUnspents() async {
|
||||||
final req = ElectrumWorkerListUnspentRequest(
|
workerSendPort!.send(
|
||||||
scripthashes: walletAddresses.allScriptHashes.toList(),
|
ElectrumWorkerListUnspentRequest(
|
||||||
|
scripthashes: walletAddresses.allScriptHashes.toList(),
|
||||||
|
).toJson(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_isInitialSync) {
|
|
||||||
await sendWorker(req);
|
|
||||||
} else {
|
|
||||||
workerSendPort!.send(req.toJson());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -1222,6 +1224,11 @@ abstract class ElectrumWalletBase
|
||||||
unspentCoins.forEach(updateCoin);
|
unspentCoins.forEach(updateCoin);
|
||||||
|
|
||||||
await refreshUnspentCoinsInfo();
|
await refreshUnspentCoinsInfo();
|
||||||
|
|
||||||
|
_syncedTimes++;
|
||||||
|
if (_syncedTimes == 3) {
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -1299,10 +1306,13 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> onHistoriesResponse(List<AddressHistoriesResponse> histories) async {
|
Future<void> onHistoriesResponse(List<AddressHistoriesResponse> histories) async {
|
||||||
if (histories.isEmpty) {
|
if (histories.isEmpty || _updatingHistories) {
|
||||||
|
_updatingHistories = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updatingHistories = true;
|
||||||
|
|
||||||
final addressesWithHistory = <BitcoinAddressRecord>[];
|
final addressesWithHistory = <BitcoinAddressRecord>[];
|
||||||
BitcoinAddressType? lastDiscoveredType;
|
BitcoinAddressType? lastDiscoveredType;
|
||||||
|
|
||||||
|
@ -1340,7 +1350,13 @@ abstract class ElectrumWalletBase
|
||||||
isChange: isChange,
|
isChange: isChange,
|
||||||
derivationType: addressRecord.derivationType,
|
derivationType: addressRecord.derivationType,
|
||||||
addressType: addressRecord.addressType,
|
addressType: addressRecord.addressType,
|
||||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(addressRecord.addressType),
|
derivationInfo: BitcoinAddressUtils.getDerivationFromType(
|
||||||
|
addressRecord.addressType,
|
||||||
|
isElectrum: [
|
||||||
|
CWBitcoinDerivationType.electrum,
|
||||||
|
CWBitcoinDerivationType.old_electrum,
|
||||||
|
].contains(addressRecord.derivationType),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final newAddressList =
|
final newAddressList =
|
||||||
|
@ -1364,6 +1380,12 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
walletAddresses.updateHiddenAddresses();
|
walletAddresses.updateHiddenAddresses();
|
||||||
|
_updatingHistories = false;
|
||||||
|
|
||||||
|
_syncedTimes++;
|
||||||
|
if (_syncedTimes == 3) {
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
||||||
|
@ -1606,10 +1628,8 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> updateTransactions([List<BitcoinAddressRecord>? addresses]) async {
|
Future<void> updateTransactions([List<BitcoinAddressRecord>? addresses]) async {
|
||||||
addresses ??= walletAddresses.allAddresses.toList();
|
workerSendPort!.send(ElectrumWorkerGetHistoryRequest(
|
||||||
|
addresses: walletAddresses.allAddresses.toList(),
|
||||||
final req = ElectrumWorkerGetHistoryRequest(
|
|
||||||
addresses: addresses,
|
|
||||||
storedTxs: transactionHistory.transactions.values.toList(),
|
storedTxs: transactionHistory.transactions.values.toList(),
|
||||||
walletType: type,
|
walletType: type,
|
||||||
// If we still don't have currentChainTip, txs will still be fetched but shown
|
// If we still don't have currentChainTip, txs will still be fetched but shown
|
||||||
|
@ -1617,13 +1637,7 @@ abstract class ElectrumWalletBase
|
||||||
chainTip: currentChainTip ?? getBitcoinHeightByDate(date: DateTime.now()),
|
chainTip: currentChainTip ?? getBitcoinHeightByDate(date: DateTime.now()),
|
||||||
network: network,
|
network: network,
|
||||||
mempoolAPIEnabled: mempoolAPIEnabled,
|
mempoolAPIEnabled: mempoolAPIEnabled,
|
||||||
);
|
).toJson());
|
||||||
|
|
||||||
if (_isInitialSync) {
|
|
||||||
await sendWorker(req);
|
|
||||||
} else {
|
|
||||||
workerSendPort!.send(req.toJson());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -1663,17 +1677,18 @@ abstract class ElectrumWalletBase
|
||||||
unconfirmed: totalUnconfirmed,
|
unconfirmed: totalUnconfirmed,
|
||||||
frozen: totalFrozen,
|
frozen: totalFrozen,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_syncedTimes++;
|
||||||
|
if (_syncedTimes == 3) {
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> updateBalance() async {
|
Future<void> updateBalance() async {
|
||||||
final req = ElectrumWorkerGetBalanceRequest(scripthashes: walletAddresses.allScriptHashes);
|
workerSendPort!.send(ElectrumWorkerGetBalanceRequest(
|
||||||
|
scripthashes: walletAddresses.allScriptHashes,
|
||||||
if (_isInitialSync) {
|
).toJson());
|
||||||
await sendWorker(req);
|
|
||||||
} else {
|
|
||||||
workerSendPort!.send(req.toJson());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -23,6 +23,8 @@ class ElectrumWorker {
|
||||||
final SendPort sendPort;
|
final SendPort sendPort;
|
||||||
ElectrumApiProvider? _electrumClient;
|
ElectrumApiProvider? _electrumClient;
|
||||||
BasedUtxoNetwork? _network;
|
BasedUtxoNetwork? _network;
|
||||||
|
bool _isScanning = false;
|
||||||
|
bool _stopScanRequested = false;
|
||||||
|
|
||||||
ElectrumWorker._(this.sendPort, {ElectrumApiProvider? electrumClient})
|
ElectrumWorker._(this.sendPort, {ElectrumApiProvider? electrumClient})
|
||||||
: _electrumClient = electrumClient;
|
: _electrumClient = electrumClient;
|
||||||
|
@ -45,8 +47,6 @@ class ElectrumWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleMessage(dynamic message) async {
|
void handleMessage(dynamic message) async {
|
||||||
print("Worker: received message: $message");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> messageJson;
|
Map<String, dynamic> messageJson;
|
||||||
if (message is String) {
|
if (message is String) {
|
||||||
|
@ -97,10 +97,35 @@ class ElectrumWorker {
|
||||||
ElectrumWorkerBroadcastRequest.fromJson(messageJson),
|
ElectrumWorkerBroadcastRequest.fromJson(messageJson),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case ElectrumRequestMethods.tweaksSubscribeMethod:
|
case ElectrumWorkerMethods.checkTweaksMethod:
|
||||||
await _handleScanSilentPayments(
|
await _handleCheckTweaks(
|
||||||
ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson),
|
ElectrumWorkerCheckTweaksRequest.fromJson(messageJson),
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
|
case ElectrumWorkerMethods.stopScanningMethod:
|
||||||
|
await _handleStopScanning(
|
||||||
|
ElectrumWorkerStopScanningRequest.fromJson(messageJson),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ElectrumRequestMethods.estimateFeeMethod:
|
||||||
|
case ElectrumRequestMethods.tweaksSubscribeMethod:
|
||||||
|
if (_isScanning) {
|
||||||
|
_stopScanRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_stopScanRequested) {
|
||||||
|
await _handleScanSilentPayments(
|
||||||
|
ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_stopScanRequested = false;
|
||||||
|
_sendResponse(
|
||||||
|
ElectrumWorkerTweaksSubscribeResponse(
|
||||||
|
result: TweaksSyncResponse(syncStatus: SyncedSyncStatus()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ElectrumRequestMethods.estimateFeeMethod:
|
case ElectrumRequestMethods.estimateFeeMethod:
|
||||||
await _handleGetFeeRates(
|
await _handleGetFeeRates(
|
||||||
|
@ -113,8 +138,7 @@ class ElectrumWorker {
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e) {
|
||||||
print(s);
|
|
||||||
_sendError(ElectrumWorkerErrorResponse(error: e.toString()));
|
_sendError(ElectrumWorkerErrorResponse(error: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,25 +146,29 @@ class ElectrumWorker {
|
||||||
Future<void> _handleConnect(ElectrumWorkerConnectionRequest request) async {
|
Future<void> _handleConnect(ElectrumWorkerConnectionRequest request) async {
|
||||||
_network = request.network;
|
_network = request.network;
|
||||||
|
|
||||||
_electrumClient = await ElectrumApiProvider.connect(
|
try {
|
||||||
request.useSSL
|
_electrumClient = await ElectrumApiProvider.connect(
|
||||||
? ElectrumSSLService.connect(
|
request.useSSL
|
||||||
request.uri,
|
? ElectrumSSLService.connect(
|
||||||
onConnectionStatusChange: (status) {
|
request.uri,
|
||||||
_sendResponse(ElectrumWorkerConnectionResponse(status: status, id: request.id));
|
onConnectionStatusChange: (status) {
|
||||||
},
|
_sendResponse(
|
||||||
defaultRequestTimeOut: const Duration(seconds: 5),
|
ElectrumWorkerConnectionResponse(status: status, id: request.id),
|
||||||
connectionTimeOut: const Duration(seconds: 5),
|
);
|
||||||
)
|
},
|
||||||
: ElectrumTCPService.connect(
|
)
|
||||||
request.uri,
|
: ElectrumTCPService.connect(
|
||||||
onConnectionStatusChange: (status) {
|
request.uri,
|
||||||
_sendResponse(ElectrumWorkerConnectionResponse(status: status, id: request.id));
|
onConnectionStatusChange: (status) {
|
||||||
},
|
_sendResponse(
|
||||||
defaultRequestTimeOut: const Duration(seconds: 5),
|
ElectrumWorkerConnectionResponse(status: status, id: request.id),
|
||||||
connectionTimeOut: const Duration(seconds: 5),
|
);
|
||||||
),
|
},
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
_sendError(ElectrumWorkerConnectionError(error: e.toString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleHeadersSubscribe(ElectrumWorkerHeadersSubscribeRequest request) async {
|
Future<void> _handleHeadersSubscribe(ElectrumWorkerHeadersSubscribeRequest request) async {
|
||||||
|
@ -230,6 +258,7 @@ class ElectrumWorker {
|
||||||
hash: txid,
|
hash: txid,
|
||||||
currentChainTip: result.chainTip,
|
currentChainTip: result.chainTip,
|
||||||
mempoolAPIEnabled: result.mempoolAPIEnabled,
|
mempoolAPIEnabled: result.mempoolAPIEnabled,
|
||||||
|
getTime: true,
|
||||||
confirmations: tx?.confirmations,
|
confirmations: tx?.confirmations,
|
||||||
date: tx?.date,
|
date: tx?.date,
|
||||||
),
|
),
|
||||||
|
@ -367,6 +396,7 @@ class ElectrumWorker {
|
||||||
required String hash,
|
required String hash,
|
||||||
required int currentChainTip,
|
required int currentChainTip,
|
||||||
required bool mempoolAPIEnabled,
|
required bool mempoolAPIEnabled,
|
||||||
|
bool getTime = false,
|
||||||
int? confirmations,
|
int? confirmations,
|
||||||
DateTime? date,
|
DateTime? date,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -378,52 +408,54 @@ class ElectrumWorker {
|
||||||
ElectrumGetTransactionHex(transactionHash: hash),
|
ElectrumGetTransactionHex(transactionHash: hash),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mempoolAPIEnabled) {
|
if (getTime) {
|
||||||
try {
|
if (mempoolAPIEnabled) {
|
||||||
final txVerbose = await http.get(
|
try {
|
||||||
Uri.parse(
|
final txVerbose = await http.get(
|
||||||
"http://mempool.cakewallet.com:8999/api/v1/tx/$hash/status",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (txVerbose.statusCode == 200 &&
|
|
||||||
txVerbose.body.isNotEmpty &&
|
|
||||||
jsonDecode(txVerbose.body) != null) {
|
|
||||||
height = jsonDecode(txVerbose.body)['block_height'] as int;
|
|
||||||
|
|
||||||
final blockHash = await http.get(
|
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
"http://mempool.cakewallet.com:8999/api/v1/block-height/$height",
|
"http://mempool.cakewallet.com:8999/api/v1/tx/$hash/status",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (blockHash.statusCode == 200 && blockHash.body.isNotEmpty) {
|
if (txVerbose.statusCode == 200 &&
|
||||||
final blockResponse = await http.get(
|
txVerbose.body.isNotEmpty &&
|
||||||
|
jsonDecode(txVerbose.body) != null) {
|
||||||
|
height = jsonDecode(txVerbose.body)['block_height'] as int;
|
||||||
|
|
||||||
|
final blockHash = await http.get(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
"http://mempool.cakewallet.com:8999/api/v1/block/${blockHash.body}",
|
"http://mempool.cakewallet.com:8999/api/v1/block-height/$height",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (blockResponse.statusCode == 200 &&
|
if (blockHash.statusCode == 200 && blockHash.body.isNotEmpty) {
|
||||||
blockResponse.body.isNotEmpty &&
|
final blockResponse = await http.get(
|
||||||
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
Uri.parse(
|
||||||
time = int.parse(jsonDecode(blockResponse.body)['timestamp'].toString());
|
"http://mempool.cakewallet.com:8999/api/v1/block/${blockHash.body}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (date != null) {
|
if (blockResponse.statusCode == 200 &&
|
||||||
final newDate = DateTime.fromMillisecondsSinceEpoch(time * 1000);
|
blockResponse.body.isNotEmpty &&
|
||||||
isDateValidated = newDate == date;
|
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
||||||
|
time = int.parse(jsonDecode(blockResponse.body)['timestamp'].toString());
|
||||||
|
|
||||||
|
if (date != null) {
|
||||||
|
final newDate = DateTime.fromMillisecondsSinceEpoch(time * 1000);
|
||||||
|
isDateValidated = newDate == date;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} catch (_) {}
|
||||||
} catch (_) {}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (confirmations == null && height != null) {
|
if (confirmations == null && height != null) {
|
||||||
final tip = currentChainTip;
|
final tip = currentChainTip;
|
||||||
if (tip > 0 && height > 0) {
|
if (tip > 0 && height > 0) {
|
||||||
// Add one because the block itself is the first confirmation
|
// Add one because the block itself is the first confirmation
|
||||||
confirmations = tip - height + 1;
|
confirmations = tip - height + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,20 +530,47 @@ class ElectrumWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _handleCheckTweaks(ElectrumWorkerCheckTweaksRequest request) async {
|
||||||
|
final response = await _electrumClient!.request(
|
||||||
|
ElectrumTweaksSubscribe(
|
||||||
|
height: 0,
|
||||||
|
count: 1,
|
||||||
|
historicalMode: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final supportsScanning = response != null;
|
||||||
|
_sendResponse(
|
||||||
|
ElectrumWorkerCheckTweaksResponse(result: supportsScanning, id: request.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleStopScanning(ElectrumWorkerStopScanningRequest request) async {
|
||||||
|
_stopScanRequested = true;
|
||||||
|
_sendResponse(
|
||||||
|
ElectrumWorkerStopScanningResponse(result: true, id: request.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _handleScanSilentPayments(ElectrumWorkerTweaksSubscribeRequest request) async {
|
Future<void> _handleScanSilentPayments(ElectrumWorkerTweaksSubscribeRequest request) async {
|
||||||
|
_isScanning = true;
|
||||||
final scanData = request.scanData;
|
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),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
|
||||||
int syncHeight = scanData.height;
|
int syncHeight = scanData.height;
|
||||||
int initialSyncHeight = syncHeight;
|
int initialSyncHeight = syncHeight;
|
||||||
|
|
||||||
int getCountPerRequest(int syncHeight) {
|
|
||||||
if (scanData.isSingleScan) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
final amountLeft = scanData.chainTip - syncHeight + 1;
|
|
||||||
return amountLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
final receivers = scanData.silentPaymentsWallets.map(
|
final receivers = scanData.silentPaymentsWallets.map(
|
||||||
(wallet) {
|
(wallet) {
|
||||||
return Receiver(
|
return Receiver(
|
||||||
|
@ -525,7 +584,6 @@ class ElectrumWorker {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initial status UI update, send how many blocks in total to scan
|
// Initial status UI update, send how many blocks in total to scan
|
||||||
final initialCount = getCountPerRequest(syncHeight);
|
|
||||||
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
|
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
|
||||||
result: TweaksSyncResponse(
|
result: TweaksSyncResponse(
|
||||||
height: syncHeight,
|
height: syncHeight,
|
||||||
|
@ -535,14 +593,19 @@ class ElectrumWorker {
|
||||||
|
|
||||||
final req = ElectrumTweaksSubscribe(
|
final req = ElectrumTweaksSubscribe(
|
||||||
height: syncHeight,
|
height: syncHeight,
|
||||||
count: initialCount,
|
count: 1,
|
||||||
historicalMode: false,
|
historicalMode: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
final stream = await _electrumClient!.subscribe(req);
|
final stream = await _electrumClient!.subscribe(req);
|
||||||
|
|
||||||
Future<void> listenFn(Map<String, dynamic> event, ElectrumTweaksSubscribe req) async {
|
void listenFn(Map<String, dynamic> event, ElectrumTweaksSubscribe req) {
|
||||||
final response = req.onResponse(event);
|
final response = req.onResponse(event);
|
||||||
|
if (_stopScanRequested || response == null) {
|
||||||
|
_stopScanRequested = false;
|
||||||
|
_isScanning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// success or error msg
|
// success or error msg
|
||||||
final noData = response.message != null;
|
final noData = response.message != null;
|
||||||
|
@ -554,13 +617,12 @@ class ElectrumWorker {
|
||||||
|
|
||||||
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
||||||
final nextHeight = syncHeight + 1;
|
final nextHeight = syncHeight + 1;
|
||||||
final nextCount = getCountPerRequest(nextHeight);
|
|
||||||
|
|
||||||
if (nextCount > 0) {
|
if (nextHeight <= scanData.chainTip) {
|
||||||
final nextStream = await _electrumClient!.subscribe(
|
final nextStream = _electrumClient!.subscribe(
|
||||||
ElectrumTweaksSubscribe(
|
ElectrumTweaksSubscribe(
|
||||||
height: syncHeight,
|
height: nextHeight,
|
||||||
count: initialCount,
|
count: 1,
|
||||||
historicalMode: false,
|
historicalMode: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -693,6 +755,7 @@ class ElectrumWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
stream?.listen((event) => listenFn(event, req));
|
stream?.listen((event) => listenFn(event, req));
|
||||||
|
_isScanning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleGetVersion(ElectrumWorkerGetVersionRequest request) async {
|
Future<void> _handleGetVersion(ElectrumWorkerGetVersionRequest request) async {
|
||||||
|
|
|
@ -5,10 +5,14 @@ class ElectrumWorkerMethods {
|
||||||
static const String connectionMethod = "connection";
|
static const String connectionMethod = "connection";
|
||||||
static const String unknownMethod = "unknown";
|
static const String unknownMethod = "unknown";
|
||||||
static const String txHashMethod = "txHash";
|
static const String txHashMethod = "txHash";
|
||||||
|
static const String checkTweaksMethod = "checkTweaks";
|
||||||
|
static const String stopScanningMethod = "stopScanning";
|
||||||
|
|
||||||
static const ElectrumWorkerMethods connect = ElectrumWorkerMethods._(connectionMethod);
|
static const ElectrumWorkerMethods connect = ElectrumWorkerMethods._(connectionMethod);
|
||||||
static const ElectrumWorkerMethods unknown = ElectrumWorkerMethods._(unknownMethod);
|
static const ElectrumWorkerMethods unknown = ElectrumWorkerMethods._(unknownMethod);
|
||||||
static const ElectrumWorkerMethods txHash = ElectrumWorkerMethods._(txHashMethod);
|
static const ElectrumWorkerMethods txHash = ElectrumWorkerMethods._(txHashMethod);
|
||||||
|
static const ElectrumWorkerMethods checkTweaks = ElectrumWorkerMethods._(checkTweaksMethod);
|
||||||
|
static const ElectrumWorkerMethods stopScanning = ElectrumWorkerMethods._(stopScanningMethod);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
part of 'methods.dart';
|
||||||
|
|
||||||
|
class ElectrumWorkerCheckTweaksRequest implements ElectrumWorkerRequest {
|
||||||
|
ElectrumWorkerCheckTweaksRequest({this.id});
|
||||||
|
|
||||||
|
final int? id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String method = ElectrumWorkerMethods.checkTweaks.method;
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory ElectrumWorkerCheckTweaksRequest.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ElectrumWorkerCheckTweaksRequest(id: json['id'] as int?);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {'method': method, 'id': id};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ElectrumWorkerCheckTweaksError extends ElectrumWorkerErrorResponse {
|
||||||
|
ElectrumWorkerCheckTweaksError({required super.error, super.id}) : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String method = ElectrumWorkerMethods.checkTweaks.method;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ElectrumWorkerCheckTweaksResponse extends ElectrumWorkerResponse<bool, String> {
|
||||||
|
ElectrumWorkerCheckTweaksResponse({
|
||||||
|
required super.result,
|
||||||
|
super.error,
|
||||||
|
super.id,
|
||||||
|
}) : super(method: ElectrumWorkerMethods.checkTweaks.method);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String resultJson(result) {
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory ElectrumWorkerCheckTweaksResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ElectrumWorkerCheckTweaksResponse(
|
||||||
|
result: json['result'] == "true",
|
||||||
|
error: json['error'] as String?,
|
||||||
|
id: json['id'] as int?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ class ElectrumWorkerGetFeesError extends ElectrumWorkerErrorResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ElectrumWorkerGetFeesResponse
|
class ElectrumWorkerGetFeesResponse
|
||||||
extends ElectrumWorkerResponse<TransactionPriorities, Map<String, int>> {
|
extends ElectrumWorkerResponse<TransactionPriorities?, Map<String, int>> {
|
||||||
ElectrumWorkerGetFeesResponse({
|
ElectrumWorkerGetFeesResponse({
|
||||||
required super.result,
|
required super.result,
|
||||||
super.error,
|
super.error,
|
||||||
|
@ -46,13 +46,15 @@ class ElectrumWorkerGetFeesResponse
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, int> resultJson(result) {
|
Map<String, int> resultJson(result) {
|
||||||
return result.toJson();
|
return result?.toJson() ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
factory ElectrumWorkerGetFeesResponse.fromJson(Map<String, dynamic> json) {
|
factory ElectrumWorkerGetFeesResponse.fromJson(Map<String, dynamic> json) {
|
||||||
return ElectrumWorkerGetFeesResponse(
|
return ElectrumWorkerGetFeesResponse(
|
||||||
result: deserializeTransactionPriorities(json['result'] as Map<String, dynamic>),
|
result: json['result'] == null
|
||||||
|
? null
|
||||||
|
: deserializeTransactionPriorities(json['result'] as Map<String, dynamic>),
|
||||||
error: json['error'] as String?,
|
error: json['error'] as String?,
|
||||||
id: json['id'] as int?,
|
id: json['id'] as int?,
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,3 +20,5 @@ part 'list_unspent.dart';
|
||||||
part 'tweaks_subscribe.dart';
|
part 'tweaks_subscribe.dart';
|
||||||
part 'get_fees.dart';
|
part 'get_fees.dart';
|
||||||
part 'version.dart';
|
part 'version.dart';
|
||||||
|
part 'check_tweaks_method.dart';
|
||||||
|
part 'stop_scanning.dart';
|
||||||
|
|
49
cw_bitcoin/lib/electrum_worker/methods/stop_scanning.dart
Normal file
49
cw_bitcoin/lib/electrum_worker/methods/stop_scanning.dart
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
part of 'methods.dart';
|
||||||
|
|
||||||
|
class ElectrumWorkerStopScanningRequest implements ElectrumWorkerRequest {
|
||||||
|
ElectrumWorkerStopScanningRequest({this.id});
|
||||||
|
|
||||||
|
final int? id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String method = ElectrumWorkerMethods.stopScanning.method;
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory ElectrumWorkerStopScanningRequest.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ElectrumWorkerStopScanningRequest(id: json['id'] as int?);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {'method': method, 'id': id};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ElectrumWorkerStopScanningError extends ElectrumWorkerErrorResponse {
|
||||||
|
ElectrumWorkerStopScanningError({required super.error, super.id}) : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String method = ElectrumWorkerMethods.stopScanning.method;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ElectrumWorkerStopScanningResponse extends ElectrumWorkerResponse<bool, String> {
|
||||||
|
ElectrumWorkerStopScanningResponse({
|
||||||
|
required super.result,
|
||||||
|
super.error,
|
||||||
|
super.id,
|
||||||
|
}) : super(method: ElectrumWorkerMethods.stopScanning.method);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String resultJson(result) {
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory ElectrumWorkerStopScanningResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ElectrumWorkerStopScanningResponse(
|
||||||
|
result: json['result'] as bool,
|
||||||
|
error: json['error'] as String?,
|
||||||
|
id: json['id'] as int?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -245,6 +245,9 @@ Future<int> getHavenCurrentHeight() async {
|
||||||
|
|
||||||
// Data taken from https://timechaincalendar.com/
|
// Data taken from https://timechaincalendar.com/
|
||||||
const bitcoinDates = {
|
const bitcoinDates = {
|
||||||
|
"2024-11": 868345,
|
||||||
|
"2024-10": 863584,
|
||||||
|
"2024-09": 859317,
|
||||||
"2024-08": 854889,
|
"2024-08": 854889,
|
||||||
"2024-07": 850182,
|
"2024-07": 850182,
|
||||||
"2024-06": 846005,
|
"2024-06": 846005,
|
||||||
|
|
|
@ -422,7 +422,9 @@ class CWBitcoin extends Bitcoin {
|
||||||
var bip39SeedBytes;
|
var bip39SeedBytes;
|
||||||
try {
|
try {
|
||||||
bip39SeedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
|
bip39SeedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||||
} catch (_) {}
|
} catch (e) {
|
||||||
|
print("bip39 seed error: $e");
|
||||||
|
}
|
||||||
|
|
||||||
if (bip39SeedBytes != null) {
|
if (bip39SeedBytes != null) {
|
||||||
for (final addressType in BITCOIN_ADDRESS_TYPES) {
|
for (final addressType in BITCOIN_ADDRESS_TYPES) {
|
||||||
|
|
|
@ -161,16 +161,21 @@ class AddressCell extends StatelessWidget {
|
||||||
if (derivationPath.isNotEmpty)
|
if (derivationPath.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Flexible(
|
child: Row(
|
||||||
child: AutoSizeText(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
derivationPath,
|
children: [
|
||||||
maxLines: 1,
|
Flexible(
|
||||||
overflow: TextOverflow.ellipsis,
|
child: AutoSizeText(
|
||||||
style: TextStyle(
|
derivationPath,
|
||||||
fontSize: isChange ? 10 : 14,
|
maxLines: 1,
|
||||||
color: textColor,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isChange ? 10 : 14,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasBalance || hasReceived)
|
if (hasBalance || hasReceived)
|
||||||
|
|
|
@ -167,6 +167,10 @@ class _AddressListState extends State<AddressList> {
|
||||||
: backgroundColor,
|
: backgroundColor,
|
||||||
textColor: textColor,
|
textColor: textColor,
|
||||||
onTap: (_) {
|
onTap: (_) {
|
||||||
|
if (item.isChange || item.isHidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (widget.onSelect != null) {
|
if (widget.onSelect != null) {
|
||||||
widget.onSelect!(item.address);
|
widget.onSelect!(item.address);
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue