mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 20:09:23 +00:00
add running setter and set running to false on exception in fuse
This commit is contained in:
parent
3df33ceb17
commit
9d001c3fba
2 changed files with 190 additions and 173 deletions
|
@ -149,6 +149,10 @@ class FusionProgressUIState extends ChangeNotifier {
|
||||||
/// A flag indicating that fusion is running.
|
/// A flag indicating that fusion is running.
|
||||||
bool _running = false;
|
bool _running = false;
|
||||||
bool get running => _running;
|
bool get running => _running;
|
||||||
|
set running(bool running) {
|
||||||
|
_running = running;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
/// A helper method for setting the running flag.
|
/// A helper method for setting the running flag.
|
||||||
///
|
///
|
||||||
|
|
|
@ -417,191 +417,204 @@ mixin FusionWalletInterface {
|
||||||
}) async {
|
}) async {
|
||||||
// Initial attempt for CashFusion integration goes here.
|
// Initial attempt for CashFusion integration goes here.
|
||||||
|
|
||||||
_updateStatus(status: fusion.FusionStatus.reset);
|
try {
|
||||||
_updateStatus(
|
_updateStatus(status: fusion.FusionStatus.reset);
|
||||||
status: fusion.FusionStatus.connecting,
|
_updateStatus(
|
||||||
info: "Connecting to the CashFusion server.",
|
status: fusion.FusionStatus.connecting,
|
||||||
);
|
info: "Connecting to the CashFusion server.",
|
||||||
|
);
|
||||||
|
|
||||||
// Use server host and port which ultimately come from text fields.
|
// Use server host and port which ultimately come from text fields.
|
||||||
fusion.FusionParams serverParams = fusion.FusionParams(
|
fusion.FusionParams serverParams = fusion.FusionParams(
|
||||||
serverHost: fusionInfo.host,
|
serverHost: fusionInfo.host,
|
||||||
serverPort: fusionInfo.port,
|
serverPort: fusionInfo.port,
|
||||||
serverSsl: fusionInfo.ssl,
|
serverSsl: fusionInfo.ssl,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Instantiate a Fusion object with custom parameters.
|
// Instantiate a Fusion object with custom parameters.
|
||||||
_mainFusionObject = fusion.Fusion(serverParams);
|
_mainFusionObject = fusion.Fusion(serverParams);
|
||||||
|
|
||||||
// Pass wallet functions to the Fusion object
|
// Pass wallet functions to the Fusion object
|
||||||
await _mainFusionObject!.initFusion(
|
await _mainFusionObject!.initFusion(
|
||||||
getTransactionsByAddress: _getTransactionsByAddress,
|
getTransactionsByAddress: _getTransactionsByAddress,
|
||||||
getUnusedReservedChangeAddresses: _getUnusedReservedChangeAddresses,
|
getUnusedReservedChangeAddresses: _getUnusedReservedChangeAddresses,
|
||||||
getSocksProxyAddress: _getSocksProxyAddress,
|
getSocksProxyAddress: _getSocksProxyAddress,
|
||||||
getChainHeight: _getChainHeight,
|
getChainHeight: _getChainHeight,
|
||||||
updateStatusCallback: _updateStatus,
|
updateStatusCallback: _updateStatus,
|
||||||
getTransactionJson: (String txid) async =>
|
getTransactionJson: (String txid) async =>
|
||||||
await _getWalletCachedElectrumX().getTransaction(
|
await _getWalletCachedElectrumX().getTransaction(
|
||||||
coin: _coin,
|
coin: _coin,
|
||||||
txHash: txid,
|
txHash: txid,
|
||||||
),
|
),
|
||||||
getPrivateKeyForPubKey: _getPrivateKeyForPubKey,
|
getPrivateKeyForPubKey: _getPrivateKeyForPubKey,
|
||||||
broadcastTransaction: (String txHex) => _getWalletCachedElectrumX()
|
broadcastTransaction: (String txHex) => _getWalletCachedElectrumX()
|
||||||
.electrumXClient
|
.electrumXClient
|
||||||
.broadcastTransaction(rawTx: txHex),
|
.broadcastTransaction(rawTx: txHex),
|
||||||
unReserveAddresses: (List<fusion.Address> addresses) async {
|
unReserveAddresses: (List<fusion.Address> addresses) async {
|
||||||
final List<Future<void>> futures = [];
|
final List<Future<void>> futures = [];
|
||||||
for (final addr in addresses) {
|
for (final addr in addresses) {
|
||||||
futures.add(
|
futures.add(
|
||||||
_db.getAddress(_walletId, addr.address).then(
|
_db.getAddress(_walletId, addr.address).then(
|
||||||
(address) async {
|
(address) async {
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
// matching address not found in db so cannot mark as unreserved
|
// matching address not found in db so cannot mark as unreserved
|
||||||
// just ignore I guess. Should never actually happen in practice.
|
// just ignore I guess. Should never actually happen in practice.
|
||||||
// Might be useful check in debugging cases?
|
// Might be useful check in debugging cases?
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
await _unReserveAddress(address);
|
await _unReserveAddress(address);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await Future.wait(futures);
|
await Future.wait(futures);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset internal and UI counts and flag.
|
// Reset internal and UI counts and flag.
|
||||||
_completedFuseCount = 0;
|
_completedFuseCount = 0;
|
||||||
_uiState?.fusionRoundsCompleted = 0;
|
_uiState?.fusionRoundsCompleted = 0;
|
||||||
_failedFuseCount = 0;
|
_failedFuseCount = 0;
|
||||||
_uiState?.fusionRoundsFailed = 0;
|
_uiState?.fusionRoundsFailed = 0;
|
||||||
_stopRequested = false;
|
_stopRequested = false;
|
||||||
|
|
||||||
bool shouldFuzeAgain() {
|
bool shouldFuzeAgain() {
|
||||||
if (fusionInfo.rounds <= 0) {
|
if (fusionInfo.rounds <= 0) {
|
||||||
// ignore count if continuous
|
// ignore count if continuous
|
||||||
return !_stopRequested;
|
return !_stopRequested;
|
||||||
} else {
|
|
||||||
// not continuous
|
|
||||||
// check to make sure we aren't doing more fusions than requested
|
|
||||||
return !_stopRequested && _completedFuseCount < fusionInfo.rounds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (shouldFuzeAgain()) {
|
|
||||||
if (_completedFuseCount > 0 || _failedFuseCount > 0) {
|
|
||||||
_updateStatus(status: fusion.FusionStatus.reset);
|
|
||||||
_updateStatus(
|
|
||||||
status: fusion.FusionStatus.connecting,
|
|
||||||
info: "Connecting to the CashFusion server.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// refresh wallet utxos
|
|
||||||
await _updateWalletUTXOS();
|
|
||||||
|
|
||||||
// Add unfrozen stack UTXOs.
|
|
||||||
final List<UTXO> walletUtxos = await _db
|
|
||||||
.getUTXOs(_walletId)
|
|
||||||
.filter()
|
|
||||||
.isBlockedEqualTo(false)
|
|
||||||
.and()
|
|
||||||
.addressIsNotNull()
|
|
||||||
.findAll();
|
|
||||||
|
|
||||||
final List<fusion.UtxoDTO> coinList = [];
|
|
||||||
// Loop through UTXOs, checking and adding valid ones.
|
|
||||||
for (final utxo in walletUtxos) {
|
|
||||||
final String addressString = utxo.address!;
|
|
||||||
final List<String> possibleAddresses = [addressString];
|
|
||||||
|
|
||||||
if (bitbox.Address.detectFormat(addressString) ==
|
|
||||||
bitbox.Address.formatCashAddr) {
|
|
||||||
possibleAddresses.add(bitbox.Address.toLegacyAddress(addressString));
|
|
||||||
} else {
|
} else {
|
||||||
possibleAddresses.add(bitbox.Address.toCashAddress(addressString));
|
// not continuous
|
||||||
|
// check to make sure we aren't doing more fusions than requested
|
||||||
|
return !_stopRequested && _completedFuseCount < fusionInfo.rounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch address to get pubkey
|
|
||||||
final addr = await _db
|
|
||||||
.getAddresses(_walletId)
|
|
||||||
.filter()
|
|
||||||
.anyOf<String,
|
|
||||||
QueryBuilder<Address, Address, QAfterFilterCondition>>(
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add UTXO to coinList.
|
|
||||||
coinList.add(dto);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fuse UTXOs.
|
while (shouldFuzeAgain()) {
|
||||||
try {
|
if (_completedFuseCount > 0 || _failedFuseCount > 0) {
|
||||||
await _mainFusionObject!.fuse(
|
_updateStatus(status: fusion.FusionStatus.reset);
|
||||||
inputsFromWallet: coinList,
|
|
||||||
network: _coin.isTestNet
|
|
||||||
? fusion.Utilities.testNet
|
|
||||||
: fusion.Utilities.mainNet,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Increment the number of successfully completed fusion rounds.
|
|
||||||
_completedFuseCount++;
|
|
||||||
|
|
||||||
// Do the same for the UI state. This also resets the failed count (for
|
|
||||||
// the UI state only).
|
|
||||||
_uiState?.incrementFusionRoundsCompleted();
|
|
||||||
|
|
||||||
// Also reset the failed count here.
|
|
||||||
_failedFuseCount = 0;
|
|
||||||
} catch (e, s) {
|
|
||||||
Logging.instance.log(
|
|
||||||
"$e\n$s",
|
|
||||||
level: LogLevel.Error,
|
|
||||||
);
|
|
||||||
// just continue on attempt failure
|
|
||||||
|
|
||||||
// Increment the number of failed fusion rounds.
|
|
||||||
_failedFuseCount++;
|
|
||||||
|
|
||||||
// Do the same for the UI state.
|
|
||||||
_uiState?.incrementFusionRoundsFailed();
|
|
||||||
|
|
||||||
// If we fail too many times in a row, stop trying.
|
|
||||||
if (_failedFuseCount >= maxFailedFuseCount) {
|
|
||||||
_updateStatus(
|
_updateStatus(
|
||||||
status: fusion.FusionStatus.failed,
|
status: fusion.FusionStatus.connecting,
|
||||||
info: "Failed $maxFailedFuseCount times in a row, stopping.");
|
info: "Connecting to the CashFusion server.",
|
||||||
_stopRequested = true;
|
);
|
||||||
_uiState?.failed = true;
|
}
|
||||||
|
|
||||||
|
// refresh wallet utxos
|
||||||
|
await _updateWalletUTXOS();
|
||||||
|
|
||||||
|
// Add unfrozen stack UTXOs.
|
||||||
|
final List<UTXO> walletUtxos = await _db
|
||||||
|
.getUTXOs(_walletId)
|
||||||
|
.filter()
|
||||||
|
.isBlockedEqualTo(false)
|
||||||
|
.and()
|
||||||
|
.addressIsNotNull()
|
||||||
|
.findAll();
|
||||||
|
|
||||||
|
final List<fusion.UtxoDTO> coinList = [];
|
||||||
|
// Loop through UTXOs, checking and adding valid ones.
|
||||||
|
for (final utxo in walletUtxos) {
|
||||||
|
final String addressString = utxo.address!;
|
||||||
|
final List<String> 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<String,
|
||||||
|
QueryBuilder<Address, Address, QAfterFilterCondition>>(
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add UTXO to coinList.
|
||||||
|
coinList.add(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuse UTXOs.
|
||||||
|
try {
|
||||||
|
await _mainFusionObject!.fuse(
|
||||||
|
inputsFromWallet: coinList,
|
||||||
|
network: _coin.isTestNet
|
||||||
|
? fusion.Utilities.testNet
|
||||||
|
: fusion.Utilities.mainNet,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Increment the number of successfully completed fusion rounds.
|
||||||
|
_completedFuseCount++;
|
||||||
|
|
||||||
|
// Do the same for the UI state. This also resets the failed count (for
|
||||||
|
// the UI state only).
|
||||||
|
_uiState?.incrementFusionRoundsCompleted();
|
||||||
|
|
||||||
|
// Also reset the failed count here.
|
||||||
|
_failedFuseCount = 0;
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
// just continue on attempt failure
|
||||||
|
|
||||||
|
// Increment the number of failed fusion rounds.
|
||||||
|
_failedFuseCount++;
|
||||||
|
|
||||||
|
// Do the same for the UI state.
|
||||||
|
_uiState?.incrementFusionRoundsFailed();
|
||||||
|
|
||||||
|
// If we fail too many times in a row, stop trying.
|
||||||
|
if (_failedFuseCount >= maxFailedFuseCount) {
|
||||||
|
_updateStatus(
|
||||||
|
status: fusion.FusionStatus.failed,
|
||||||
|
info: "Failed $maxFailedFuseCount times in a row, stopping.");
|
||||||
|
_stopRequested = true;
|
||||||
|
_uiState?.failed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"$e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stop the fusion process and update the UI state.
|
||||||
|
await _mainFusionObject?.stop();
|
||||||
|
_mainFusionObject = null;
|
||||||
|
_uiState?.running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue