From 94d7f566b716517af06a6029255e92d7f543eb6a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 29 Dec 2024 21:58:49 -0600 Subject: [PATCH] fix: simplify BIP48BitcoinWallet via addition of BIP48Bitcoin extension --- .../crypto_currency/coins/bip48_bitcoin.dart | 34 + lib/wallets/wallet/impl/bip48_wallet.dart | 864 +----------------- 2 files changed, 38 insertions(+), 860 deletions(-) create mode 100644 lib/wallets/crypto_currency/coins/bip48_bitcoin.dart diff --git a/lib/wallets/crypto_currency/coins/bip48_bitcoin.dart b/lib/wallets/crypto_currency/coins/bip48_bitcoin.dart new file mode 100644 index 000000000..c17c9bb5f --- /dev/null +++ b/lib/wallets/crypto_currency/coins/bip48_bitcoin.dart @@ -0,0 +1,34 @@ +import '../../../utilities/enums/derive_path_type_enum.dart'; +import 'bitcoin.dart'; + +class BIP48Bitcoin extends Bitcoin { + BIP48Bitcoin(super.network); + + @override + List get supportedDerivationPathTypes => [ + ...super.supportedDerivationPathTypes, + DerivePathType.bip48p2shp2wsh, + DerivePathType.bip48p2wsh, + ]; + + @override + String constructDerivePath({ + required DerivePathType derivePathType, + int account = 0, + required int chain, + required int index, + }) { + if (derivePathType == DerivePathType.bip48p2shp2wsh || + derivePathType == DerivePathType.bip48p2wsh) { + final coinType = networkParams.wifPrefix == 0x80 ? "0" : "1"; + return "m/48'/$coinType'/$account'/$chain/$index"; + } + + return super.constructDerivePath( + derivePathType: derivePathType, + account: account, + chain: chain, + index: index, + ); + } +} diff --git a/lib/wallets/wallet/impl/bip48_wallet.dart b/lib/wallets/wallet/impl/bip48_wallet.dart index 68dfba94a..fc9c90038 100644 --- a/lib/wallets/wallet/impl/bip48_wallet.dart +++ b/lib/wallets/wallet/impl/bip48_wallet.dart @@ -1,345 +1,22 @@ import 'dart:async'; -import 'dart:math'; - -import 'package:isar/isar.dart'; import '../../../electrumx_rpc/cached_electrumx_client.dart'; import '../../../electrumx_rpc/electrumx_client.dart'; -import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; -import '../../../models/isar/models/blockchain_data/transaction.dart'; -import '../../../models/isar/models/blockchain_data/utxo.dart'; -import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; -import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; -import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; -import '../../../models/paymint/fee_object_model.dart'; -import '../../../utilities/amount/amount.dart'; -import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; +import '../../crypto_currency/coins/bip48_bitcoin.dart'; import '../../crypto_currency/crypto_currency.dart'; -import '../../crypto_currency/intermediate/bip39_hd_currency.dart'; -import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; -import '../wallet.dart'; -import '../wallet_mixin_interfaces/multi_address_interface.dart'; +import 'bitcoin_wallet.dart'; -class BIP48Wallet extends Wallet - with MultiAddressInterface { - BIP48Wallet(CryptoCurrencyNetwork network) : super(Bitcoin(network) as T); +class BIP48BitcoinWallet extends BitcoinWallet { + BIP48BitcoinWallet(CryptoCurrencyNetwork network) : super(network); late ElectrumXClient electrumXClient; late CachedElectrumXClient electrumXCachedClient; - Future sweepAllEstimate(int feeRate) async { - int available = 0; - int inputCount = 0; - final height = await chainHeight; - for (final output in (await mainDB.getUTXOs(walletId).findAll())) { - if (!output.isBlocked && - output.isConfirmed( - height, - cryptoCurrency.minConfirms, - cryptoCurrency.minCoinbaseConfirms, - )) { - available += output.value; - inputCount++; - } - } - - // transaction will only have 1 output minus the fee - final estimatedFee = _roughFeeEstimate(inputCount, 1, feeRate); - - return Amount( - rawValue: BigInt.from(available), - fractionDigits: cryptoCurrency.fractionDigits, - ) - - estimatedFee; - } - - // int _estimateTxFee({required int vSize, required int feeRatePerKB}) { - // return vSize * (feeRatePerKB / 1000).ceil(); - // } - - Amount _roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { - return Amount( - rawValue: BigInt.from( - ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * - (feeRatePerKB / 1000).ceil(), - ), - fractionDigits: cryptoCurrency.fractionDigits, - ); - } - // ==================== Overrides ============================================ - @override - bool get supportsMultiRecipient => true; - - @override - int get isarTransactionVersion => 2; - - @override - FilterOperation? get changeAddressFilterOperation => FilterGroup.and( - [ - FilterCondition.equalTo( - property: r"type", - value: info.mainAddressType, - ), - const FilterCondition.equalTo( - property: r"subType", - value: AddressSubType.change, - ), - const FilterCondition.greaterThan( - property: r"derivationIndex", - value: 0, - ), - ], - ); - - @override - FilterOperation? get receivingAddressFilterOperation => FilterGroup.and( - [ - FilterCondition.equalTo( - property: r"type", - value: info.mainAddressType, - ), - const FilterCondition.equalTo( - property: r"subType", - value: AddressSubType.receiving, - ), - const FilterCondition.greaterThan( - property: r"derivationIndex", - value: 0, - ), - ], - ); - - @override - Future updateTransactions() async { - // Get all addresses. - final List
allAddressesOld = - await _fetchAddressesForElectrumXScan(); - - // Separate receiving and change addresses. - final Set receivingAddresses = allAddressesOld - .where((e) => e.subType == AddressSubType.receiving) - .map((e) => e.value) - .toSet(); - final Set changeAddresses = allAddressesOld - .where((e) => e.subType == AddressSubType.change) - .map((e) => e.value) - .toSet(); - - // Remove duplicates. - final allAddressesSet = {...receivingAddresses, ...changeAddresses}; - - final currentHeight = await chainHeight; - - // Fetch history from ElectrumX. - final List> allTxHashes = - await _fetchHistory(allAddressesSet); - - final List> allTransactions = []; - - for (final txHash in allTxHashes) { - final storedTx = await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .filter() - .txidEqualTo(txHash["tx_hash"] as String) - .findFirst(); - - if (storedTx == null || - !storedTx.isConfirmed( - currentHeight, - cryptoCurrency.minConfirms, - cryptoCurrency.minCoinbaseConfirms, - )) { - final tx = await electrumXCachedClient.getTransaction( - txHash: txHash["tx_hash"] as String, - verbose: true, - cryptoCurrency: cryptoCurrency, - ); - - if (!_duplicateTxCheck(allTransactions, tx["txid"] as String)) { - tx["height"] = txHash["height"]; - allTransactions.add(tx); - } - } - } - - // Parse all new txs. - final List txns = []; - for (final txData in allTransactions) { - bool wasSentFromThisWallet = false; - // Set to true if any inputs were detected as owned by this wallet. - - bool wasReceivedInThisWallet = false; - // Set to true if any outputs were detected as owned by this wallet. - - // Parse inputs. - BigInt amountReceivedInThisWallet = BigInt.zero; - BigInt changeAmountReceivedInThisWallet = BigInt.zero; - final List inputs = []; - for (final jsonInput in txData["vin"] as List) { - final map = Map.from(jsonInput as Map); - - final List addresses = []; - String valueStringSats = "0"; - OutpointV2? outpoint; - - final coinbase = map["coinbase"] as String?; - - if (coinbase == null) { - // Not a coinbase (ie a typical input). - final txid = map["txid"] as String; - final vout = map["vout"] as int; - - final inputTx = await electrumXCachedClient.getTransaction( - txHash: txid, - cryptoCurrency: cryptoCurrency, - ); - - final prevOutJson = Map.from( - (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map, - ); - - final prevOut = OutputV2.fromElectrumXJson( - prevOutJson, - decimalPlaces: cryptoCurrency.fractionDigits, - isFullAmountNotSats: true, - walletOwns: false, // Doesn't matter here as this is not saved. - ); - - outpoint = OutpointV2.isarCantDoRequiredInDefaultConstructor( - txid: txid, - vout: vout, - ); - valueStringSats = prevOut.valueStringSats; - addresses.addAll(prevOut.addresses); - } - - InputV2 input = InputV2.fromElectrumxJson( - json: map, - outpoint: outpoint, - valueStringSats: valueStringSats, - addresses: addresses, - coinbase: coinbase, - // Need addresses before we can know if the wallet owns this input. - walletOwns: false, - ); - - // Check if input was from this wallet. - if (allAddressesSet.intersection(input.addresses.toSet()).isNotEmpty) { - wasSentFromThisWallet = true; - input = input.copyWith(walletOwns: true); - } - - inputs.add(input); - } - - // Parse outputs. - final List outputs = []; - for (final outputJson in txData["vout"] as List) { - OutputV2 output = OutputV2.fromElectrumXJson( - Map.from(outputJson as Map), - decimalPlaces: cryptoCurrency.fractionDigits, - isFullAmountNotSats: true, - // Need addresses before we can know if the wallet owns this input. - walletOwns: false, - ); - - // If output was to my wallet, add value to amount received. - if (receivingAddresses - .intersection(output.addresses.toSet()) - .isNotEmpty) { - wasReceivedInThisWallet = true; - amountReceivedInThisWallet += output.value; - output = output.copyWith(walletOwns: true); - } else if (changeAddresses - .intersection(output.addresses.toSet()) - .isNotEmpty) { - wasReceivedInThisWallet = true; - changeAmountReceivedInThisWallet += output.value; - output = output.copyWith(walletOwns: true); - } - - outputs.add(output); - } - - final totalOut = outputs - .map((e) => e.value) - .fold(BigInt.zero, (value, element) => value + element); - - TransactionType type; - TransactionSubType subType = TransactionSubType.none; - // Will BIP48 wallets enjoy BIP47 compatibility? We should add vectors - // for this if so--do any wallets implement such functionality? - if (outputs.length > 1 && inputs.isNotEmpty) { - for (int i = 0; i < outputs.length; i++) { - final List? scriptChunks = - outputs[i].scriptPubKeyAsm?.split(" "); - if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") { - final blindedPaymentCode = scriptChunks![1]; - final bytes = blindedPaymentCode.toUint8ListFromHex; - - // https://en.bitcoin.it/wiki/BIP_0047#Sending - if (bytes.length == 80 && bytes.first == 1) { - subType = TransactionSubType.bip47Notification; - break; - } - } - } - } - - // At least one input was owned by this wallet. - if (wasSentFromThisWallet) { - type = TransactionType.outgoing; - - if (wasReceivedInThisWallet) { - if (changeAmountReceivedInThisWallet + amountReceivedInThisWallet == - totalOut) { - // Definitely sent all to self. - type = TransactionType.sentToSelf; - } else if (amountReceivedInThisWallet == BigInt.zero) { - // Most likely just a typical send, do nothing here yet. - } - } - } else if (wasReceivedInThisWallet) { - // Only found outputs owned by this wallet. - type = TransactionType.incoming; - - // TODO: [prio=none] Check for special Bitcoin outputs like ordinals. - } else { - Logging.instance.log( - "Unexpected tx found (ignoring it): $txData", - level: LogLevel.Error, - ); - continue; - } - - final tx = TransactionV2( - walletId: walletId, - blockHash: txData["blockhash"] as String?, - hash: txData["hash"] as String, - txid: txData["txid"] as String, - height: txData["height"] as int?, - version: txData["version"] as int, - timestamp: txData["blocktime"] as int? ?? - DateTime.timestamp().millisecondsSinceEpoch ~/ 1000, - inputs: List.unmodifiable(inputs), - outputs: List.unmodifiable(outputs), - type: type, - subType: subType, - otherData: null, - ); - - txns.add(tx); - } - - await mainDB.updateOrPutTransactionV2s(txns); - } - @override Future checkSaveInitialReceivingAddress() async { final address = await getCurrentReceivingAddress(); @@ -378,93 +55,6 @@ class BIP48Wallet extends Wallet } } - @override - Future estimateFeeFor(Amount amount, int feeRate) async { - final available = info.cachedBalance.spendable; - - if (available == amount) { - return amount - (await sweepAllEstimate(feeRate)); - } else if (amount <= Amount.zero || amount > available) { - return _roughFeeEstimate(1, 2, feeRate); - } - - Amount runningBalance = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - int inputCount = 0; - for (final output in (await mainDB.getUTXOs(walletId).findAll())) { - if (!output.isBlocked) { - runningBalance += Amount( - rawValue: BigInt.from(output.value), - fractionDigits: cryptoCurrency.fractionDigits, - ); - inputCount++; - if (runningBalance > amount) { - break; - } - } - } - - final oneOutPutFee = _roughFeeEstimate(inputCount, 1, feeRate); - final twoOutPutFee = _roughFeeEstimate(inputCount, 2, feeRate); - - if (runningBalance - amount > oneOutPutFee) { - if (runningBalance - amount > oneOutPutFee + cryptoCurrency.dustLimit) { - final change = runningBalance - amount - twoOutPutFee; - if (change > cryptoCurrency.dustLimit && - runningBalance - amount - change == twoOutPutFee) { - return runningBalance - amount - change; - } else { - return runningBalance - amount; - } - } else { - return runningBalance - amount; - } - } else if (runningBalance - amount == oneOutPutFee) { - return oneOutPutFee; - } else { - return twoOutPutFee; - } - } - - @override - Future get fees async { - try { - // adjust numbers for different speeds? - const int f = 1, m = 5, s = 20; - - final fast = await electrumXClient.estimateFee(blocks: f); - final medium = await electrumXClient.estimateFee(blocks: m); - final slow = await electrumXClient.estimateFee(blocks: s); - - final feeObject = FeeObject( - numberOfBlocksFast: f, - numberOfBlocksAverage: m, - numberOfBlocksSlow: s, - fast: Amount.fromDecimal( - fast, - fractionDigits: cryptoCurrency.fractionDigits, - ).raw.toInt(), - medium: Amount.fromDecimal( - medium, - fractionDigits: cryptoCurrency.fractionDigits, - ).raw.toInt(), - slow: Amount.fromDecimal( - slow, - fractionDigits: cryptoCurrency.fractionDigits, - ).raw.toInt(), - ); - - Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info); - return feeObject; - } catch (e) { - Logging.instance - .log("Exception rethrown from _getFees(): $e", level: LogLevel.Error); - rethrow; - } - } - @override Future prepareSend({required TxData txData}) { // TODO: implement prepareSend @@ -479,325 +69,6 @@ class BIP48Wallet extends Wallet // TODO. } - @override - Future updateBalance() async { - final utxos = await mainDB.getUTXOs(walletId).findAll(); - - final currentChainHeight = await chainHeight; - - Amount satoshiBalanceTotal = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - Amount satoshiBalancePending = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - Amount satoshiBalanceSpendable = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - Amount satoshiBalanceBlocked = Amount( - rawValue: BigInt.zero, - fractionDigits: cryptoCurrency.fractionDigits, - ); - - for (final utxo in utxos) { - final utxoAmount = Amount( - rawValue: BigInt.from(utxo.value), - fractionDigits: cryptoCurrency.fractionDigits, - ); - - satoshiBalanceTotal += utxoAmount; - - if (utxo.isBlocked) { - satoshiBalanceBlocked += utxoAmount; - } else { - if (utxo.isConfirmed( - currentChainHeight, - cryptoCurrency.minConfirms, - cryptoCurrency.minCoinbaseConfirms, - )) { - satoshiBalanceSpendable += utxoAmount; - } else { - satoshiBalancePending += utxoAmount; - } - } - } - - final balance = Balance( - total: satoshiBalanceTotal, - spendable: satoshiBalanceSpendable, - blockedTotal: satoshiBalanceBlocked, - pendingSpendable: satoshiBalancePending, - ); - - await info.updateBalance(newBalance: balance, isar: mainDB.isar); - } - - @override - Future updateChainHeight() async { - final int height; - try { - final result = await electrumXClient.getBlockHeadTip(); - height = result["height"] as int; - } catch (e) { - rethrow; - } - - await info.updateCachedChainHeight( - newHeight: height, - isar: mainDB.isar, - ); - } - - @override - Future pingCheck() async { - try { - final result = await electrumXClient.ping(); - return result; - } catch (_) { - return false; - } - } - - @override - Future updateNode() async { - await _updateElectrumX(); - } - - @override - Future updateUTXOs() async { - final allAddresses = await _fetchAddressesForElectrumXScan(); - - try { - final fetchedUtxoList = >>[]; - for (int i = 0; i < allAddresses.length; i++) { - final scriptHash = cryptoCurrency.addressToScriptHash( - address: allAddresses[i].value, - ); - - final utxos = await electrumXClient.getUTXOs(scripthash: scriptHash); - if (utxos.isNotEmpty) { - fetchedUtxoList.add(utxos); - } - } - - final List outputArray = []; - - for (int i = 0; i < fetchedUtxoList.length; i++) { - for (int j = 0; j < fetchedUtxoList[i].length; j++) { - final utxo = await _parseUTXO( - jsonUTXO: fetchedUtxoList[i][j], - ); - - outputArray.add(utxo); - } - } - - return await mainDB.updateUTXOs(walletId, outputArray); - } catch (e, s) { - Logging.instance.log( - "Output fetch unsuccessful: $e\n$s", - level: LogLevel.Error, - ); - return false; - } - } - - // =================== Private =============================================== - - Future _getCurrentElectrumXNode() async { - final node = getCurrentNode(); - - return ElectrumXNode( - address: node.host, - port: node.port, - name: node.name, - useSSL: node.useSSL, - id: node.id, - torEnabled: node.torEnabled, - clearnetEnabled: node.clearnetEnabled, - ); - } - - // TODO [prio=low]: Use ElectrumXInterface method. - Future _updateElectrumX() async { - final failovers = nodeService - .failoverNodesFor(currency: cryptoCurrency) - .map( - (e) => ElectrumXNode( - address: e.host, - port: e.port, - name: e.name, - id: e.id, - useSSL: e.useSSL, - torEnabled: e.torEnabled, - clearnetEnabled: e.clearnetEnabled, - ), - ) - .toList(); - - final newNode = await _getCurrentElectrumXNode(); - try { - await electrumXClient.closeAdapter(); - } catch (e) { - if (e.toString().contains("initialized")) { - // Ignore. This should happen every first time the wallet is opened. - } else { - Logging.instance.log( - "Error closing electrumXClient: $e", - level: LogLevel.Error, - ); - } - } - electrumXClient = ElectrumXClient.from( - node: newNode, - prefs: prefs, - failovers: failovers, - cryptoCurrency: cryptoCurrency, - ); - - electrumXCachedClient = CachedElectrumXClient.from( - electrumXClient: electrumXClient, - ); - } - - bool _duplicateTxCheck( - List> allTransactions, - String txid, - ) { - for (int i = 0; i < allTransactions.length; i++) { - if (allTransactions[i]["txid"] == txid) { - return true; - } - } - return false; - } - - Future _parseUTXO({ - required Map jsonUTXO, - }) async { - final txn = await electrumXCachedClient.getTransaction( - txHash: jsonUTXO["tx_hash"] as String, - verbose: true, - cryptoCurrency: cryptoCurrency, - ); - - final vout = jsonUTXO["tx_pos"] as int; - - final outputs = txn["vout"] as List; - - // String? scriptPubKey; - String? utxoOwnerAddress; - // get UTXO owner address - for (final output in outputs) { - if (output["n"] == vout) { - // scriptPubKey = output["scriptPubKey"]?["hex"] as String?; - utxoOwnerAddress = - output["scriptPubKey"]?["addresses"]?[0] as String? ?? - output["scriptPubKey"]?["address"] as String?; - } - } - - final utxo = UTXO( - walletId: walletId, - txid: txn["txid"] as String, - vout: vout, - value: jsonUTXO["value"] as int, - name: "", - isBlocked: false, - blockedReason: null, - isCoinbase: txn["is_coinbase"] as bool? ?? false, - blockHash: txn["blockhash"] as String?, - blockHeight: jsonUTXO["height"] as int?, - blockTime: txn["blocktime"] as int?, - address: utxoOwnerAddress, - ); - - return utxo; - } - - @override - Future checkChangeAddressForTransactions() async { - try { - final currentChange = await getCurrentChangeAddress(); - - final bool needsGenerate; - if (currentChange == null) { - // no addresses in db yet for some reason. - // Should not happen at this point... - - needsGenerate = true; - } else { - final txCount = await _fetchTxCount(address: currentChange); - needsGenerate = txCount > 0 || currentChange.derivationIndex < 0; - } - - if (needsGenerate) { - await generateNewChangeAddress(); - - // TODO: get rid of this? Could cause problems (long loading/infinite loop or something) - // keep checking until address with no tx history is set as current - await checkChangeAddressForTransactions(); - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from _checkChangeAddressForTransactions" - "($cryptoCurrency): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } - - @override - Future checkReceivingAddressForTransactions() async { - if (info.otherData[WalletInfoKeys.reuseAddress] == true) { - try { - throw Exception(); - } catch (_, s) { - Logging.instance.log( - "checkReceivingAddressForTransactions called but reuse address flag set: $s", - level: LogLevel.Error, - ); - } - } - - try { - final currentReceiving = await getCurrentReceivingAddress(); - - final bool needsGenerate; - if (currentReceiving == null) { - // no addresses in db yet for some reason. - // Should not happen at this point... - - needsGenerate = true; - } else { - final txCount = await _fetchTxCount(address: currentReceiving); - needsGenerate = txCount > 0 || currentReceiving.derivationIndex < 0; - } - - if (needsGenerate) { - await generateNewReceivingAddress(); - - // TODO: [prio=low] Make sure we scan all addresses but only show one. - if (info.otherData[WalletInfoKeys.reuseAddress] != true) { - // TODO: get rid of this? Could cause problems (long loading/infinite loop or something) - // keep checking until address with no tx history is set as current - await checkReceivingAddressForTransactions(); - } - } - } catch (e, s) { - Logging.instance.log( - "Exception rethrown from _checkReceivingAddressForTransactions" - "($cryptoCurrency): $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } - @override Future generateNewChangeAddress() async { final current = await getCurrentChangeAddress(); @@ -839,76 +110,6 @@ class BIP48Wallet extends Wallet rethrow; } } - - await mainDB.updateOrPutAddresses([address]); - await info.updateReceivingAddress( - newAddress: address.value, - isar: mainDB.isar, - ); - } - - Future lookAhead() async { - Address? currentReceiving = await getCurrentReceivingAddress(); - if (currentReceiving == null) { - await generateNewReceivingAddress(); - currentReceiving = await getCurrentReceivingAddress(); - } - Address? currentChange = await getCurrentChangeAddress(); - if (currentChange == null) { - await generateNewChangeAddress(); - currentChange = await getCurrentChangeAddress(); - } - - final List
nextReceivingAddresses = []; - final List
nextChangeAddresses = []; - - int receiveIndex = currentReceiving!.derivationIndex; - int changeIndex = currentChange!.derivationIndex; - for (int i = 0; i < 10; i++) { - final receiveAddress = await _generateAddressSafe( - chain: 0, - startingIndex: receiveIndex + 1, - ); - receiveIndex = receiveAddress.derivationIndex; - nextReceivingAddresses.add(receiveAddress); - - final changeAddress = await _generateAddressSafe( - chain: 1, - startingIndex: changeIndex + 1, - ); - changeIndex = changeAddress.derivationIndex; - nextChangeAddresses.add(changeAddress); - } - - int activeReceiveIndex = currentReceiving.derivationIndex; - int activeChangeIndex = currentChange.derivationIndex; - for (final address in nextReceivingAddresses) { - final txCount = await _fetchTxCount(address: address); - if (txCount > 0) { - activeReceiveIndex = max(activeReceiveIndex, address.derivationIndex); - } - } - for (final address in nextChangeAddresses) { - final txCount = await _fetchTxCount(address: address); - if (txCount > 0) { - activeChangeIndex = max(activeChangeIndex, address.derivationIndex); - } - } - - nextReceivingAddresses - .removeWhere((e) => e.derivationIndex > activeReceiveIndex); - if (nextReceivingAddresses.isNotEmpty) { - await mainDB.updateOrPutAddresses(nextReceivingAddresses); - await info.updateReceivingAddress( - newAddress: nextReceivingAddresses.last.value, - isar: mainDB.isar, - ); - } - nextChangeAddresses - .removeWhere((e) => e.derivationIndex > activeChangeIndex); - if (nextChangeAddresses.isNotEmpty) { - await mainDB.updateOrPutAddresses(nextChangeAddresses); - } } Future
_generateAddressSafe({ @@ -930,61 +131,4 @@ class BIP48Wallet extends Wallet return address; } - - Future _fetchTxCount({required Address address}) async { - final transactions = await electrumXClient.getHistory( - scripthash: cryptoCurrency.addressToScriptHash( - address: address.value, - ), - ); - return transactions.length; - } - - Future> _fetchAddressesForElectrumXScan() async { - final allAddresses = await mainDB - .getAddresses(walletId) - .filter() - .not() - .group( - (q) => q - .typeEqualTo(AddressType.nonWallet) - .or() - .subTypeEqualTo(AddressSubType.nonWallet), - ) - .findAll(); - return allAddresses; - } - - Future>> _fetchHistory( - Iterable allAddresses, - ) async { - try { - final List> allTxHashes = []; - for (int i = 0; i < allAddresses.length; i++) { - final addressString = allAddresses.elementAt(i); - final scriptHash = cryptoCurrency.addressToScriptHash( - address: addressString, - ); - - final response = await electrumXClient.getHistory( - scripthash: scriptHash, - ); - - for (int j = 0; j < response.length; j++) { - response[j]["address"] = addressString; - if (!allTxHashes.contains(response[j])) { - allTxHashes.add(response[j]); - } - } - } - - return allTxHashes; - } catch (e, s) { - Logging.instance.log( - "$runtimeType._fetchHistory: $e\n$s", - level: LogLevel.Error, - ); - rethrow; - } - } }