From 939e10248b0e2f8147c5e3e336e2da48c929d85a Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 11:04:45 -0600 Subject: [PATCH 01/12] fix: grammar --- lib/electrumx_rpc/cached_electrumx.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 935e8605e..6338b1d13 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -115,8 +115,9 @@ class CachedElectrumX { key: groupId, value: set); Logging.instance.log( - "Updated currently anonymity set for ${coin.name} with group ID $groupId", - level: LogLevel.Info); + "Updated current anonymity set for ${coin.name} with group ID $groupId", + level: LogLevel.Info, + ); } return set; From 1b92f2b36be25cbef02ff71f198d4b03aa3c503f Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 11:05:25 -0600 Subject: [PATCH 02/12] no more derivations stored for firo --- lib/services/coins/firo/firo_wallet.dart | 1006 +++++++---------- .../services/coins/firo/firo_wallet_test.dart | 214 +--- .../coins/firo/firo_wallet_test.mocks.dart | 782 +++++++++++-- test/services/coins/manager_test.mocks.dart | 32 +- .../transaction_card_test.mocks.dart | 32 +- 5 files changed, 1139 insertions(+), 927 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 57e916e2e..0d79b56d1 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:isolate'; import 'dart:math'; +import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip39; import 'package:bitcoindart/bitcoindart.dart'; import 'package:decimal/decimal.dart'; @@ -163,19 +164,8 @@ Future<void> executeNative(Map<String, dynamic> arguments) async { sendPort.send(restoreData); return; } - } else if (function == "isolateDerive") { - final mnemonic = arguments['mnemonic'] as String; - final mnemonicPassphrase = arguments['mnemonicPassphrase'] as String; - final from = arguments['from'] as int; - final to = arguments['to'] as int; - final network = arguments['network'] as NetworkType?; - if (!(network == null)) { - var derived = await isolateDerive( - mnemonic, mnemonicPassphrase, from, to, network); - sendPort.send(derived); - return; - } } + Logging.instance.log( "Error Arguments for $function not formatted correctly", level: LogLevel.Fatal); @@ -199,69 +189,6 @@ void stop(ReceivePort port) { } } -Future<Map<String, dynamic>> isolateDerive( - String mnemonic, - String mnemonicPassphrase, - int from, - int to, - NetworkType _network, -) async { - Map<String, dynamic> result = {}; - Map<String, dynamic> allReceive = {}; - Map<String, dynamic> allChange = {}; - final root = await Bip32Utils.getBip32Root( - mnemonic, - mnemonicPassphrase, - _network, - ); - - for (int i = from; i < to; i++) { - final derivePathReceiving = constructDerivePath( - networkWIF: _network.wif, - chain: 0, - index: i, - ); - var currentNode = await Bip32Utils.getBip32NodeFromRoot( - root, - derivePathReceiving, - ); - var address = P2PKH( - network: _network, data: PaymentData(pubkey: currentNode.publicKey)) - .data - .address!; - allReceive["$i"] = { - "publicKey": Format.uint8listToString(currentNode.publicKey), - "wif": currentNode.toWIF(), - "address": address, - }; - - final derivePathChange = constructDerivePath( - networkWIF: _network.wif, - chain: 1, - index: i, - ); - currentNode = await Bip32Utils.getBip32NodeFromRoot( - root, - derivePathChange, - ); - address = P2PKH( - network: _network, data: PaymentData(pubkey: currentNode.publicKey)) - .data - .address!; - allChange["$i"] = { - "publicKey": Format.uint8listToString(currentNode.publicKey), - "wif": currentNode.toWIF(), - "address": address, - }; - if (i % 50 == 0) { - Logging.instance.log("thread at $i", level: LogLevel.Info); - } - } - result['receive'] = allReceive; - result['change'] = allChange; - return result; -} - Future<Map<String, dynamic>> isolateRestore( String mnemonic, String mnemonicPassphrase, @@ -900,6 +827,7 @@ class FiroWallet extends CoinServiceAPI .or() .isLelantusEqualTo(false) .findAll(); + // _transactionData ??= _refreshTransactions(); // models.TransactionData? cachedTxData; @@ -943,10 +871,12 @@ class FiroWallet extends CoinServiceAPI /// Holds the max fee that can be sent Future<int>? _maxFee; + @override Future<int> get maxFee => _maxFee ??= _fetchMaxFee(); Future<FeeObject>? _feeObject; + @override Future<FeeObject> get fees => _feeObject ??= _getFees(); @@ -978,6 +908,7 @@ class FiroWallet extends CoinServiceAPI await _generateAddressForChain(1, 0); late String _walletName; + @override String get walletName => _walletName; @@ -987,6 +918,7 @@ class FiroWallet extends CoinServiceAPI /// unique wallet id late final String _walletId; + @override String get walletId => _walletId; @@ -1287,9 +1219,11 @@ class FiroWallet extends CoinServiceAPI } late ElectrumX _electrumXClient; + ElectrumX get electrumXClient => _electrumXClient; late CachedElectrumX _cachedElectrumXClient; + CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient; late SecureStorageInterface _secureStore; @@ -1703,66 +1637,65 @@ class FiroWallet extends CoinServiceAPI String? pubKey; String? wif; - // fetch receiving derivations if null - receiveDerivations[sd.derivePathType] ??= Map<String, dynamic>.from( - jsonDecode((await _secureStore.read( - key: "${walletId}_receiveDerivations", - )) ?? - "{}") as Map, - ); + final address = await db.getAddress(walletId, sd.utxo.address!); + if (address?.derivationPath != null) { + final node = await Bip32Utils.getBip32Node( + (await mnemonicString)!, + (await mnemonicPassphrase)!, + _network, + address!.derivationPath!.value, + ); - dynamic receiveDerivation; - for (int j = 0; - j < receiveDerivations[sd.derivePathType]!.length && - receiveDerivation == null; - j++) { - if (receiveDerivations[sd.derivePathType]!["$j"]["address"] == - sd.utxo.address!) { - receiveDerivation = receiveDerivations[sd.derivePathType]!["$j"]; - } + wif = node.toWIF(); + pubKey = Format.uint8listToString(node.publicKey); } - - if (receiveDerivation != null) { - pubKey = receiveDerivation["publicKey"] as String; - wif = receiveDerivation["wif"] as String; - } else { - // fetch change derivations if null - changeDerivations[sd.derivePathType] ??= Map<String, dynamic>.from( + if (wif == null || pubKey == null) { + // fetch receiving derivations if null + receiveDerivations[sd.derivePathType] ??= Map<String, dynamic>.from( jsonDecode((await _secureStore.read( - key: "${walletId}_changeDerivations", + key: "${walletId}_receiveDerivations", )) ?? "{}") as Map, ); - dynamic changeDerivation; + dynamic receiveDerivation; for (int j = 0; - j < changeDerivations[sd.derivePathType]!.length && - changeDerivation == null; + j < receiveDerivations[sd.derivePathType]!.length && + receiveDerivation == null; j++) { - if (changeDerivations[sd.derivePathType]!["$j"]["address"] == + if (receiveDerivations[sd.derivePathType]!["$j"]["address"] == sd.utxo.address!) { - changeDerivation = changeDerivations[sd.derivePathType]!["$j"]; + receiveDerivation = receiveDerivations[sd.derivePathType]!["$j"]; } } - if (changeDerivation != null) { - pubKey = changeDerivation["publicKey"] as String; - wif = changeDerivation["wif"] as String; - } - } - - if (wif == null || pubKey == null) { - final address = await db.getAddress(walletId, sd.utxo.address!); - if (address?.derivationPath != null) { - final node = await Bip32Utils.getBip32Node( - (await mnemonicString)!, - (await mnemonicPassphrase)!, - _network, - address!.derivationPath!.value, + if (receiveDerivation != null) { + pubKey = receiveDerivation["publicKey"] as String; + wif = receiveDerivation["wif"] as String; + } else { + // fetch change derivations if null + changeDerivations[sd.derivePathType] ??= Map<String, dynamic>.from( + jsonDecode((await _secureStore.read( + key: "${walletId}_changeDerivations", + )) ?? + "{}") as Map, ); - wif = node.toWIF(); - pubKey = Format.uint8listToString(node.publicKey); + dynamic changeDerivation; + for (int j = 0; + j < changeDerivations[sd.derivePathType]!.length && + changeDerivation == null; + j++) { + if (changeDerivations[sd.derivePathType]!["$j"]["address"] == + sd.utxo.address!) { + changeDerivation = changeDerivations[sd.derivePathType]!["$j"]; + } + } + + if (changeDerivation != null) { + pubKey = changeDerivation["publicKey"] as String; + wif = changeDerivation["wif"] as String; + } } } @@ -1793,6 +1726,8 @@ class FiroWallet extends CoinServiceAPI sd.redeemScript = redeemScript; sd.output = data.output; sd.keyPair = keyPair; + } else { + throw Exception("key or wif not found for ${sd.utxo}"); } } @@ -2210,6 +2145,7 @@ class FiroWallet extends CoinServiceAPI } bool refreshMutex = false; + @override bool get isRefreshing => refreshMutex; @@ -2236,28 +2172,6 @@ class FiroWallet extends CoinServiceAPI GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); - final receiveDerivationsString = - await _secureStore.read(key: "${walletId}_receiveDerivations"); - if (receiveDerivationsString == null || - receiveDerivationsString == "{}") { - GlobalEventBus.instance - .fire(RefreshPercentChangedEvent(0.05, walletId)); - final mnemonic = await mnemonicString; - final mnemonicPassphrase = - await _secureStore.read(key: '${_walletId}_mnemonicPassphrase'); - if (mnemonicPassphrase == null) { - Logging.instance.log( - "Exception in _generateAddressForChain: mnemonic passphrase null, possible migration issue; if using internal builds, delete wallet and restore from seed, if using a release build, please file bug report", - level: LogLevel.Error); - } - - await fillAddresses( - mnemonic!, - mnemonicPassphrase!, - numberOfThreads: Platform.numberOfProcessors - isolates.length - 1, - ); - } - await checkReceivingAddressForTransactions(); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.1, walletId)); @@ -2721,12 +2635,13 @@ class FiroWallet extends CoinServiceAPI /// Builds and signs a transaction Future<Map<String, dynamic>> buildMintTransaction( - List<isar_models.UTXO> utxosToUse, - int satoshisPerRecipient, - List<Map<String, dynamic>> mintsMap) async { + List<isar_models.UTXO> utxosToUse, + int satoshisPerRecipient, + List<Map<String, dynamic>> mintsMap, + ) async { //todo: check if print needed // debugPrint(utxosToUse.toString()); - List<String> addressesToDerive = []; + List<String> addressStringsToGet = []; // Populating the addresses to derive for (var i = 0; i < utxosToUse.length; i++) { @@ -2745,64 +2660,95 @@ class FiroWallet extends CoinServiceAPI final address = vouts[outputIndex]["scriptPubKey"]["addresses"][0] as String?; if (address != null) { - addressesToDerive.add(address); + addressStringsToGet.add(address); } } } - List<ECPair> elipticCurvePairArray = []; + final List<isar_models.Address> addresses = []; + for (final addressString in addressStringsToGet) { + final address = await db.getAddress(walletId, addressString); + if (address == null) { + Logging.instance.log( + "Failed to fetch the corresponding address object for $addressString", + level: LogLevel.Fatal, + ); + } else { + addresses.add(address); + } + } + + List<ECPair> ellipticCurvePairArray = []; List<Uint8List> outputDataArray = []; - final receiveDerivationsString = - await _secureStore.read(key: "${walletId}_receiveDerivations"); - final changeDerivationsString = - await _secureStore.read(key: "${walletId}_changeDerivations"); + Map<String, dynamic>? receiveDerivations; + Map<String, dynamic>? changeDerivations; - final receiveDerivations = Map<String, dynamic>.from( - jsonDecode(receiveDerivationsString ?? "{}") as Map); - final changeDerivations = Map<String, dynamic>.from( - jsonDecode(changeDerivationsString ?? "{}") as Map); + for (final addressString in addressStringsToGet) { + String? pubKey; + String? wif; - for (var i = 0; i < addressesToDerive.length; i++) { - final addressToCheckFor = addressesToDerive[i]; + final address = await db.getAddress(walletId, addressString); - for (var i = 0; i < receiveDerivations.length; i++) { - final receive = receiveDerivations["$i"]; - final change = changeDerivations["$i"]; + if (address?.derivationPath != null) { + final node = await Bip32Utils.getBip32Node( + (await mnemonicString)!, + (await mnemonicPassphrase)!, + _network, + address!.derivationPath!.value, + ); + wif = node.toWIF(); + pubKey = Format.uint8listToString(node.publicKey); + } - if (receive['address'] == addressToCheckFor) { - Logging.instance - .log('Receiving found on loop $i', level: LogLevel.Info); - // Logging.instance.log( - // 'decoded receive[\'wif\'] version: ${wif.decode(receive['wif'] as String)}, _network: $_network'); - elipticCurvePairArray - .add(ECPair.fromWIF(receive['wif'] as String, network: _network)); - outputDataArray.add(P2PKH( - network: _network, - data: PaymentData( - pubkey: Format.stringToUint8List( - receive['publicKey'] as String))) - .data - .output!); - break; + if (wif == null || pubKey == null) { + receiveDerivations ??= Map<String, dynamic>.from( + jsonDecode((await _secureStore.read( + key: "${walletId}_receiveDerivations")) ?? + "{}") as Map, + ); + for (var i = 0; i < receiveDerivations.length; i++) { + final receive = receiveDerivations["$i"]; + if (receive['address'] == addressString) { + wif = receive['wif'] as String; + pubKey = receive['publicKey'] as String; + break; + } } - if (change['address'] == addressToCheckFor) { - Logging.instance.log('Change found on loop $i', level: LogLevel.Info); - // Logging.instance.log( - // 'decoded change[\'wif\'] version: ${wif.decode(change['wif'] as String)}, _network: $_network'); - elipticCurvePairArray - .add(ECPair.fromWIF(change['wif'] as String, network: _network)); - outputDataArray.add(P2PKH( - network: _network, - data: PaymentData( - pubkey: Format.stringToUint8List( - change['publicKey'] as String))) - .data - .output!); - break; + if (wif == null || pubKey == null) { + changeDerivations ??= Map<String, dynamic>.from( + jsonDecode((await _secureStore.read( + key: "${walletId}_changeDerivations")) ?? + "{}") as Map, + ); + + for (var i = 0; i < changeDerivations.length; i++) { + final change = changeDerivations["$i"]; + if (change['address'] == addressString) { + wif = change['wif'] as String; + pubKey = change['publicKey'] as String; + + break; + } + } } } + + ellipticCurvePairArray.add( + ECPair.fromWIF( + wif!, + network: _network, + ), + ); + outputDataArray.add(P2PKH( + network: _network, + data: PaymentData( + pubkey: Format.stringToUint8List( + pubKey!, + ), + ), + ).data.output!); } final txb = TransactionBuilder(network: _network); @@ -2831,7 +2777,7 @@ class FiroWallet extends CoinServiceAPI for (var i = 0; i < utxosToUse.length; i++) { txb.sign( vin: i, - keyPair: elipticCurvePairArray[i], + keyPair: ellipticCurvePairArray[i], witnessValue: utxosToUse[i].value, ); } @@ -3859,72 +3805,6 @@ class FiroWallet extends CoinServiceAPI return address!.value; } - Future<void> fillAddresses( - String suppliedMnemonic, - String mnemonicPassphrase, { - int perBatch = 50, - int numberOfThreads = 4, - }) async { - if (numberOfThreads <= 0) { - numberOfThreads = 1; - } - if (Platform.environment["FLUTTER_TEST"] == "true" || integrationTestFlag) { - perBatch = 10; - numberOfThreads = 4; - } - - final receiveDerivationsString = - await _secureStore.read(key: "${walletId}_receiveDerivations"); - final changeDerivationsString = - await _secureStore.read(key: "${walletId}_changeDerivations"); - - var receiveDerivations = Map<String, dynamic>.from( - jsonDecode(receiveDerivationsString ?? "{}") as Map); - var changeDerivations = Map<String, dynamic>.from( - jsonDecode(changeDerivationsString ?? "{}") as Map); - - final int start = receiveDerivations.length; - - List<ReceivePort> ports = List.empty(growable: true); - for (int i = 0; i < numberOfThreads; i++) { - ReceivePort receivePort = await getIsolate({ - "function": "isolateDerive", - "mnemonic": suppliedMnemonic, - "mnemonicPassphrase": mnemonicPassphrase, - "from": start + i * perBatch, - "to": start + (i + 1) * perBatch, - "network": _network, - }); - ports.add(receivePort); - } - for (int i = 0; i < numberOfThreads; i++) { - ReceivePort receivePort = ports.elementAt(i); - var message = await receivePort.first; - if (message is String) { - Logging.instance.log("this is a string", level: LogLevel.Error); - stop(receivePort); - throw Exception("isolateDerive isolate failed"); - } - stop(receivePort); - Logging.instance.log('Closing isolateDerive!', level: LogLevel.Info); - receiveDerivations.addAll(message['receive'] as Map<String, dynamic>); - changeDerivations.addAll(message['change'] as Map<String, dynamic>); - } - Logging.instance.log("isolate derives", level: LogLevel.Info); - // Logging.instance.log(receiveDerivations); - // Logging.instance.log(changeDerivations); - - final newReceiveDerivationsString = jsonEncode(receiveDerivations); - final newChangeDerivationsString = jsonEncode(changeDerivations); - - await _secureStore.write( - key: "${walletId}_receiveDerivations", - value: newReceiveDerivationsString); - await _secureStore.write( - key: "${walletId}_changeDerivations", - value: newChangeDerivationsString); - } - /// Generates a new internal or external chain address for the wallet using a BIP84 derivation path. /// [chain] - Use 0 for receiving (external), 1 for change (internal). Should not be any other value! /// [index] - This can be any integer >= 0 @@ -3934,75 +3814,44 @@ class FiroWallet extends CoinServiceAPI final _mnemonicPassphrase = await mnemonicPassphrase; if (_mnemonicPassphrase == null) { Logging.instance.log( - "Exception in _generateAddressForChain: mnemonic passphrase null, possible migration issue; if using internal builds, delete wallet and restore from seed, if using a release build, please file bug report", + "Exception in _generateAddressForChain: mnemonic passphrase null," + " possible migration issue; if using internal builds, delete " + "wallet and restore from seed, if using a release build, " + "please file bug report", level: LogLevel.Error); } - Map<String, dynamic>? derivations; - if (chain == 0) { - final receiveDerivationsString = - await _secureStore.read(key: "${walletId}_receiveDerivations"); - derivations = Map<String, dynamic>.from( - jsonDecode(receiveDerivationsString ?? "{}") as Map); - } else if (chain == 1) { - final changeDerivationsString = - await _secureStore.read(key: "${walletId}_changeDerivations"); - derivations = Map<String, dynamic>.from( - jsonDecode(changeDerivationsString ?? "{}") as Map); - } final derivePath = constructDerivePath( networkWIF: _network.wif, chain: chain, index: index, ); - if (derivations!.isNotEmpty) { - if (derivations["$index"] == null) { - await fillAddresses( - _mnemonic!, - _mnemonicPassphrase!, - numberOfThreads: Platform.numberOfProcessors - isolates.length - 1, - ); - Logging.instance.log("calling _generateAddressForChain recursively", - level: LogLevel.Info); - return _generateAddressForChain(chain, index); - } - return isar_models.Address( - walletId: walletId, - value: derivations["$index"]['address'] as String, - publicKey: Format.stringToUint8List( - derivations["$index"]['publicKey'] as String), - type: isar_models.AddressType.p2pkh, - derivationIndex: index, - derivationPath: isar_models.DerivationPath()..value = derivePath, - subType: chain == 0 - ? isar_models.AddressSubType.receiving - : isar_models.AddressSubType.change, - ); - } else { - final node = await Bip32Utils.getBip32Node( - _mnemonic!, - _mnemonicPassphrase!, - _network, - derivePath, - ); - final address = - P2PKH(network: _network, data: PaymentData(pubkey: node.publicKey)) - .data - .address!; + final node = await Bip32Utils.getBip32Node( + _mnemonic!, + _mnemonicPassphrase!, + _network, + derivePath, + ); - return isar_models.Address( - walletId: walletId, - value: address, - publicKey: node.publicKey, - type: isar_models.AddressType.p2pkh, - derivationIndex: index, - derivationPath: isar_models.DerivationPath()..value = derivePath, - subType: chain == 0 - ? isar_models.AddressSubType.receiving - : isar_models.AddressSubType.change, - ); - } + final address = P2PKH( + network: _network, + data: PaymentData( + pubkey: node.publicKey, + ), + ).data.address!; + + return isar_models.Address( + walletId: walletId, + value: address, + publicKey: node.publicKey, + type: isar_models.AddressType.p2pkh, + derivationIndex: index, + derivationPath: isar_models.DerivationPath()..value = derivePath, + subType: chain == 0 + ? isar_models.AddressSubType.receiving + : isar_models.AddressSubType.change, + ); } // /// Takes in a list of isar_models.UTXOs and adds a name (dependent on object index within list) @@ -4087,6 +3936,8 @@ class FiroWallet extends CoinServiceAPI _mnemonic!, _mnemonicPassphrase!, maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + true, ); longMutex = false; @@ -4124,151 +3975,6 @@ class FiroWallet extends CoinServiceAPI await _secureStore.delete(key: "${walletId}_changeDerivations"); } - // Future<void> _rescanBackup() async { - // Logging.instance.log("starting rescan backup", level: LogLevel.Info); - // - // // backup current and clear data - // final tempReceivingAddresses = - // DB.instance.get<dynamic>(boxName: walletId, key: 'receivingAddresses'); - // await DB.instance.delete<dynamic>( - // key: 'receivingAddresses', - // boxName: walletId, - // ); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'receivingAddresses_BACKUP', - // value: tempReceivingAddresses); - // - // final tempChangeAddresses = - // DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddresses'); - // await DB.instance.delete<dynamic>( - // key: 'changeAddresses', - // boxName: walletId, - // ); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'changeAddresses_BACKUP', - // value: tempChangeAddresses); - // - // final tempReceivingIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'receivingIndex'); - // await DB.instance.delete<dynamic>( - // key: 'receivingIndex', - // boxName: walletId, - // ); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'receivingIndex_BACKUP', - // value: tempReceivingIndex); - // - // final tempChangeIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'changeIndex'); - // await DB.instance.delete<dynamic>( - // key: 'changeIndex', - // boxName: walletId, - // ); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'changeIndex_BACKUP', value: tempChangeIndex); - // - // final receiveDerivationsString = - // await _secureStore.read(key: "${walletId}_receiveDerivations"); - // final changeDerivationsString = - // await _secureStore.read(key: "${walletId}_changeDerivations"); - // - // await _secureStore.write( - // key: "${walletId}_receiveDerivations_BACKUP", - // value: receiveDerivationsString); - // await _secureStore.write( - // key: "${walletId}_changeDerivations_BACKUP", - // value: changeDerivationsString); - // - // await _secureStore.write( - // key: "${walletId}_receiveDerivations", value: null); - // await _secureStore.write(key: "${walletId}_changeDerivations", value: null); - // - // // back up but no need to delete - // final tempMintIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'mintIndex'); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'mintIndex_BACKUP', value: tempMintIndex); - // - // final tempLelantusCoins = - // DB.instance.get<dynamic>(boxName: walletId, key: '_lelantus_coins'); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: '_lelantus_coins_BACKUP', - // value: tempLelantusCoins); - // - // final tempJIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'jindex'); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'jindex_BACKUP', value: tempJIndex); - // - // final tempLelantusTxModel = DB.instance - // .get<dynamic>(boxName: walletId, key: 'latest_lelantus_tx_model'); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'latest_lelantus_tx_model_BACKUP', - // value: tempLelantusTxModel); - // - // Logging.instance.log("rescan backup complete", level: LogLevel.Info); - // } - // - // Future<void> _rescanRestore() async { - // Logging.instance.log("starting rescan restore", level: LogLevel.Info); - // - // // restore from backup - // final tempReceivingAddresses = DB.instance - // .get<dynamic>(boxName: walletId, key: 'receivingAddresses_BACKUP'); - // final tempChangeAddresses = DB.instance - // .get<dynamic>(boxName: walletId, key: 'changeAddresses_BACKUP'); - // final tempReceivingIndex = DB.instance - // .get<dynamic>(boxName: walletId, key: 'receivingIndex_BACKUP'); - // final tempChangeIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'changeIndex_BACKUP'); - // final tempMintIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'mintIndex_BACKUP'); - // final tempLelantusCoins = DB.instance - // .get<dynamic>(boxName: walletId, key: '_lelantus_coins_BACKUP'); - // final tempJIndex = - // DB.instance.get<dynamic>(boxName: walletId, key: 'jindex_BACKUP'); - // final tempLelantusTxModel = DB.instance.get<dynamic>( - // boxName: walletId, key: 'latest_lelantus_tx_model_BACKUP'); - // - // final receiveDerivationsString = - // await _secureStore.read(key: "${walletId}_receiveDerivations_BACKUP"); - // final changeDerivationsString = - // await _secureStore.read(key: "${walletId}_changeDerivations_BACKUP"); - // - // await _secureStore.write( - // key: "${walletId}_receiveDerivations", value: receiveDerivationsString); - // await _secureStore.write( - // key: "${walletId}_changeDerivations", value: changeDerivationsString); - // - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'receivingAddresses', - // value: tempReceivingAddresses); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'changeAddresses', value: tempChangeAddresses); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'receivingIndex', value: tempReceivingIndex); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'changeIndex', value: tempChangeIndex); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: 'mintIndex', value: tempMintIndex); - // await DB.instance.put<dynamic>( - // boxName: walletId, key: '_lelantus_coins', value: tempLelantusCoins); - // await DB.instance - // .put<dynamic>(boxName: walletId, key: 'jindex', value: tempJIndex); - // await DB.instance.put<dynamic>( - // boxName: walletId, - // key: 'latest_lelantus_tx_model', - // value: tempLelantusTxModel); - // - // Logging.instance.log("rescan restore complete", level: LogLevel.Info); - // } - /// wrapper for _recoverWalletFromBIP32SeedPhrase() @override Future<void> recoverFromMnemonic({ @@ -4329,6 +4035,8 @@ class FiroWallet extends CoinServiceAPI mnemonic.trim(), mnemonicPassphrase ?? "", maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + false, ); await compute( @@ -4363,142 +4071,270 @@ class FiroWallet extends CoinServiceAPI return setDataMap; } - Future<void> _makeDerivations( + Future<Map<String, int>> _getBatchTxCount({ + required Map<String, String> addresses, + }) async { + try { + final Map<String, List<dynamic>> args = {}; + for (final entry in addresses.entries) { + args[entry.key] = [ + AddressUtils.convertToScriptHash(entry.value, _network) + ]; + } + final response = await electrumXClient.getBatchHistory(args: args); + + final Map<String, int> result = {}; + for (final entry in response.entries) { + result[entry.key] = entry.value.length; + } + return result; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown in _getBatchTxCount(address: $addresses: $e\n$s", + level: LogLevel.Error); + rethrow; + } + } + + Future<Tuple2<List<isar_models.Address>, int>> _checkGaps( + int maxNumberOfIndexesToCheck, + int maxUnusedAddressGap, + int txCountBatchSize, + bip32.BIP32 root, + int chain, + ) async { + List<isar_models.Address> addressArray = []; + int gapCounter = 0; + int highestIndexWithHistory = 0; + + for (int index = 0; + index < maxNumberOfIndexesToCheck && gapCounter < maxUnusedAddressGap; + index += txCountBatchSize) { + List<String> iterationsAddressArray = []; + Logging.instance.log( + "index: $index, \t GapCounter $chain: $gapCounter", + level: LogLevel.Info, + ); + + final _id = "k_$index"; + Map<String, String> txCountCallArgs = {}; + + for (int j = 0; j < txCountBatchSize; j++) { + final derivePath = constructDerivePath( + networkWIF: root.network.wif, + chain: chain, + index: index + j, + ); + final node = await Bip32Utils.getBip32NodeFromRoot(root, derivePath); + + final data = PaymentData(pubkey: node.publicKey); + final String addressString = P2PKH( + data: data, + network: _network, + ).data.address!; + const isar_models.AddressType addrType = isar_models.AddressType.p2pkh; + + final address = isar_models.Address( + walletId: walletId, + value: addressString, + publicKey: node.publicKey, + type: addrType, + derivationIndex: index + j, + derivationPath: isar_models.DerivationPath()..value = derivePath, + subType: chain == 0 + ? isar_models.AddressSubType.receiving + : isar_models.AddressSubType.change, + ); + + addressArray.add(address); + + txCountCallArgs.addAll({ + "${_id}_$j": addressString, + }); + } + + // get address tx counts + final counts = await _getBatchTxCount(addresses: txCountCallArgs); + + // check and add appropriate addresses + for (int k = 0; k < txCountBatchSize; k++) { + int count = counts["${_id}_$k"]!; + if (count > 0) { + iterationsAddressArray.add(txCountCallArgs["${_id}_$k"]!); + + // update highest + highestIndexWithHistory = index + k; + + // reset counter + gapCounter = 0; + } + + // increase counter when no tx history found + if (count == 0) { + gapCounter++; + } + } + // cache all the transactions while waiting for the current function to finish. + unawaited(getTransactionCacheEarly(iterationsAddressArray)); + } + return Tuple2(addressArray, highestIndexWithHistory); + } + + Future<void> getTransactionCacheEarly(List<String> allAddresses) async { + try { + final List<Map<String, dynamic>> allTxHashes = + await _fetchHistory(allAddresses); + for (final txHash in allTxHashes) { + try { + unawaited(cachedElectrumXClient.getTransaction( + txHash: txHash["tx_hash"] as String, + verbose: true, + coin: coin, + )); + } catch (e) { + continue; + } + } + } catch (e) { + // + } + } + + Future<void> _recoverHistory( String suppliedMnemonic, String mnemonicPassphrase, int maxUnusedAddressGap, + int maxNumberOfIndexesToCheck, + bool isRescan, ) async { - List<isar_models.Address> receivingAddressArray = []; - List<isar_models.Address> changeAddressArray = []; + final root = await Bip32Utils.getBip32Root( + suppliedMnemonic, + mnemonicPassphrase, + _network, + ); - int receivingIndex = -1; - int changeIndex = -1; + final List<Future<Tuple2<List<isar_models.Address>, int>>> receiveFutures = + []; + final List<Future<Tuple2<List<isar_models.Address>, int>>> changeFutures = + []; - // The gap limit will be capped at 20 - int receivingGapCounter = 0; - int changeGapCounter = 0; + const receiveChain = 0; + const changeChain = 1; + const indexZero = 0; - await fillAddresses(suppliedMnemonic, mnemonicPassphrase, - numberOfThreads: Platform.numberOfProcessors - isolates.length - 1); + // actual size is 36 due to p2pkh, p2sh, and p2wpkh so 12x3 + const txCountBatchSize = 12; - final receiveDerivationsString = - await _secureStore.read(key: "${walletId}_receiveDerivations"); - final changeDerivationsString = - await _secureStore.read(key: "${walletId}_changeDerivations"); + try { + // receiving addresses + Logging.instance.log( + "checking receiving addresses...", + level: LogLevel.Info, + ); - final receiveDerivations = Map<String, dynamic>.from( - jsonDecode(receiveDerivationsString ?? "{}") as Map); - final changeDerivations = Map<String, dynamic>.from( - jsonDecode(changeDerivationsString ?? "{}") as Map); + receiveFutures.add( + _checkGaps( + maxNumberOfIndexesToCheck, + maxUnusedAddressGap, + txCountBatchSize, + root, + receiveChain, + ), + ); - // log("rcv: $receiveDerivations"); - // log("chg: $changeDerivations"); + // change addresses + Logging.instance.log( + "checking change addresses...", + level: LogLevel.Info, + ); + changeFutures.add( + _checkGaps( + maxNumberOfIndexesToCheck, + maxUnusedAddressGap, + txCountBatchSize, + root, + changeChain, + ), + ); - // Deriving and checking for receiving addresses - for (var i = 0; i < receiveDerivations.length; i++) { - // Break out of loop when receivingGapCounter hits maxUnusedAddressGap - // Same gap limit for change as for receiving, breaks when it hits maxUnusedAddressGap - if (receivingGapCounter >= maxUnusedAddressGap && - changeGapCounter >= maxUnusedAddressGap) { - break; - } + // io limitations may require running these linearly instead + final futuresResult = await Future.wait([ + Future.wait(receiveFutures), + Future.wait(changeFutures), + ]); - final receiveDerivation = receiveDerivations["$i"]; - final address = receiveDerivation['address'] as String; + final receiveResults = futuresResult[0]; + final changeResults = futuresResult[1]; - final changeDerivation = changeDerivations["$i"]; - final _address = changeDerivation['address'] as String; - Future<int>? futureNumTxs; - Future<int>? _futureNumTxs; - if (receivingGapCounter < maxUnusedAddressGap) { - futureNumTxs = _getReceivedTxCount(address: address); - } - if (changeGapCounter < maxUnusedAddressGap) { - _futureNumTxs = _getReceivedTxCount(address: _address); - } - try { - if (futureNumTxs != null) { - int numTxs = await futureNumTxs; - if (numTxs >= 1) { - receivingIndex = i; - final derivePath = constructDerivePath( - networkWIF: _network.wif, - chain: 0, - index: receivingIndex, - ); - final addr = isar_models.Address( - walletId: walletId, - value: address, - publicKey: Format.stringToUint8List( - receiveDerivation['publicKey'] as String), - type: isar_models.AddressType.p2pkh, - derivationIndex: i, - derivationPath: isar_models.DerivationPath()..value = derivePath, - subType: isar_models.AddressSubType.receiving, - ); - receivingAddressArray.add(addr); - } else if (numTxs == 0) { - receivingGapCounter += 1; - } + final List<isar_models.Address> addressesToStore = []; + + int highestReceivingIndexWithHistory = 0; + // If restoring a wallet that never received any funds, then set receivingArray manually + // If we didn't do this, it'd store an empty array + for (final tuple in receiveResults) { + if (tuple.item1.isEmpty) { + final address = await _generateAddressForChain( + receiveChain, + indexZero, + ); + addressesToStore.add(address); + } else { + highestReceivingIndexWithHistory = + max(tuple.item2, highestReceivingIndexWithHistory); + addressesToStore.addAll(tuple.item1); } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s", - level: LogLevel.Error); - rethrow; } - try { - if (_futureNumTxs != null) { - int numTxs = await _futureNumTxs; - if (numTxs >= 1) { - changeIndex = i; - final derivePath = constructDerivePath( - networkWIF: _network.wif, - chain: 1, - index: changeIndex, - ); - final addr = isar_models.Address( - walletId: walletId, - value: _address, - publicKey: Format.stringToUint8List( - changeDerivation['publicKey'] as String), - type: isar_models.AddressType.p2pkh, - derivationIndex: i, - derivationPath: isar_models.DerivationPath()..value = derivePath, - subType: isar_models.AddressSubType.change, - ); - changeAddressArray.add(addr); - } else if (numTxs == 0) { - changeGapCounter += 1; - } + int highestChangeIndexWithHistory = 0; + // If restoring a wallet that never sent any funds with change, then set changeArray + // manually. If we didn't do this, it'd store an empty array. + for (final tuple in changeResults) { + if (tuple.item1.isEmpty) { + final address = await _generateAddressForChain( + changeChain, + indexZero, + ); + addressesToStore.add(address); + } else { + highestChangeIndexWithHistory = + max(tuple.item2, highestChangeIndexWithHistory); + addressesToStore.addAll(tuple.item1); } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s", - level: LogLevel.Error); - rethrow; } - } - // If restoring a wallet that never received any funds, then set receivingArray manually - // If we didn't do this, it'd store an empty array - if (receivingIndex == -1) { - final receivingAddress = await _generateAddressForChain(0, 0); - receivingAddressArray.add(receivingAddress); - } + // remove extra addresses to help minimize risk of creating a large gap + addressesToStore.removeWhere((e) => + e.subType == isar_models.AddressSubType.change && + e.derivationIndex > highestChangeIndexWithHistory); + addressesToStore.removeWhere((e) => + e.subType == isar_models.AddressSubType.receiving && + e.derivationIndex > highestReceivingIndexWithHistory); - // If restoring a wallet that never sent any funds with change, then set changeArray - // manually. If we didn't do this, it'd store an empty array. - if (changeIndex == -1) { - final changeAddress = await _generateAddressForChain(1, 0); - changeAddressArray.add(changeAddress); - } + if (isRescan) { + await db.updateOrPutAddresses(addressesToStore); + } else { + await db.putAddresses(addressesToStore); + } - await db.updateOrPutAddresses([ - ...receivingAddressArray, - ...changeAddressArray, - ]); + await Future.wait([ + _refreshTransactions(), + _refreshUTXOs(), + ]); + + await Future.wait([ + updateCachedId(walletId), + updateCachedIsFavorite(false), + ]); + + longMutex = false; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from _recoverWalletFromBIP32SeedPhrase(): $e\n$s", + level: LogLevel.Error); + + longMutex = false; + rethrow; + } } /// Recovers wallet from [suppliedMnemonic]. Expects a valid mnemonic. @@ -4506,6 +4342,8 @@ class FiroWallet extends CoinServiceAPI String suppliedMnemonic, String mnemonicPassphrase, int maxUnusedAddressGap, + int maxNumberOfIndexesToCheck, + bool isRescan, ) async { longMutex = true; Logging.instance @@ -4513,16 +4351,26 @@ class FiroWallet extends CoinServiceAPI try { final latestSetId = await getLatestSetId(); final setDataMap = getSetDataMap(latestSetId); + final usedSerialNumbers = getUsedCoinSerials(); - final makeDerivations = _makeDerivations( - suppliedMnemonic, mnemonicPassphrase, maxUnusedAddressGap); + final generateAndCheckAddresses = _recoverHistory( + suppliedMnemonic, + mnemonicPassphrase, + maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + isRescan, + ); await Future.wait([ updateCachedId(walletId), updateCachedIsFavorite(false), ]); - await Future.wait([usedSerialNumbers, setDataMap, makeDerivations]); + await Future.wait([ + usedSerialNumbers, + setDataMap, + generateAndCheckAddresses, + ]); await _restore(latestSetId, await setDataMap, await usedSerialNumbers); longMutex = false; diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index d7b23e51e..a3f0eca1a 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -8,6 +8,7 @@ import 'package:hive/hive.dart'; import 'package:hive_test/hive_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; @@ -17,7 +18,6 @@ import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; -import 'package:stackwallet/utilities/address_utils.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; @@ -34,16 +34,10 @@ import 'sample_data/transaction_data_samples.dart'; ElectrumX, CachedElectrumX, TransactionNotificationTracker, + MainDB, ]) void main() { group("isolate functions", () { - test("isolateDerive", () async { - final result = await isolateDerive( - IsolateDeriveParams.mnemonic, "", 0, 2, firoNetwork); - expect(result, isA<Map<String, dynamic>>()); - expect(result.toString(), IsolateDeriveParams.expected); - }); - test("isolateRestore success", () async { final cachedClient = MockCachedElectrumX(); final txDataOLD = old.TransactionData.fromJson(dateTimeChunksJson); @@ -1169,42 +1163,6 @@ void main() { "b36161c6e619395b3d40a851c45c1fef7a5c541eed911b5524a66c5703a689c9"); }); - test("fillAddresses", () async { - final client = MockElectrumX(); - final cachedClient = MockCachedElectrumX(); - final secureStore = FakeSecureStorage(); - - final firo = FiroWallet( - walletName: testWalletName, - walletId: "${testWalletId}fillAddresses", - coin: Coin.firo, - client: client, - cachedClient: cachedClient, - secureStore: secureStore, - tracker: MockTransactionNotificationTracker(), - ); - - await firo.fillAddresses( - FillAddressesParams.mnemonic, - "", - ); - - final rcv = await secureStore.read( - key: "${testWalletId}fillAddresses_receiveDerivations"); - final chg = await secureStore.read( - key: "${testWalletId}fillAddresses_changeDerivations"); - final receiveDerivations = - Map<String, dynamic>.from(jsonDecode(rcv as String) as Map); - final changeDerivations = - Map<String, dynamic>.from(jsonDecode(chg as String) as Map); - - expect(receiveDerivations.toString(), - FillAddressesParams.expectedReceiveDerivationsString); - - expect(changeDerivations.toString(), - FillAddressesParams.expectedChangeDerivationsString); - }); - // the above test needs to pass in order for this test to pass test("buildMintTransaction", () async { TestWidgetsFlutterBinding.ensureInitialized(); @@ -1230,6 +1188,7 @@ void main() { final client = MockElectrumX(); final cachedClient = MockCachedElectrumX(); final secureStore = FakeSecureStorage(); + final mainDB = MockMainDB(); await secureStore.write( key: "${testWalletId}buildMintTransaction_mnemonic", @@ -1246,6 +1205,9 @@ void main() { when(client.getBlockHeadTip()).thenAnswer( (_) async => {"height": 455873, "hex": "this value not used here"}); + when(mainDB.getAddress("${testWalletId}buildMintTransaction", any)) + .thenAnswer((realInvocation) async => null); + final firo = FiroWallet( walletName: testWalletName, walletId: "${testWalletId}buildMintTransaction", @@ -1254,6 +1216,7 @@ void main() { cachedClient: cachedClient, secureStore: secureStore, tracker: MockTransactionNotificationTracker(), + mockableOverride: mainDB, ); final wallet = @@ -2811,169 +2774,6 @@ void main() { // throwsA(isA<Exception>())); // }, timeout: const Timeout(Duration(minutes: 3))); - test("send fails due to bad transaction created", () async { - TestWidgetsFlutterBinding.ensureInitialized(); - const MethodChannel('uk.spiralarm.flutter/devicelocale') - .setMockMethodCallHandler((methodCall) async => 'en_US'); - - final client = MockElectrumX(); - final cachedClient = MockCachedElectrumX(); - final secureStore = FakeSecureStorage(); - - when(client.getLatestCoinId()).thenAnswer((_) async => 1); - when(client.getBlockHeadTip()).thenAnswer( - (_) async => {"height": 459185, "hex": "... some block hex ..."}); - - when(client.broadcastTransaction(rawTx: anyNamed("rawTx"))) - .thenAnswer((_) async { - return "some bad txid"; - }); - - when(client.getBatchHistory(args: batchHistoryRequest0)) - .thenAnswer((realInvocation) async => batchHistoryResponse0); - - when(cachedClient.getAnonymitySet( - groupId: "1", - coin: Coin.firo, - )).thenAnswer((_) async => GetAnonymitySetSampleData.data); - - // mock transaction calls - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash0, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData0); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash1, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData1); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash2, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData2); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash3, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData3); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash4, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData4); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash5, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData5); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash6, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData6); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash7, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData7); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash8, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData8); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash9, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData9); - when(cachedClient.getTransaction( - txHash: SampleGetTransactionData.txHash10, - coin: Coin.firo, - )).thenAnswer((_) async => SampleGetTransactionData.txData10); - - final firo = FiroWallet( - walletId: "${testWalletId}send", - coin: Coin.firo, - walletName: testWalletName, - client: client, - cachedClient: cachedClient, - secureStore: secureStore, - tracker: MockTransactionNotificationTracker(), - ); - - // set mnemonic - await secureStore.write( - key: "${testWalletId}send_mnemonic", value: TEST_MNEMONIC); - - // set timer to non null so a periodic timer isn't created - firo.timer = Timer(const Duration(), () {}); - - // build sending wallet - await firo.fillAddresses( - TEST_MNEMONIC, - "", - ); - final wallet = await Hive.openBox<dynamic>("${testWalletId}send"); - - final rcv = - await secureStore.read(key: "${testWalletId}send_receiveDerivations"); - final chg = - await secureStore.read(key: "${testWalletId}send_changeDerivations"); - final receiveDerivations = - Map<String, dynamic>.from(jsonDecode(rcv as String) as Map); - final changeDerivations = - Map<String, dynamic>.from(jsonDecode(chg as String) as Map); - - for (int i = 0; i < receiveDerivations.length; i++) { - final receiveHash = AddressUtils.convertToScriptHash( - receiveDerivations["$i"]!["address"] as String, firoNetwork); - final changeHash = AddressUtils.convertToScriptHash( - changeDerivations["$i"]!["address"] as String, firoNetwork); - List<Map<String, dynamic>> data; - switch (receiveHash) { - case SampleGetHistoryData.scripthash0: - data = SampleGetHistoryData.data0; - break; - case SampleGetHistoryData.scripthash1: - data = SampleGetHistoryData.data1; - break; - case SampleGetHistoryData.scripthash2: - data = SampleGetHistoryData.data2; - break; - case SampleGetHistoryData.scripthash3: - data = SampleGetHistoryData.data3; - break; - default: - data = []; - } - when(client.getHistory(scripthash: receiveHash)) - .thenAnswer((_) async => data); - - switch (changeHash) { - case SampleGetHistoryData.scripthash0: - data = SampleGetHistoryData.data0; - break; - case SampleGetHistoryData.scripthash1: - data = SampleGetHistoryData.data1; - break; - case SampleGetHistoryData.scripthash2: - data = SampleGetHistoryData.data2; - break; - case SampleGetHistoryData.scripthash3: - data = SampleGetHistoryData.data3; - break; - default: - data = []; - } - - when(client.getHistory(scripthash: changeHash)) - .thenAnswer((_) async => data); - } - - await wallet.put('_lelantus_coins', SampleLelantus.lelantusCoins); - await wallet.put('jindex', [2, 4, 6]); - await wallet.put('mintIndex', 8); - await wallet.put('receivingAddresses', [ - "a8VV7vMzJdTQj1eLEJNskhLEBUxfNWhpAg", - "aPjLWDTPQsoPHUTxKBNRzoebDALj3eTcfh", - "aKmXfS7nEZdqWBGRdAXcyMoEoKhZQDPBoq" - ]); - await wallet - .put('changeAddresses', ["a5V5r6We6mNZzWJwGwEeRML3mEYLjvK39w"]); - }, timeout: const Timeout(Duration(minutes: 3))); - // test("wallet balances", () async { // TestWidgetsFlutterBinding.ensureInitialized(); // const MethodChannel('uk.spiralarm.flutter/devicelocale') diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 0e3549d87..790be8e23 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -3,16 +3,22 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:decimal/decimal.dart' as _i2; +import 'package:isar/isar.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; -import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i6; -import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4; +import 'package:stackwallet/db/isar/main_db.dart' as _i10; +import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i7; +import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i5; +import 'package:stackwallet/models/isar/models/block_explorer.dart' as _i12; +import 'package:stackwallet/models/isar/models/contact_entry.dart' as _i11; +import 'package:stackwallet/models/isar/models/isar_models.dart' as _i13; import 'package:stackwallet/services/transaction_notification_tracker.dart' - as _i8; -import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i7; + as _i9; +import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i8; import 'package:stackwallet/utilities/prefs.dart' as _i3; +import 'package:tuple/tuple.dart' as _i14; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -45,16 +51,37 @@ class _FakePrefs_1 extends _i1.SmartFake implements _i3.Prefs { ); } +class _FakeIsar_2 extends _i1.SmartFake implements _i4.Isar { + _FakeIsar_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeQueryBuilder_3<OBJ, R, S> extends _i1.SmartFake + implements _i4.QueryBuilder<OBJ, R, S> { + _FakeQueryBuilder_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [ElectrumX]. /// /// See the documentation for Mockito's code generation for more information. -class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { +class MockElectrumX extends _i1.Mock implements _i5.ElectrumX { MockElectrumX() { _i1.throwOnMissingStub(this); } @override - set failovers(List<_i4.ElectrumXNode>? _failovers) => super.noSuchMethod( + set failovers(List<_i5.ElectrumXNode>? _failovers) => super.noSuchMethod( Invocation.setter( #failovers, _failovers, @@ -90,7 +117,7 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { returnValue: false, ) as bool); @override - _i5.Future<dynamic> request({ + _i6.Future<dynamic> request({ required String? command, List<dynamic>? args = const [], Duration? connectionTimeout = const Duration(seconds: 60), @@ -109,10 +136,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #retries: retries, }, ), - returnValue: _i5.Future<dynamic>.value(), - ) as _i5.Future<dynamic>); + returnValue: _i6.Future<dynamic>.value(), + ) as _i6.Future<dynamic>); @override - _i5.Future<List<Map<String, dynamic>>> batchRequest({ + _i6.Future<List<Map<String, dynamic>>> batchRequest({ required String? command, required Map<String, List<dynamic>>? args, Duration? connectionTimeout = const Duration(seconds: 60), @@ -129,11 +156,11 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #retries: retries, }, ), - returnValue: _i5.Future<List<Map<String, dynamic>>>.value( + returnValue: _i6.Future<List<Map<String, dynamic>>>.value( <Map<String, dynamic>>[]), - ) as _i5.Future<List<Map<String, dynamic>>>); + ) as _i6.Future<List<Map<String, dynamic>>>); @override - _i5.Future<bool> ping({ + _i6.Future<bool> ping({ String? requestID, int? retryCount = 1, }) => @@ -146,10 +173,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #retryCount: retryCount, }, ), - returnValue: _i5.Future<bool>.value(false), - ) as _i5.Future<bool>); + returnValue: _i6.Future<bool>.value(false), + ) as _i6.Future<bool>); @override - _i5.Future<Map<String, dynamic>> getBlockHeadTip({String? requestID}) => + _i6.Future<Map<String, dynamic>> getBlockHeadTip({String? requestID}) => (super.noSuchMethod( Invocation.method( #getBlockHeadTip, @@ -157,10 +184,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { {#requestID: requestID}, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<Map<String, dynamic>> getServerFeatures({String? requestID}) => + _i6.Future<Map<String, dynamic>> getServerFeatures({String? requestID}) => (super.noSuchMethod( Invocation.method( #getServerFeatures, @@ -168,10 +195,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { {#requestID: requestID}, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<String> broadcastTransaction({ + _i6.Future<String> broadcastTransaction({ required String? rawTx, String? requestID, }) => @@ -184,10 +211,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #requestID: requestID, }, ), - returnValue: _i5.Future<String>.value(''), - ) as _i5.Future<String>); + returnValue: _i6.Future<String>.value(''), + ) as _i6.Future<String>); @override - _i5.Future<Map<String, dynamic>> getBalance({ + _i6.Future<Map<String, dynamic>> getBalance({ required String? scripthash, String? requestID, }) => @@ -201,10 +228,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { }, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<List<Map<String, dynamic>>> getHistory({ + _i6.Future<List<Map<String, dynamic>>> getHistory({ required String? scripthash, String? requestID, }) => @@ -217,11 +244,11 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #requestID: requestID, }, ), - returnValue: _i5.Future<List<Map<String, dynamic>>>.value( + returnValue: _i6.Future<List<Map<String, dynamic>>>.value( <Map<String, dynamic>>[]), - ) as _i5.Future<List<Map<String, dynamic>>>); + ) as _i6.Future<List<Map<String, dynamic>>>); @override - _i5.Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory( + _i6.Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory( {required Map<String, List<dynamic>>? args}) => (super.noSuchMethod( Invocation.method( @@ -229,11 +256,11 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { [], {#args: args}, ), - returnValue: _i5.Future<Map<String, List<Map<String, dynamic>>>>.value( + returnValue: _i6.Future<Map<String, List<Map<String, dynamic>>>>.value( <String, List<Map<String, dynamic>>>{}), - ) as _i5.Future<Map<String, List<Map<String, dynamic>>>>); + ) as _i6.Future<Map<String, List<Map<String, dynamic>>>>); @override - _i5.Future<List<Map<String, dynamic>>> getUTXOs({ + _i6.Future<List<Map<String, dynamic>>> getUTXOs({ required String? scripthash, String? requestID, }) => @@ -246,11 +273,11 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #requestID: requestID, }, ), - returnValue: _i5.Future<List<Map<String, dynamic>>>.value( + returnValue: _i6.Future<List<Map<String, dynamic>>>.value( <Map<String, dynamic>>[]), - ) as _i5.Future<List<Map<String, dynamic>>>); + ) as _i6.Future<List<Map<String, dynamic>>>); @override - _i5.Future<Map<String, List<Map<String, dynamic>>>> getBatchUTXOs( + _i6.Future<Map<String, List<Map<String, dynamic>>>> getBatchUTXOs( {required Map<String, List<dynamic>>? args}) => (super.noSuchMethod( Invocation.method( @@ -258,11 +285,11 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { [], {#args: args}, ), - returnValue: _i5.Future<Map<String, List<Map<String, dynamic>>>>.value( + returnValue: _i6.Future<Map<String, List<Map<String, dynamic>>>>.value( <String, List<Map<String, dynamic>>>{}), - ) as _i5.Future<Map<String, List<Map<String, dynamic>>>>); + ) as _i6.Future<Map<String, List<Map<String, dynamic>>>>); @override - _i5.Future<Map<String, dynamic>> getTransaction({ + _i6.Future<Map<String, dynamic>> getTransaction({ required String? txHash, bool? verbose = true, String? requestID, @@ -278,10 +305,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { }, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<Map<String, dynamic>> getAnonymitySet({ + _i6.Future<Map<String, dynamic>> getAnonymitySet({ String? groupId = r'1', String? blockhash = r'', String? requestID, @@ -297,10 +324,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { }, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<dynamic> getMintData({ + _i6.Future<dynamic> getMintData({ dynamic mints, String? requestID, }) => @@ -313,10 +340,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #requestID: requestID, }, ), - returnValue: _i5.Future<dynamic>.value(), - ) as _i5.Future<dynamic>); + returnValue: _i6.Future<dynamic>.value(), + ) as _i6.Future<dynamic>); @override - _i5.Future<Map<String, dynamic>> getUsedCoinSerials({ + _i6.Future<Map<String, dynamic>> getUsedCoinSerials({ String? requestID, required int? startNumber, }) => @@ -330,19 +357,19 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { }, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<int> getLatestCoinId({String? requestID}) => (super.noSuchMethod( + _i6.Future<int> getLatestCoinId({String? requestID}) => (super.noSuchMethod( Invocation.method( #getLatestCoinId, [], {#requestID: requestID}, ), - returnValue: _i5.Future<int>.value(0), - ) as _i5.Future<int>); + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); @override - _i5.Future<Map<String, dynamic>> getFeeRate({String? requestID}) => + _i6.Future<Map<String, dynamic>> getFeeRate({String? requestID}) => (super.noSuchMethod( Invocation.method( #getFeeRate, @@ -350,10 +377,10 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { {#requestID: requestID}, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<_i2.Decimal> estimateFee({ + _i6.Future<_i2.Decimal> estimateFee({ String? requestID, required int? blocks, }) => @@ -366,7 +393,7 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { #blocks: blocks, }, ), - returnValue: _i5.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i6.Future<_i2.Decimal>.value(_FakeDecimal_0( this, Invocation.method( #estimateFee, @@ -377,15 +404,15 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { }, ), )), - ) as _i5.Future<_i2.Decimal>); + ) as _i6.Future<_i2.Decimal>); @override - _i5.Future<_i2.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + _i6.Future<_i2.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( Invocation.method( #relayFee, [], {#requestID: requestID}, ), - returnValue: _i5.Future<_i2.Decimal>.value(_FakeDecimal_0( + returnValue: _i6.Future<_i2.Decimal>.value(_FakeDecimal_0( this, Invocation.method( #relayFee, @@ -393,13 +420,13 @@ class MockElectrumX extends _i1.Mock implements _i4.ElectrumX { {#requestID: requestID}, ), )), - ) as _i5.Future<_i2.Decimal>); + ) as _i6.Future<_i2.Decimal>); } /// A class which mocks [CachedElectrumX]. /// /// See the documentation for Mockito's code generation for more information. -class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { +class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX { MockCachedElectrumX() { _i1.throwOnMissingStub(this); } @@ -428,15 +455,15 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { ), ) as _i3.Prefs); @override - List<_i4.ElectrumXNode> get failovers => (super.noSuchMethod( + List<_i5.ElectrumXNode> get failovers => (super.noSuchMethod( Invocation.getter(#failovers), - returnValue: <_i4.ElectrumXNode>[], - ) as List<_i4.ElectrumXNode>); + returnValue: <_i5.ElectrumXNode>[], + ) as List<_i5.ElectrumXNode>); @override - _i5.Future<Map<String, dynamic>> getAnonymitySet({ + _i6.Future<Map<String, dynamic>> getAnonymitySet({ required String? groupId, String? blockhash = r'', - required _i7.Coin? coin, + required _i8.Coin? coin, }) => (super.noSuchMethod( Invocation.method( @@ -449,8 +476,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { }, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override String base64ToHex(String? source) => (super.noSuchMethod( Invocation.method( @@ -468,9 +495,9 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { returnValue: '', ) as String); @override - _i5.Future<Map<String, dynamic>> getTransaction({ + _i6.Future<Map<String, dynamic>> getTransaction({ required String? txHash, - required _i7.Coin? coin, + required _i8.Coin? coin, bool? verbose = true, }) => (super.noSuchMethod( @@ -484,11 +511,11 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { }, ), returnValue: - _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), - ) as _i5.Future<Map<String, dynamic>>); + _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), + ) as _i6.Future<Map<String, dynamic>>); @override - _i5.Future<List<dynamic>> getUsedCoinSerials({ - required _i7.Coin? coin, + _i6.Future<List<dynamic>> getUsedCoinSerials({ + required _i8.Coin? coin, int? startNumber = 0, }) => (super.noSuchMethod( @@ -500,26 +527,26 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i5.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i5.Future<List<dynamic>>); + returnValue: _i6.Future<List<dynamic>>.value(<dynamic>[]), + ) as _i6.Future<List<dynamic>>); @override - _i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) => + _i6.Future<void> clearSharedTransactionCache({required _i8.Coin? coin}) => (super.noSuchMethod( Invocation.method( #clearSharedTransactionCache, [], {#coin: coin}, ), - returnValue: _i5.Future<void>.value(), - returnValueForMissingStub: _i5.Future<void>.value(), - ) as _i5.Future<void>); + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); } /// A class which mocks [TransactionNotificationTracker]. /// /// See the documentation for Mockito's code generation for more information. class MockTransactionNotificationTracker extends _i1.Mock - implements _i8.TransactionNotificationTracker { + implements _i9.TransactionNotificationTracker { MockTransactionNotificationTracker() { _i1.throwOnMissingStub(this); } @@ -548,14 +575,14 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future<void> addNotifiedPending(String? txid) => (super.noSuchMethod( + _i6.Future<void> addNotifiedPending(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedPending, [txid], ), - returnValue: _i5.Future<void>.value(), - returnValueForMissingStub: _i5.Future<void>.value(), - ) as _i5.Future<void>); + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); @override bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( @@ -565,21 +592,582 @@ class MockTransactionNotificationTracker extends _i1.Mock returnValue: false, ) as bool); @override - _i5.Future<void> addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + _i6.Future<void> addNotifiedConfirmed(String? txid) => (super.noSuchMethod( Invocation.method( #addNotifiedConfirmed, [txid], ), - returnValue: _i5.Future<void>.value(), - returnValueForMissingStub: _i5.Future<void>.value(), - ) as _i5.Future<void>); + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); @override - _i5.Future<void> deleteTransaction(String? txid) => (super.noSuchMethod( + _i6.Future<void> deleteTransaction(String? txid) => (super.noSuchMethod( Invocation.method( #deleteTransaction, [txid], ), - returnValue: _i5.Future<void>.value(), - returnValueForMissingStub: _i5.Future<void>.value(), - ) as _i5.Future<void>); + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); +} + +/// A class which mocks [MainDB]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMainDB extends _i1.Mock implements _i10.MainDB { + MockMainDB() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Isar get isar => (super.noSuchMethod( + Invocation.getter(#isar), + returnValue: _FakeIsar_2( + this, + Invocation.getter(#isar), + ), + ) as _i4.Isar); + @override + _i6.Future<bool> initMainDB({_i4.Isar? mock}) => (super.noSuchMethod( + Invocation.method( + #initMainDB, + [], + {#mock: mock}, + ), + returnValue: _i6.Future<bool>.value(false), + ) as _i6.Future<bool>); + @override + List<_i11.ContactEntry> getContactEntries() => (super.noSuchMethod( + Invocation.method( + #getContactEntries, + [], + ), + returnValue: <_i11.ContactEntry>[], + ) as List<_i11.ContactEntry>); + @override + _i6.Future<bool> deleteContactEntry({required String? id}) => + (super.noSuchMethod( + Invocation.method( + #deleteContactEntry, + [], + {#id: id}, + ), + returnValue: _i6.Future<bool>.value(false), + ) as _i6.Future<bool>); + @override + _i6.Future<bool> isContactEntryExists({required String? id}) => + (super.noSuchMethod( + Invocation.method( + #isContactEntryExists, + [], + {#id: id}, + ), + returnValue: _i6.Future<bool>.value(false), + ) as _i6.Future<bool>); + @override + _i11.ContactEntry? getContactEntry({required String? id}) => + (super.noSuchMethod(Invocation.method( + #getContactEntry, + [], + {#id: id}, + )) as _i11.ContactEntry?); + @override + _i6.Future<bool> putContactEntry( + {required _i11.ContactEntry? contactEntry}) => + (super.noSuchMethod( + Invocation.method( + #putContactEntry, + [], + {#contactEntry: contactEntry}, + ), + returnValue: _i6.Future<bool>.value(false), + ) as _i6.Future<bool>); + @override + _i12.TransactionBlockExplorer? getTransactionBlockExplorer( + {required _i8.Coin? coin}) => + (super.noSuchMethod(Invocation.method( + #getTransactionBlockExplorer, + [], + {#coin: coin}, + )) as _i12.TransactionBlockExplorer?); + @override + _i6.Future<int> putTransactionBlockExplorer( + _i12.TransactionBlockExplorer? explorer) => + (super.noSuchMethod( + Invocation.method( + #putTransactionBlockExplorer, + [explorer], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + _i4.QueryBuilder<_i13.Address, _i13.Address, _i4.QAfterWhereClause> + getAddresses(String? walletId) => (super.noSuchMethod( + Invocation.method( + #getAddresses, + [walletId], + ), + returnValue: _FakeQueryBuilder_3<_i13.Address, _i13.Address, + _i4.QAfterWhereClause>( + this, + Invocation.method( + #getAddresses, + [walletId], + ), + ), + ) as _i4 + .QueryBuilder<_i13.Address, _i13.Address, _i4.QAfterWhereClause>); + @override + _i6.Future<int> putAddress(_i13.Address? address) => (super.noSuchMethod( + Invocation.method( + #putAddress, + [address], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + _i6.Future<List<int>> putAddresses(List<_i13.Address>? addresses) => + (super.noSuchMethod( + Invocation.method( + #putAddresses, + [addresses], + ), + returnValue: _i6.Future<List<int>>.value(<int>[]), + ) as _i6.Future<List<int>>); + @override + _i6.Future<List<int>> updateOrPutAddresses(List<_i13.Address>? addresses) => + (super.noSuchMethod( + Invocation.method( + #updateOrPutAddresses, + [addresses], + ), + returnValue: _i6.Future<List<int>>.value(<int>[]), + ) as _i6.Future<List<int>>); + @override + _i6.Future<_i13.Address?> getAddress( + String? walletId, + String? address, + ) => + (super.noSuchMethod( + Invocation.method( + #getAddress, + [ + walletId, + address, + ], + ), + returnValue: _i6.Future<_i13.Address?>.value(), + ) as _i6.Future<_i13.Address?>); + @override + _i6.Future<int> updateAddress( + _i13.Address? oldAddress, + _i13.Address? newAddress, + ) => + (super.noSuchMethod( + Invocation.method( + #updateAddress, + [ + oldAddress, + newAddress, + ], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + _i4.QueryBuilder<_i13.Transaction, _i13.Transaction, _i4.QAfterWhereClause> + getTransactions(String? walletId) => (super.noSuchMethod( + Invocation.method( + #getTransactions, + [walletId], + ), + returnValue: _FakeQueryBuilder_3<_i13.Transaction, _i13.Transaction, + _i4.QAfterWhereClause>( + this, + Invocation.method( + #getTransactions, + [walletId], + ), + ), + ) as _i4.QueryBuilder<_i13.Transaction, _i13.Transaction, + _i4.QAfterWhereClause>); + @override + _i6.Future<int> putTransaction(_i13.Transaction? transaction) => + (super.noSuchMethod( + Invocation.method( + #putTransaction, + [transaction], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + _i6.Future<List<int>> putTransactions(List<_i13.Transaction>? transactions) => + (super.noSuchMethod( + Invocation.method( + #putTransactions, + [transactions], + ), + returnValue: _i6.Future<List<int>>.value(<int>[]), + ) as _i6.Future<List<int>>); + @override + _i6.Future<_i13.Transaction?> getTransaction( + String? walletId, + String? txid, + ) => + (super.noSuchMethod( + Invocation.method( + #getTransaction, + [ + walletId, + txid, + ], + ), + returnValue: _i6.Future<_i13.Transaction?>.value(), + ) as _i6.Future<_i13.Transaction?>); + @override + _i6.Stream<_i13.Transaction?> watchTransaction({ + required int? id, + bool? fireImmediately = false, + }) => + (super.noSuchMethod( + Invocation.method( + #watchTransaction, + [], + { + #id: id, + #fireImmediately: fireImmediately, + }, + ), + returnValue: _i6.Stream<_i13.Transaction?>.empty(), + ) as _i6.Stream<_i13.Transaction?>); + @override + _i4.QueryBuilder<_i13.UTXO, _i13.UTXO, _i4.QAfterWhereClause> getUTXOs( + String? walletId) => + (super.noSuchMethod( + Invocation.method( + #getUTXOs, + [walletId], + ), + returnValue: + _FakeQueryBuilder_3<_i13.UTXO, _i13.UTXO, _i4.QAfterWhereClause>( + this, + Invocation.method( + #getUTXOs, + [walletId], + ), + ), + ) as _i4.QueryBuilder<_i13.UTXO, _i13.UTXO, _i4.QAfterWhereClause>); + @override + _i6.Future<void> putUTXO(_i13.UTXO? utxo) => (super.noSuchMethod( + Invocation.method( + #putUTXO, + [utxo], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<void> putUTXOs(List<_i13.UTXO>? utxos) => (super.noSuchMethod( + Invocation.method( + #putUTXOs, + [utxos], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<void> updateUTXOs( + String? walletId, + List<_i13.UTXO>? utxos, + ) => + (super.noSuchMethod( + Invocation.method( + #updateUTXOs, + [ + walletId, + utxos, + ], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Stream<_i13.UTXO?> watchUTXO({ + required int? id, + bool? fireImmediately = false, + }) => + (super.noSuchMethod( + Invocation.method( + #watchUTXO, + [], + { + #id: id, + #fireImmediately: fireImmediately, + }, + ), + returnValue: _i6.Stream<_i13.UTXO?>.empty(), + ) as _i6.Stream<_i13.UTXO?>); + @override + _i4.QueryBuilder<_i13.TransactionNote, _i13.TransactionNote, + _i4.QAfterWhereClause> getTransactionNotes( + String? walletId) => + (super.noSuchMethod( + Invocation.method( + #getTransactionNotes, + [walletId], + ), + returnValue: _FakeQueryBuilder_3<_i13.TransactionNote, + _i13.TransactionNote, _i4.QAfterWhereClause>( + this, + Invocation.method( + #getTransactionNotes, + [walletId], + ), + ), + ) as _i4.QueryBuilder<_i13.TransactionNote, _i13.TransactionNote, + _i4.QAfterWhereClause>); + @override + _i6.Future<void> putTransactionNote(_i13.TransactionNote? transactionNote) => + (super.noSuchMethod( + Invocation.method( + #putTransactionNote, + [transactionNote], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<void> putTransactionNotes( + List<_i13.TransactionNote>? transactionNotes) => + (super.noSuchMethod( + Invocation.method( + #putTransactionNotes, + [transactionNotes], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<_i13.TransactionNote?> getTransactionNote( + String? walletId, + String? txid, + ) => + (super.noSuchMethod( + Invocation.method( + #getTransactionNote, + [ + walletId, + txid, + ], + ), + returnValue: _i6.Future<_i13.TransactionNote?>.value(), + ) as _i6.Future<_i13.TransactionNote?>); + @override + _i6.Stream<_i13.TransactionNote?> watchTransactionNote({ + required int? id, + bool? fireImmediately = false, + }) => + (super.noSuchMethod( + Invocation.method( + #watchTransactionNote, + [], + { + #id: id, + #fireImmediately: fireImmediately, + }, + ), + returnValue: _i6.Stream<_i13.TransactionNote?>.empty(), + ) as _i6.Stream<_i13.TransactionNote?>); + @override + _i4.QueryBuilder<_i13.AddressLabel, _i13.AddressLabel, _i4.QAfterWhereClause> + getAddressLabels(String? walletId) => (super.noSuchMethod( + Invocation.method( + #getAddressLabels, + [walletId], + ), + returnValue: _FakeQueryBuilder_3<_i13.AddressLabel, + _i13.AddressLabel, _i4.QAfterWhereClause>( + this, + Invocation.method( + #getAddressLabels, + [walletId], + ), + ), + ) as _i4.QueryBuilder<_i13.AddressLabel, _i13.AddressLabel, + _i4.QAfterWhereClause>); + @override + _i6.Future<int> putAddressLabel(_i13.AddressLabel? addressLabel) => + (super.noSuchMethod( + Invocation.method( + #putAddressLabel, + [addressLabel], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + int putAddressLabelSync(_i13.AddressLabel? addressLabel) => + (super.noSuchMethod( + Invocation.method( + #putAddressLabelSync, + [addressLabel], + ), + returnValue: 0, + ) as int); + @override + _i6.Future<void> putAddressLabels(List<_i13.AddressLabel>? addressLabels) => + (super.noSuchMethod( + Invocation.method( + #putAddressLabels, + [addressLabels], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<_i13.AddressLabel?> getAddressLabel( + String? walletId, + String? addressString, + ) => + (super.noSuchMethod( + Invocation.method( + #getAddressLabel, + [ + walletId, + addressString, + ], + ), + returnValue: _i6.Future<_i13.AddressLabel?>.value(), + ) as _i6.Future<_i13.AddressLabel?>); + @override + _i13.AddressLabel? getAddressLabelSync( + String? walletId, + String? addressString, + ) => + (super.noSuchMethod(Invocation.method( + #getAddressLabelSync, + [ + walletId, + addressString, + ], + )) as _i13.AddressLabel?); + @override + _i6.Stream<_i13.AddressLabel?> watchAddressLabel({ + required int? id, + bool? fireImmediately = false, + }) => + (super.noSuchMethod( + Invocation.method( + #watchAddressLabel, + [], + { + #id: id, + #fireImmediately: fireImmediately, + }, + ), + returnValue: _i6.Stream<_i13.AddressLabel?>.empty(), + ) as _i6.Stream<_i13.AddressLabel?>); + @override + _i6.Future<int> updateAddressLabel(_i13.AddressLabel? addressLabel) => + (super.noSuchMethod( + Invocation.method( + #updateAddressLabel, + [addressLabel], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + _i6.Future<void> deleteWalletBlockchainData(String? walletId) => + (super.noSuchMethod( + Invocation.method( + #deleteWalletBlockchainData, + [walletId], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<void> deleteAddressLabels(String? walletId) => (super.noSuchMethod( + Invocation.method( + #deleteAddressLabels, + [walletId], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<void> deleteTransactionNotes(String? walletId) => + (super.noSuchMethod( + Invocation.method( + #deleteTransactionNotes, + [walletId], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i6.Future<void> addNewTransactionData( + List<_i14.Tuple2<_i13.Transaction, _i13.Address?>>? transactionsData, + String? walletId, + ) => + (super.noSuchMethod( + Invocation.method( + #addNewTransactionData, + [ + transactionsData, + walletId, + ], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); + @override + _i4.QueryBuilder<_i13.EthContract, _i13.EthContract, _i4.QWhere> + getEthContracts() => (super.noSuchMethod( + Invocation.method( + #getEthContracts, + [], + ), + returnValue: _FakeQueryBuilder_3<_i13.EthContract, _i13.EthContract, + _i4.QWhere>( + this, + Invocation.method( + #getEthContracts, + [], + ), + ), + ) as _i4 + .QueryBuilder<_i13.EthContract, _i13.EthContract, _i4.QWhere>); + @override + _i6.Future<_i13.EthContract?> getEthContract(String? contractAddress) => + (super.noSuchMethod( + Invocation.method( + #getEthContract, + [contractAddress], + ), + returnValue: _i6.Future<_i13.EthContract?>.value(), + ) as _i6.Future<_i13.EthContract?>); + @override + _i13.EthContract? getEthContractSync(String? contractAddress) => + (super.noSuchMethod(Invocation.method( + #getEthContractSync, + [contractAddress], + )) as _i13.EthContract?); + @override + _i6.Future<int> putEthContract(_i13.EthContract? contract) => + (super.noSuchMethod( + Invocation.method( + #putEthContract, + [contract], + ), + returnValue: _i6.Future<int>.value(0), + ) as _i6.Future<int>); + @override + _i6.Future<void> putEthContracts(List<_i13.EthContract>? contracts) => + (super.noSuchMethod( + Invocation.method( + #putEthContracts, + [contracts], + ), + returnValue: _i6.Future<void>.value(), + returnValueForMissingStub: _i6.Future<void>.value(), + ) as _i6.Future<void>); } diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 2319e31ba..e6157a8c3 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -658,28 +658,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future<void>.value(), ) as _i11.Future<void>); @override - _i11.Future<void> fillAddresses( - String? suppliedMnemonic, - String? mnemonicPassphrase, { - int? perBatch = 50, - int? numberOfThreads = 4, - }) => - (super.noSuchMethod( - Invocation.method( - #fillAddresses, - [ - suppliedMnemonic, - mnemonicPassphrase, - ], - { - #perBatch: perBatch, - #numberOfThreads: numberOfThreads, - }, - ), - returnValue: _i11.Future<void>.value(), - returnValueForMissingStub: _i11.Future<void>.value(), - ) as _i11.Future<void>); - @override _i11.Future<void> fullRescan( int? maxUnusedAddressGap, int? maxNumberOfIndexesToCheck, @@ -728,6 +706,16 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValue: _i11.Future<Map<int, dynamic>>.value(<int, dynamic>{}), ) as _i11.Future<Map<int, dynamic>>); @override + _i11.Future<void> getTransactionCacheEarly(List<String>? allAddresses) => + (super.noSuchMethod( + Invocation.method( + #getTransactionCacheEarly, + [allAddresses], + ), + returnValue: _i11.Future<void>.value(), + returnValueForMissingStub: _i11.Future<void>.value(), + ) as _i11.Future<void>); + @override _i11.Future<List<Map<String, dynamic>>> fetchAnonymitySets() => (super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index e35ea6a6d..26cd58cc2 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1647,28 +1647,6 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet { returnValueForMissingStub: _i18.Future<void>.value(), ) as _i18.Future<void>); @override - _i18.Future<void> fillAddresses( - String? suppliedMnemonic, - String? mnemonicPassphrase, { - int? perBatch = 50, - int? numberOfThreads = 4, - }) => - (super.noSuchMethod( - Invocation.method( - #fillAddresses, - [ - suppliedMnemonic, - mnemonicPassphrase, - ], - { - #perBatch: perBatch, - #numberOfThreads: numberOfThreads, - }, - ), - returnValue: _i18.Future<void>.value(), - returnValueForMissingStub: _i18.Future<void>.value(), - ) as _i18.Future<void>); - @override _i18.Future<void> fullRescan( int? maxUnusedAddressGap, int? maxNumberOfIndexesToCheck, @@ -1717,6 +1695,16 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet { returnValue: _i18.Future<Map<int, dynamic>>.value(<int, dynamic>{}), ) as _i18.Future<Map<int, dynamic>>); @override + _i18.Future<void> getTransactionCacheEarly(List<String>? allAddresses) => + (super.noSuchMethod( + Invocation.method( + #getTransactionCacheEarly, + [allAddresses], + ), + returnValue: _i18.Future<void>.value(), + returnValueForMissingStub: _i18.Future<void>.value(), + ) as _i18.Future<void>); + @override _i18.Future<List<Map<String, dynamic>>> fetchAnonymitySets() => (super.noSuchMethod( Invocation.method( From d0c53c267399495946df410735c7e8f3338c848b Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 13:29:48 -0600 Subject: [PATCH 03/12] Strongly typed used serials list --- lib/electrumx_rpc/cached_electrumx.dart | 19 ++++--- lib/services/coins/firo/firo_wallet.dart | 71 ++++++++++++------------ 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 6338b1d13..2aab533f8 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -188,16 +188,17 @@ class CachedElectrumX { } } - Future<List<dynamic>> getUsedCoinSerials({ + Future<List<String>> getUsedCoinSerials({ required Coin coin, int startNumber = 0, }) async { try { - List<dynamic>? cachedSerials = DB.instance.get<dynamic>( + final _list = DB.instance.get<dynamic>( boxName: DB.instance.boxNameUsedSerialsCache(coin: coin), key: "serials") as List?; - cachedSerials ??= []; + List<String> cachedSerials = + _list == null ? [] : List<String>.from(_list); final startNumber = cachedSerials.length; @@ -211,8 +212,9 @@ class CachedElectrumX { ); final serials = await client.getUsedCoinSerials(startNumber: startNumber); - List newSerials = []; - for (var element in (serials["serials"] as List)) { + List<String> newSerials = []; + + for (final element in (serials["serials"] as List)) { if (!isHexadecimal(element as String)) { newSerials.add(base64ToHex(element)); } else { @@ -222,9 +224,10 @@ class CachedElectrumX { cachedSerials.addAll(newSerials); await DB.instance.put<dynamic>( - boxName: DB.instance.boxNameUsedSerialsCache(coin: coin), - key: "serials", - value: cachedSerials); + boxName: DB.instance.boxNameUsedSerialsCache(coin: coin), + key: "serials", + value: cachedSerials, + ); return cachedSerials; } catch (e, s) { diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 0d79b56d1..77a5a1288 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -146,24 +146,23 @@ Future<void> executeNative(Map<String, dynamic> arguments) async { } else if (function == "restore") { final latestSetId = arguments['latestSetId'] as int; final setDataMap = arguments['setDataMap'] as Map; - final usedSerialNumbers = arguments['usedSerialNumbers'] as List?; + final usedSerialNumbers = arguments['usedSerialNumbers'] as List<String>; final mnemonic = arguments['mnemonic'] as String; final mnemonicPassphrase = arguments['mnemonicPassphrase'] as String; final coin = arguments['coin'] as Coin; - final network = arguments['network'] as NetworkType?; - if (!(usedSerialNumbers == null || network == null)) { - var restoreData = await isolateRestore( - mnemonic, - mnemonicPassphrase, - coin, - latestSetId, - setDataMap, - usedSerialNumbers, - network, - ); - sendPort.send(restoreData); - return; - } + final network = arguments['network'] as NetworkType; + + final restoreData = await isolateRestore( + mnemonic, + mnemonicPassphrase, + coin, + latestSetId, + setDataMap, + usedSerialNumbers, + network, + ); + sendPort.send(restoreData); + return; } Logging.instance.log( @@ -195,7 +194,7 @@ Future<Map<String, dynamic>> isolateRestore( Coin coin, int _latestSetId, Map<dynamic, dynamic> _setDataMap, - List<dynamic> _usedSerialNumbers, + List<String> _usedSerialNumbers, NetworkType network, ) async { List<int> jindexes = []; @@ -206,11 +205,7 @@ Future<Map<String, dynamic>> isolateRestore( var currentIndex = 0; try { - final usedSerialNumbers = _usedSerialNumbers; - Set<dynamic> usedSerialNumbersSet = {}; - for (int ind = 0; ind < usedSerialNumbers.length; ind++) { - usedSerialNumbersSet.add(usedSerialNumbers[ind]); - } + Set<String> usedSerialNumbersSet = _usedSerialNumbers.toSet(); final root = await Bip32Utils.getBip32Root( mnemonic, @@ -223,9 +218,11 @@ Future<Map<String, dynamic>> isolateRestore( chain: MINT_INDEX, index: currentIndex, ); - final mintKeyPair = - await Bip32Utils.getBip32NodeFromRoot(root, _derivePath); - final mintTag = CreateTag( + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + _derivePath, + ); + final String mintTag = CreateTag( Format.uint8listToString(mintKeyPair.privateKey!), currentIndex, Format.uint8listToString(mintKeyPair.identifier), @@ -240,13 +237,16 @@ Future<Map<String, dynamic>> isolateRestore( if (foundCoin.length == 4) { lastFoundIndex = currentIndex; if (foundCoin[2] is int) { - final amount = foundCoin[2] as int; - final serialNumber = GetSerialNumber(amount, - Format.uint8listToString(mintKeyPair.privateKey!), currentIndex, - isTestnet: coin == Coin.firoTestNet); - String publicCoin = foundCoin[0] as String; - String txId = foundCoin[3] as String; - bool isUsed = usedSerialNumbersSet.contains(serialNumber); + final int amount = foundCoin[2] as int; + final String serialNumber = GetSerialNumber( + amount, + Format.uint8listToString(mintKeyPair.privateKey!), + currentIndex, + isTestnet: coin == Coin.firoTestNet, + ); + final String publicCoin = foundCoin[0] as String; + final String txId = foundCoin[3] as String; + final bool isUsed = usedSerialNumbersSet.contains(serialNumber); final duplicateCoin = lelantusCoins.firstWhere((element) { final coin = element.values.first; return coin.txId == txId && @@ -4383,8 +4383,11 @@ class FiroWallet extends CoinServiceAPI } } - Future<void> _restore(int latestSetId, Map<dynamic, dynamic> setDataMap, - dynamic usedSerialNumbers) async { + Future<void> _restore( + int latestSetId, + Map<dynamic, dynamic> setDataMap, + List<String> usedSerialNumbers, + ) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; @@ -4555,7 +4558,7 @@ class FiroWallet extends CoinServiceAPI } } - Future<List<dynamic>> getUsedCoinSerials() async { + Future<List<String>> getUsedCoinSerials() async { try { final response = await cachedElectrumXClient.getUsedCoinSerials( coin: coin, From 87507585b8d48cf59b14afa8c165e6c50199c3c0 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 14:08:25 -0600 Subject: [PATCH 04/12] make function easier to read --- lib/services/coins/firo/firo_wallet.dart | 79 ++++++++++++++---------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 77a5a1288..42679bae1 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -223,39 +223,51 @@ Future<Map<String, dynamic>> isolateRestore( _derivePath, ); final String mintTag = CreateTag( - Format.uint8listToString(mintKeyPair.privateKey!), - currentIndex, - Format.uint8listToString(mintKeyPair.identifier), - isTestnet: coin == Coin.firoTestNet); + Format.uint8listToString(mintKeyPair.privateKey!), + currentIndex, + Format.uint8listToString(mintKeyPair.identifier), + isTestnet: coin == Coin.firoTestNet, + ); for (var setId = 1; setId <= _latestSetId; setId++) { - final setData = _setDataMap[setId]; - final foundCoin = setData["coins"].firstWhere( - (dynamic e) => e[1] == mintTag, - orElse: () => <Object>[]); + final setData = _setDataMap[setId] as Map; + final foundCoin = (setData["coins"] as List).firstWhere( + (e) => e[1] == mintTag, + orElse: () => <Object>[], + ); if (foundCoin.length == 4) { lastFoundIndex = currentIndex; - if (foundCoin[2] is int) { - final int amount = foundCoin[2] as int; + + final String publicCoin = foundCoin[0] as String; + final String txId = foundCoin[3] as String; + + // this value will either be an int or a String + final dynamic thirdValue = foundCoin[2]; + + if (thirdValue is int) { + final int amount = thirdValue; final String serialNumber = GetSerialNumber( amount, Format.uint8listToString(mintKeyPair.privateKey!), currentIndex, isTestnet: coin == Coin.firoTestNet, ); - final String publicCoin = foundCoin[0] as String; - final String txId = foundCoin[3] as String; final bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere((element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, orElse: () => {}); + final duplicateCoin = lelantusCoins.firstWhere( + (element) { + final coin = element.values.first; + return coin.txId == txId && + coin.index == currentIndex && + coin.anonymitySetId != setId; + }, + orElse: () => {}, + ); if (duplicateCoin.isNotEmpty) { - //todo: check if print needed - // debugPrint("removing duplicate: $duplicateCoin"); + Logging.instance.log( + "Firo isolateRestore removing duplicate coin: $duplicateCoin", + level: LogLevel.Info, + ); lelantusCoins.remove(duplicateCoin); } lelantusCoins.add({ @@ -270,22 +282,25 @@ Future<Map<String, dynamic>> isolateRestore( }); Logging.instance .log("amount $amount used $isUsed", level: LogLevel.Info); - } else { - final keyPath = GetAesKeyPath(foundCoin[0] as String); + } else if (thirdValue is String) { + final keyPath = GetAesKeyPath(publicCoin); final derivePath = constructDerivePath( networkWIF: network.wif, chain: JMINT_INDEX, index: keyPath, ); - final aesKeyPair = - await Bip32Utils.getBip32NodeFromRoot(root, derivePath); + final aesKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + derivePath, + ); if (aesKeyPair.privateKey != null) { - final aesPrivateKey = - Format.uint8listToString(aesKeyPair.privateKey!); + final aesPrivateKey = Format.uint8listToString( + aesKeyPair.privateKey!, + ); final amount = decryptMintAmount( aesPrivateKey, - foundCoin[2] as String, + thirdValue, ); final serialNumber = GetSerialNumber( @@ -293,8 +308,6 @@ Future<Map<String, dynamic>> isolateRestore( Format.uint8listToString(mintKeyPair.privateKey!), currentIndex, isTestnet: coin == Coin.firoTestNet); - String publicCoin = foundCoin[0] as String; - String txId = foundCoin[3] as String; bool isUsed = usedSerialNumbersSet.contains(serialNumber); final duplicateCoin = lelantusCoins.firstWhere((element) { final coin = element.values.first; @@ -308,7 +321,7 @@ Future<Map<String, dynamic>> isolateRestore( lelantusCoins.remove(duplicateCoin); } lelantusCoins.add({ - '${foundCoin[3]!}': LelantusCoin( + txId: LelantusCoin( currentIndex, amount, publicCoin, @@ -319,7 +332,7 @@ Future<Map<String, dynamic>> isolateRestore( }); jindexes.add(currentIndex); - spendTxIds.add(foundCoin[3] as String); + spendTxIds.add(txId); } } } @@ -2170,6 +2183,10 @@ class FiroWallet extends CoinServiceAPI ), ); + await cachedElectrumXClient.getAnonymitySet(groupId: "1", coin: coin); + await cachedElectrumXClient.getAnonymitySet(groupId: "2", coin: coin); + await cachedElectrumXClient.getAnonymitySet(groupId: "3", coin: coin); + GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); await checkReceivingAddressForTransactions(); From a513e90082259bd4858df73176a3a528fc450c23 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 14:13:14 -0600 Subject: [PATCH 05/12] make function easier to read: Part 2 --- lib/services/coins/firo/firo_wallet.dart | 50 +++++++++++++----------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 42679bae1..2fbaa8438 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -280,11 +280,13 @@ Future<Map<String, dynamic>> isolateRestore( isUsed, ) }); - Logging.instance - .log("amount $amount used $isUsed", level: LogLevel.Info); + Logging.instance.log( + "amount $amount used $isUsed", + level: LogLevel.Info, + ); } else if (thirdValue is String) { - final keyPath = GetAesKeyPath(publicCoin); - final derivePath = constructDerivePath( + final int keyPath = GetAesKeyPath(publicCoin); + final String derivePath = constructDerivePath( networkWIF: network.wif, chain: JMINT_INDEX, index: keyPath, @@ -295,29 +297,35 @@ Future<Map<String, dynamic>> isolateRestore( ); if (aesKeyPair.privateKey != null) { - final aesPrivateKey = Format.uint8listToString( + final String aesPrivateKey = Format.uint8listToString( aesKeyPair.privateKey!, ); - final amount = decryptMintAmount( + final int amount = decryptMintAmount( aesPrivateKey, thirdValue, ); - final serialNumber = GetSerialNumber( - amount, - Format.uint8listToString(mintKeyPair.privateKey!), - currentIndex, - isTestnet: coin == Coin.firoTestNet); + final String serialNumber = GetSerialNumber( + amount, + Format.uint8listToString(mintKeyPair.privateKey!), + currentIndex, + isTestnet: coin == Coin.firoTestNet, + ); bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere((element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, orElse: () => {}); + final duplicateCoin = lelantusCoins.firstWhere( + (element) { + final coin = element.values.first; + return coin.txId == txId && + coin.index == currentIndex && + coin.anonymitySetId != setId; + }, + orElse: () => {}, + ); if (duplicateCoin.isNotEmpty) { - //todo: check if print needed - // debugPrint("removing duplicate: $duplicateCoin"); + Logging.instance.log( + "Firo isolateRestore removing duplicate coin: $duplicateCoin", + level: LogLevel.Info, + ); lelantusCoins.remove(duplicateCoin); } lelantusCoins.add({ @@ -2183,10 +2191,6 @@ class FiroWallet extends CoinServiceAPI ), ); - await cachedElectrumXClient.getAnonymitySet(groupId: "1", coin: coin); - await cachedElectrumXClient.getAnonymitySet(groupId: "2", coin: coin); - await cachedElectrumXClient.getAnonymitySet(groupId: "3", coin: coin); - GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId)); await checkReceivingAddressForTransactions(); From eac4ee54f1cf69d89779f79d864cd868e3316d0b Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 14:17:12 -0600 Subject: [PATCH 06/12] logging on possible bad data --- lib/services/coins/firo/firo_wallet.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 2fbaa8438..2f60e7a52 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -342,7 +342,17 @@ Future<Map<String, dynamic>> isolateRestore( spendTxIds.add(txId); } + } else { + Logging.instance.log( + "Unexpected coin found: $foundCoin", + level: LogLevel.Warning, + ); } + } else { + Logging.instance.log( + "Coin not found in data with the mint tag: $mintTag", + level: LogLevel.Warning, + ); } } From 7d8f03dd202ae7450f90a62bcf074d64790b4587 Mon Sep 17 00:00:00 2001 From: Josh Babb <sneurlax@gmail.com> Date: Tue, 16 May 2023 15:20:18 -0500 Subject: [PATCH 07/12] add warning --- lib/services/coins/firo/firo_wallet.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 2f60e7a52..ae5deb29c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -341,6 +341,11 @@ Future<Map<String, dynamic>> isolateRestore( jindexes.add(currentIndex); spendTxIds.add(txId); + } else { + Logging.instance.log( + "AES keypair derivation issue for derive path: $derivePath", + level: LogLevel.Warning, + ); } } else { Logging.instance.log( @@ -4594,6 +4599,8 @@ class FiroWallet extends CoinServiceAPI final response = await cachedElectrumXClient.getUsedCoinSerials( coin: coin, ); + print("getUsedCoinSerials"); + print(response); return response; } catch (e, s) { Logging.instance.log("Exception rethrown in firo_wallet.dart: $e\n$s", From 01999e4f29db838af2a308b7423130f826e4f872 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 14:38:24 -0600 Subject: [PATCH 08/12] revert if pointless or breaks things? --- lib/services/coins/firo/firo_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index ae5deb29c..0b913c07b 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -271,7 +271,7 @@ Future<Map<String, dynamic>> isolateRestore( lelantusCoins.remove(duplicateCoin); } lelantusCoins.add({ - publicCoin: LelantusCoin( + txId: LelantusCoin( currentIndex, amount, publicCoin, From 02fe1103cb21324f8f88049e44b06f23368df787 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 14:43:52 -0600 Subject: [PATCH 09/12] fix assignment issue --- lib/services/coins/firo/firo_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 0b913c07b..1d55427ae 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2447,7 +2447,7 @@ class FiroWallet extends CoinServiceAPI element.forEach((key, value) { isar_models.Transaction? tx; try { - tx == data.firstWhere((e) => e.txid == value.txId); + tx = data.firstWhere((e) => e.txid == value.txId); } catch (_) { tx = null; } From 9725ed32be88f0df87b4ab72abb8dd18236b91a2 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 15:15:08 -0600 Subject: [PATCH 10/12] WIP rework of anon balance calc --- lib/services/coins/firo/firo_wallet.dart | 87 +++++++++++------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 1d55427ae..847570495 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2432,61 +2432,54 @@ class FiroWallet extends CoinServiceAPI lelantusCoins.removeWhere((element) => element.values.any((elementCoin) => elementCoin.value == 0)); } - final data = await _txnData; - final lData = await db - .getTransactions(walletId) - .filter() - .isLelantusEqualTo(true) - .findAll(); + final currentChainHeight = await chainHeight; final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; - for (var element in lelantusCoins) { - element.forEach((key, value) { - isar_models.Transaction? tx; - try { - tx = data.firstWhere((e) => e.txid == value.txId); - } catch (_) { - tx = null; - } + for (final element in lelantusCoins) { + element.forEach((key, lelantusCoin) { + isar_models.Transaction? txn = db.isar.transactions + .where() + .txidWalletIdEqualTo( + lelantusCoin.txId, + walletId, + ) + .findFirstSync(); - isar_models.Transaction? ltx; - try { - ltx = lData.firstWhere((e) => e.txid == value.txId); - } catch (_) { - ltx = null; - } - - // Logging.instance.log("$value $tx $ltx"); - if (!jindexes!.contains(value.index) && tx == null) { - if (!value.isUsed && - ltx != null && - ltx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - // mint tx, add value to balance - intLelantusBalance += value.value; - } /* else { + if (txn == null) { + // TODO: ?????????????????????????????????????? + } else { + bool isLelantus = txn.isLelantus == true; + if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { + if (!lelantusCoin.isUsed && + txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + // mint tx, add value to balance + intLelantusBalance += lelantusCoin.value; + } /* else { // This coin is not confirmed and may be replaced }*/ - } else if (jindexes.contains(value.index) && - tx == null && - !value.isUsed && - ltx != null && - !ltx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - unconfirmedLelantusBalance += value.value; - } else if (jindexes.contains(value.index) && !value.isUsed) { - intLelantusBalance += value.value; - } else if (!value.isUsed && - (tx == null - ? true - : tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += value.value; - } else if (tx != null && - tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { - unconfirmedLelantusBalance += value.value; + } else if (jindexes.contains(lelantusCoin.index) && + isLelantus && + !lelantusCoin.isUsed && + !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + unconfirmedLelantusBalance += lelantusCoin.value; + } else if (jindexes.contains(lelantusCoin.index) && + !lelantusCoin.isUsed) { + intLelantusBalance += lelantusCoin.value; + } else if (!lelantusCoin.isUsed && + (txn.isLelantus == true + ? true + : txn.isConfirmed( + currentChainHeight, MINIMUM_CONFIRMATIONS) != + false)) { + intLelantusBalance += lelantusCoin.value; + } else if (!isLelantus && + txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == + false) { + unconfirmedLelantusBalance += lelantusCoin.value; + } } }); } From ab78e3fbca8c43deebcc772c710da493c75ac324 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 15:59:06 -0600 Subject: [PATCH 11/12] clean up _getUnspentCoins() --- lib/services/coins/firo/firo_wallet.dart | 42 ++++++------------------ 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 847570495..23e367ca4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2354,12 +2354,6 @@ class FiroWallet extends CoinServiceAPI element.values.any((elementCoin) => elementCoin.value == 0)); } final jindexes = firoGetJIndex(); - final transactions = await _txnData; - final lelantusTransactionsd = await db - .getTransactions(walletId) - .filter() - .isLelantusEqualTo(true) - .findAll(); List<LelantusCoin> coins = []; @@ -2373,42 +2367,26 @@ class FiroWallet extends CoinServiceAPI for (int i = 0; i < lelantusCoinsList.length; i++) { // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); + final txid = lelantusCoinsList[i].txId; final txn = await cachedElectrumXClient.getTransaction( - txHash: lelantusCoinsList[i].txId, + txHash: txid, verbose: true, coin: coin, ); final confirmations = txn["confirmations"]; bool isUnconfirmed = confirmations is int && confirmations < 1; + + final tx = await db.getTransaction(walletId, txid); + if (!jindexes!.contains(lelantusCoinsList[i].index) && - transactions - .where((e) => e.txid == lelantusCoinsList[i].txId) - .isEmpty && - !(lelantusTransactionsd - .where((e) => e.txid == lelantusCoinsList[i].txId) - .isNotEmpty && - lelantusTransactionsd - .where((e) => e.txid == lelantusCoinsList[i].txId) - .first - .isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { + tx != null && + tx.isLelantus == true && + !(tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { isUnconfirmed = true; } - // TODO: optimize the following - if ((transactions - .where((e) => e.txid == lelantusCoinsList[i].txId) - .isNotEmpty && - !transactions - .where((e) => e.txid == lelantusCoinsList[i].txId) - .first - .isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) || - (lelantusTransactionsd - .where((e) => e.txid == lelantusCoinsList[i].txId) - .isNotEmpty && - !lelantusTransactionsd - .where((e) => e.txid == lelantusCoinsList[i].txId) - .first - .isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { + if (tx != null && + !tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { continue; } if (!lelantusCoinsList[i].isUsed && From 57b7327287d83f05e69140b44f3029efe5fb0ddc Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 16 May 2023 15:59:33 -0600 Subject: [PATCH 12/12] update test and mocks --- .../wallet_settings_view_screen_test.mocks.dart | 6 +++--- .../services/coins/bitcoin/bitcoin_wallet_test.mocks.dart | 6 +++--- .../coins/bitcoincash/bitcoincash_wallet_test.mocks.dart | 6 +++--- .../coins/dogecoin/dogecoin_wallet_test.mocks.dart | 6 +++--- test/services/coins/firo/firo_wallet_test.dart | 8 ++++---- test/services/coins/firo/firo_wallet_test.mocks.dart | 6 +++--- test/services/coins/manager_test.mocks.dart | 6 +++--- .../coins/namecoin/namecoin_wallet_test.mocks.dart | 6 +++--- .../services/coins/particl/particl_wallet_test.mocks.dart | 6 +++--- test/widget_tests/transaction_card_test.mocks.dart | 6 +++--- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart index 026d06a20..b63e29f34 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart @@ -175,7 +175,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX { _i9.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i9.Future<Map<String, dynamic>>); @override - _i9.Future<List<dynamic>> getUsedCoinSerials({ + _i9.Future<List<String>> getUsedCoinSerials({ required _i10.Coin? coin, int? startNumber = 0, }) => @@ -188,8 +188,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i9.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i9.Future<List<dynamic>>); + returnValue: _i9.Future<List<String>>.value(<String>[]), + ) as _i9.Future<List<String>>); @override _i9.Future<void> clearSharedTransactionCache({required _i10.Coin? coin}) => (super.noSuchMethod( diff --git a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart index 12ae0ae62..93eb0e1c8 100644 --- a/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart +++ b/test/services/coins/bitcoin/bitcoin_wallet_test.mocks.dart @@ -487,7 +487,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i5.Future<Map<String, dynamic>>); @override - _i5.Future<List<dynamic>> getUsedCoinSerials({ + _i5.Future<List<String>> getUsedCoinSerials({ required _i7.Coin? coin, int? startNumber = 0, }) => @@ -500,8 +500,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i5.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i5.Future<List<dynamic>>); + returnValue: _i5.Future<List<String>>.value(<String>[]), + ) as _i5.Future<List<String>>); @override _i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) => (super.noSuchMethod( diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart index 699278ce1..05cd24ebd 100644 --- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart +++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.mocks.dart @@ -487,7 +487,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i5.Future<Map<String, dynamic>>); @override - _i5.Future<List<dynamic>> getUsedCoinSerials({ + _i5.Future<List<String>> getUsedCoinSerials({ required _i7.Coin? coin, int? startNumber = 0, }) => @@ -500,8 +500,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i5.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i5.Future<List<dynamic>>); + returnValue: _i5.Future<List<String>>.value(<String>[]), + ) as _i5.Future<List<String>>); @override _i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) => (super.noSuchMethod( diff --git a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart index 3c78f6dc6..ff04b9f73 100644 --- a/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart +++ b/test/services/coins/dogecoin/dogecoin_wallet_test.mocks.dart @@ -487,7 +487,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i5.Future<Map<String, dynamic>>); @override - _i5.Future<List<dynamic>> getUsedCoinSerials({ + _i5.Future<List<String>> getUsedCoinSerials({ required _i7.Coin? coin, int? startNumber = 0, }) => @@ -500,8 +500,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i5.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i5.Future<List<dynamic>>); + returnValue: _i5.Future<List<String>>.value(<String>[]), + ) as _i5.Future<List<String>>); @override _i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) => (super.noSuchMethod( diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index a3f0eca1a..766df279f 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -70,7 +70,7 @@ void main() { Coin.firo, 1, setData, - usedSerials, + List<String>.from(usedSerials), firoNetwork, ); const currentHeight = 100000000000; @@ -130,7 +130,7 @@ void main() { Coin.firo, 1, setData, - usedSerials, + List<String>.from(usedSerials), firoNetwork, ), throwsA(isA<Error>())); @@ -2277,8 +2277,8 @@ void main() { groupId: "1", blockhash: "", coin: Coin.firo)) .thenAnswer((_) async => GetAnonymitySetSampleData.data); when(cachedClient.getUsedCoinSerials(startNumber: 0, coin: Coin.firo)) - .thenAnswer( - (_) async => GetUsedSerialsSampleData.serials['serials'] as List); + .thenAnswer((_) async => List<String>.from( + GetUsedSerialsSampleData.serials['serials'] as List)); final firo = FiroWallet( walletId: "${testWalletId}getUsedCoinSerials", diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 790be8e23..bb6d13ca7 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -514,7 +514,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX { _i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i6.Future<Map<String, dynamic>>); @override - _i6.Future<List<dynamic>> getUsedCoinSerials({ + _i6.Future<List<String>> getUsedCoinSerials({ required _i8.Coin? coin, int? startNumber = 0, }) => @@ -527,8 +527,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i6.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i6.Future<List<dynamic>>); + returnValue: _i6.Future<List<String>>.value(<String>[]), + ) as _i6.Future<List<String>>); @override _i6.Future<void> clearSharedTransactionCache({required _i8.Coin? coin}) => (super.noSuchMethod( diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index e6157a8c3..8a607f14f 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -734,13 +734,13 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValue: _i11.Future<int>.value(0), ) as _i11.Future<int>); @override - _i11.Future<List<dynamic>> getUsedCoinSerials() => (super.noSuchMethod( + _i11.Future<List<String>> getUsedCoinSerials() => (super.noSuchMethod( Invocation.method( #getUsedCoinSerials, [], ), - returnValue: _i11.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i11.Future<List<dynamic>>); + returnValue: _i11.Future<List<String>>.value(<String>[]), + ) as _i11.Future<List<String>>); @override _i11.Future<void> exit() => (super.noSuchMethod( Invocation.method( diff --git a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart index 28500d8af..3e9fd4fde 100644 --- a/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart +++ b/test/services/coins/namecoin/namecoin_wallet_test.mocks.dart @@ -487,7 +487,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i5.Future<Map<String, dynamic>>); @override - _i5.Future<List<dynamic>> getUsedCoinSerials({ + _i5.Future<List<String>> getUsedCoinSerials({ required _i7.Coin? coin, int? startNumber = 0, }) => @@ -500,8 +500,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i5.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i5.Future<List<dynamic>>); + returnValue: _i5.Future<List<String>>.value(<String>[]), + ) as _i5.Future<List<String>>); @override _i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) => (super.noSuchMethod( diff --git a/test/services/coins/particl/particl_wallet_test.mocks.dart b/test/services/coins/particl/particl_wallet_test.mocks.dart index 43a1473ce..11a8de944 100644 --- a/test/services/coins/particl/particl_wallet_test.mocks.dart +++ b/test/services/coins/particl/particl_wallet_test.mocks.dart @@ -487,7 +487,7 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { _i5.Future<Map<String, dynamic>>.value(<String, dynamic>{}), ) as _i5.Future<Map<String, dynamic>>); @override - _i5.Future<List<dynamic>> getUsedCoinSerials({ + _i5.Future<List<String>> getUsedCoinSerials({ required _i7.Coin? coin, int? startNumber = 0, }) => @@ -500,8 +500,8 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX { #startNumber: startNumber, }, ), - returnValue: _i5.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i5.Future<List<dynamic>>); + returnValue: _i5.Future<List<String>>.value(<String>[]), + ) as _i5.Future<List<String>>); @override _i5.Future<void> clearSharedTransactionCache({required _i7.Coin? coin}) => (super.noSuchMethod( diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 26cd58cc2..67081a304 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1723,13 +1723,13 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet { returnValue: _i18.Future<int>.value(0), ) as _i18.Future<int>); @override - _i18.Future<List<dynamic>> getUsedCoinSerials() => (super.noSuchMethod( + _i18.Future<List<String>> getUsedCoinSerials() => (super.noSuchMethod( Invocation.method( #getUsedCoinSerials, [], ), - returnValue: _i18.Future<List<dynamic>>.value(<dynamic>[]), - ) as _i18.Future<List<dynamic>>); + returnValue: _i18.Future<List<String>>.value(<String>[]), + ) as _i18.Future<List<String>>); @override _i18.Future<void> exit() => (super.noSuchMethod( Invocation.method(