From 7a59f6020eea7b16a24142774d7b6900a001f965 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 18 Oct 2023 12:44:05 -0600 Subject: [PATCH] do fusion runs with a new set of updated wallet UTXOs --- .../coins/bitcoincash/bitcoincash_wallet.dart | 1 + .../mixins/fusion_wallet_interface.dart | 134 ++++++++++-------- 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart index e9dd38d54..e4a0125e0 100644 --- a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart +++ b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart @@ -142,6 +142,7 @@ class BitcoinCashWallet extends CoinServiceAPI getWalletCachedElectrumX: () => cachedElectrumXClient, getNextUnusedChangeAddress: _getUnusedChangeAddresses, getChainHeight: () async => chainHeight, + updateWalletUTXOS: _updateUTXOs, mnemonic: mnemonicString, mnemonicPassphrase: mnemonicPassphrase, network: _network, diff --git a/lib/services/mixins/fusion_wallet_interface.dart b/lib/services/mixins/fusion_wallet_interface.dart index 7d4841b5b..b1fa0f33d 100644 --- a/lib/services/mixins/fusion_wallet_interface.dart +++ b/lib/services/mixins/fusion_wallet_interface.dart @@ -123,9 +123,11 @@ mixin FusionWalletInterface { _getNextUnusedChangeAddresses; late final CachedElectrumX Function() _getWalletCachedElectrumX; late final Future Function() _getChainHeight; + late final Future Function() _updateWalletUTXOS; // Fusion object. fusion.Fusion? _mainFusionObject; + bool _stopRequested = false; /// Initializes the FusionWalletInterface mixin. /// @@ -138,6 +140,7 @@ mixin FusionWalletInterface { getNextUnusedChangeAddress, required CachedElectrumX Function() getWalletCachedElectrumX, required Future Function() getChainHeight, + required Future Function() updateWalletUTXOS, required Future mnemonic, required Future mnemonicPassphrase, required btcdart.NetworkType network, @@ -150,6 +153,7 @@ mixin FusionWalletInterface { _torService = FusionTorService.sharedInstance; _getWalletCachedElectrumX = getWalletCachedElectrumX; _getChainHeight = getChainHeight; + _updateWalletUTXOS = updateWalletUTXOS; _mnemonic = mnemonic; _mnemonicPassphrase = mnemonicPassphrase; _network = network; @@ -409,7 +413,6 @@ mixin FusionWalletInterface { serverHost: fusionInfo.host, serverPort: fusionInfo.port, serverSsl: fusionInfo.ssl, - roundCount: fusionInfo.rounds, ); // Instantiate a Fusion object with custom parameters. @@ -453,78 +456,84 @@ mixin FusionWalletInterface { }, ); - // Add unfrozen stack UTXOs. - final List walletUtxos = await _db - .getUTXOs(_walletId) - .filter() - .isBlockedEqualTo(false) - .and() - .addressIsNotNull() - .findAll(); - final List coinList = []; + int fuzeCount = fusionInfo.rounds; + _stopRequested = false; - // Loop through UTXOs, checking and adding valid ones. - for (final utxo in walletUtxos) { - final String addressString = utxo.address!; - final List possibleAddresses = [addressString]; + while (fuzeCount > 0 && !_stopRequested) { + fuzeCount--; - if (bitbox.Address.detectFormat(addressString) == - bitbox.Address.formatCashAddr) { - possibleAddresses.add(bitbox.Address.toLegacyAddress(addressString)); - } else { - possibleAddresses.add(bitbox.Address.toCashAddress(addressString)); - } + // refresh wallet utxos + await _updateWalletUTXOS(); - // Fetch address to get pubkey - final addr = await _db - .getAddresses(_walletId) + // Add unfrozen stack UTXOs. + final List walletUtxos = await _db + .getUTXOs(_walletId) .filter() - .anyOf>( - possibleAddresses, (q, e) => q.valueEqualTo(e)) + .isBlockedEqualTo(false) .and() - .group((q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving)) - .and() - .typeEqualTo(AddressType.p2pkh) - .findFirst(); + .addressIsNotNull() + .findAll(); - // depending on the address type in the query above this can be null - if (addr == null) { - // A utxo object should always have a non null address. - // If non found then just ignore the UTXO (aka don't fuse it) - Logging.instance.log( - "Ignoring utxo=$utxo for address=\"$addressString\" while selecting UTXOs for Fusion", - level: LogLevel.Info, + final List coinList = []; + // Loop through UTXOs, checking and adding valid ones. + for (final utxo in walletUtxos) { + final String addressString = utxo.address!; + final List possibleAddresses = [addressString]; + + if (bitbox.Address.detectFormat(addressString) == + bitbox.Address.formatCashAddr) { + possibleAddresses.add(bitbox.Address.toLegacyAddress(addressString)); + } else { + possibleAddresses.add(bitbox.Address.toCashAddress(addressString)); + } + + // Fetch address to get pubkey + final addr = await _db + .getAddresses(_walletId) + .filter() + .anyOf>( + possibleAddresses, (q, e) => q.valueEqualTo(e)) + .and() + .group((q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving)) + .and() + .typeEqualTo(AddressType.p2pkh) + .findFirst(); + + // depending on the address type in the query above this can be null + if (addr == null) { + // A utxo object should always have a non null address. + // If non found then just ignore the UTXO (aka don't fuse it) + Logging.instance.log( + "Ignoring utxo=$utxo for address=\"$addressString\" while selecting UTXOs for Fusion", + level: LogLevel.Info, + ); + continue; + } + + final dto = fusion.UtxoDTO( + txid: utxo.txid, + vout: utxo.vout, + value: utxo.value, + address: utxo.address!, + pubKey: addr.publicKey, ); - continue; + + // Add UTXO to coinList. + coinList.add(dto); } - final dto = fusion.UtxoDTO( - txid: utxo.txid, - vout: utxo.vout, - value: utxo.value, - address: utxo.address!, - pubKey: addr.publicKey, + // Fuse UTXOs. + await _mainFusionObject!.fuse( + inputsFromWallet: coinList, + network: _coin.isTestNet + ? fusion.Utilities.testNet + : fusion.Utilities.mainNet, ); - - // Add UTXO to coinList. - coinList.add(dto); } - - // Fuse UTXOs. - return await _mainFusionObject!.fuse( - inputsFromWallet: coinList, - network: - _coin.isTestNet ? fusion.Utilities.testNet : fusion.Utilities.mainNet, - ); - } - - Future refreshFusion() { - // TODO - throw UnimplementedError( - "TODO refreshFusion eg look up number of fusion participants connected/coordinating"); } /// Stop the fusion process. @@ -532,6 +541,7 @@ mixin FusionWalletInterface { /// This function is called when the user taps the "Cancel" button in the UI /// or closes the fusion progress dialog. Future stop() async { + _stopRequested = true; await _mainFusionObject?.stop(); } }