recount mixes if mix data unavailable, correct mix status cell to remove mix progress from non-mixing utxos, show registered inputs total in tooltip

This commit is contained in:
Craig Raw 2021-10-25 12:25:18 +02:00
parent 2a0412320a
commit 4b39316821
10 changed files with 231 additions and 28 deletions

View file

@ -10,8 +10,10 @@ import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.UtxoEntry; import com.sparrowwallet.sparrow.wallet.UtxoEntry;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolException; import com.sparrowwallet.sparrow.whirlpool.WhirlpoolException;
import javafx.animation.FadeTransition;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.util.Duration;
import org.controlsfx.glyphfont.Glyph; import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.tools.Platform; import org.controlsfx.tools.Platform;
@ -46,7 +48,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
} else if(mixStatus.getMixFailReason() != null) { } else if(mixStatus.getMixFailReason() != null) {
setMixFail(mixStatus.getMixFailReason(), mixStatus.getMixError()); setMixFail(mixStatus.getMixFailReason(), mixStatus.getMixError());
} else if(mixStatus.getMixProgress() != null) { } else if(mixStatus.getMixProgress() != null) {
setMixProgress(mixStatus.getMixProgress()); setMixProgress(mixStatus.getUtxoEntry(), mixStatus.getMixProgress());
} else { } else {
setGraphic(null); setGraphic(null);
setTooltip(null); setTooltip(null);
@ -65,13 +67,28 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
private void setMixFail(MixFailReason mixFailReason, String mixError) { private void setMixFail(MixFailReason mixFailReason, String mixError) {
if(mixFailReason != MixFailReason.CANCEL) { if(mixFailReason != MixFailReason.CANCEL) {
setGraphic(getFailGlyph()); if(getGraphic() != null && getGraphic().getUserData() == mixFailReason) {
//Fade transition already set
return;
}
Glyph failGlyph = getFailGlyph();
setGraphic(failGlyph);
Tooltip tt = new Tooltip(); Tooltip tt = new Tooltip();
tt.setText(mixFailReason.getMessage() + (mixError == null ? "" : ": " + mixError) + tt.setText(mixFailReason.getMessage() + (mixError == null ? "" : ": " + mixError) +
"\nMix failures are generally caused by peers disconnecting during a mix." + "\nMix failures are generally caused by peers disconnecting during a mix." +
"\nMake sure your internet connection is stable and the computer is configured to prevent sleeping." + "\nMake sure your internet connection is stable and the computer is configured to prevent sleeping." +
"\nTo prevent sleeping, use the " + getPlatformSleepConfig() + " or enable the function in the Tools menu."); "\nTo prevent sleeping, use the " + getPlatformSleepConfig() + " or enable the function in the Tools menu.");
setTooltip(tt); setTooltip(tt);
FadeTransition ft = new FadeTransition(Duration.hours(1), failGlyph);
ft.setFromValue(1);
ft.setToValue(0);
ft.setOnFinished(event -> {
setTooltip(null);
});
ft.play();
failGlyph.setUserData(mixFailReason);
} else { } else {
setGraphic(null); setGraphic(null);
setTooltip(null); setTooltip(null);
@ -89,14 +106,28 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
return "system power settings"; return "system power settings";
} }
private void setMixProgress(MixProgress mixProgress) { private void setMixProgress(UtxoEntry utxoEntry, MixProgress mixProgress) {
if(mixProgress.getMixStep() != MixStep.FAIL) { if(mixProgress.getMixStep() != MixStep.FAIL) {
ProgressIndicator progressIndicator = getProgressIndicator(); ProgressIndicator progressIndicator = getProgressIndicator();
progressIndicator.setProgress(mixProgress.getMixStep().getProgressPercent() == 100 ? -1 : mixProgress.getMixStep().getProgressPercent() / 100.0); progressIndicator.setProgress(mixProgress.getMixStep().getProgressPercent() == 100 ? -1 : mixProgress.getMixStep().getProgressPercent() / 100.0);
setGraphic(progressIndicator); setGraphic(progressIndicator);
Tooltip tt = new Tooltip(); Tooltip tt = new Tooltip();
tt.setText(mixProgress.getMixStep().getMessage().substring(0, 1).toUpperCase() + mixProgress.getMixStep().getMessage().substring(1)); String status = mixProgress.getMixStep().getMessage().substring(0, 1).toUpperCase() + mixProgress.getMixStep().getMessage().substring(1);
tt.setText(status);
setTooltip(tt); setTooltip(tt);
if(mixProgress.getMixStep() == MixStep.REGISTERED_INPUT) {
tt.setOnShowing(event -> {
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(utxoEntry.getWallet());
Whirlpool.RegisteredInputsService registeredInputsService = new Whirlpool.RegisteredInputsService(whirlpool, mixProgress.getPoolId());
registeredInputsService.setOnSucceeded(eventStateHandler -> {
if(registeredInputsService.getValue() != null) {
tt.setText(status + " (1 of " + registeredInputsService.getValue() + ")");
}
});
registeredInputsService.start();
});
}
} else { } else {
setGraphic(null); setGraphic(null);
setTooltip(null); setTooltip(null);

View file

@ -1,6 +1,7 @@
package com.sparrowwallet.sparrow.io; package com.sparrowwallet.sparrow.io;
import com.google.gson.*; import com.google.gson.*;
import com.samourai.whirlpool.client.wallet.beans.IndexRange;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.sparrow.Mode; import com.sparrowwallet.sparrow.Mode;
import com.sparrowwallet.sparrow.Theme; import com.sparrowwallet.sparrow.Theme;
@ -40,9 +41,10 @@ public class Config {
private boolean openWalletsInNewWindows = false; private boolean openWalletsInNewWindows = false;
private boolean hideEmptyUsedAddresses = false; private boolean hideEmptyUsedAddresses = false;
private boolean showTransactionHex = true; private boolean showTransactionHex = true;
private boolean showLoadingLog = false; private boolean showLoadingLog = true;
private boolean showUtxosChart = true; private boolean showUtxosChart = true;
private boolean preventSleep = false; private boolean preventSleep = false;
private IndexRange postmixIndexRange = IndexRange.FULL;
private List<File> recentWalletFiles; private List<File> recentWalletFiles;
private Integer keyDerivationPeriod; private Integer keyDerivationPeriod;
private File hwi; private File hwi;
@ -288,6 +290,15 @@ public class Config {
this.preventSleep = preventSleep; this.preventSleep = preventSleep;
} }
public IndexRange getPostmixIndexRange() {
return postmixIndexRange;
}
public void setPostmixIndexRange(IndexRange postmixIndexRange) {
this.postmixIndexRange = postmixIndexRange;
flush();
}
public List<File> getRecentWalletFiles() { public List<File> getRecentWalletFiles() {
return recentWalletFiles; return recentWalletFiles;
} }

View file

@ -4,7 +4,6 @@ import com.google.common.eventbus.Subscribe;
import com.samourai.whirlpool.client.whirlpool.beans.Pool; import com.samourai.whirlpool.client.whirlpool.beans.Pool;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.SecureString;
import com.sparrowwallet.drongo.address.InvalidAddressException; import com.sparrowwallet.drongo.address.InvalidAddressException;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;

View file

@ -4,6 +4,7 @@ import com.samourai.whirlpool.client.mix.listener.MixFailReason;
import com.samourai.whirlpool.client.mix.listener.MixStep; import com.samourai.whirlpool.client.mix.listener.MixStep;
import com.samourai.whirlpool.client.wallet.beans.MixProgress; import com.samourai.whirlpool.client.wallet.beans.MixProgress;
import com.samourai.whirlpool.protocol.beans.Utxo; import com.samourai.whirlpool.protocol.beans.Utxo;
import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
@ -151,12 +152,12 @@ public class UtxoEntry extends HashIndexEntry {
return wallet.getUtxoMixData(getHashIndex()); return wallet.getUtxoMixData(getHashIndex());
} }
//Mix data not available - recount (and store if WhirlpoolWallet is running)
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(wallet); Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(wallet);
if(whirlpool != null) { if(whirlpool != null && getUtxoEntry().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX && node.getKeyPurpose() == KeyPurpose.RECEIVE) {
UtxoMixData utxoMixData = whirlpool.getMixData(getHashIndex()); int mixesDone = whirlpool.recountMixesDone(getUtxoEntry().getWallet(), getHashIndex());
if(utxoMixData != null) { whirlpool.setMixesDone(getHashIndex(), mixesDone);
return utxoMixData; return new UtxoMixData(mixesDone, null);
}
} }
return new UtxoMixData(getUtxoEntry().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX ? 1 : 0, null); return new UtxoMixData(getUtxoEntry().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX ? 1 : 0, null);

View file

@ -502,6 +502,10 @@ public class UtxosController extends WalletFormController implements Initializab
utxosTable.updateHistoryStatus(event); utxosTable.updateHistoryStatus(event);
} }
@Subscribe
public void newBlock(NewBlockEvent event) {
getWalletForm().getWalletUtxosEntry().updateMixProgress();
}
@Subscribe @Subscribe
public void bwtSyncStatus(BwtSyncStatusEvent event) { public void bwtSyncStatus(BwtSyncStatusEvent event) {

View file

@ -12,7 +12,7 @@ public class WalletUtxosEntry extends Entry {
public WalletUtxosEntry(Wallet wallet) { public WalletUtxosEntry(Wallet wallet) {
super(wallet, wallet.getName(), wallet.getWalletUtxos().entrySet().stream().map(entry -> new UtxoEntry(wallet, entry.getKey(), HashIndexEntry.Type.OUTPUT, entry.getValue())).collect(Collectors.toList())); super(wallet, wallet.getName(), wallet.getWalletUtxos().entrySet().stream().map(entry -> new UtxoEntry(wallet, entry.getKey(), HashIndexEntry.Type.OUTPUT, entry.getValue())).collect(Collectors.toList()));
calculateDuplicates(); calculateDuplicates();
retrieveMixProgress(); updateMixProgress();
} }
@Override @Override
@ -38,16 +38,18 @@ public class WalletUtxosEntry extends Entry {
} }
} }
protected void retrieveMixProgress() { public void updateMixProgress() {
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWallet()); Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(getWallet());
if(whirlpool != null) { if(whirlpool != null) {
for(Entry entry : getChildren()) { for(Entry entry : getChildren()) {
UtxoEntry utxoEntry = (UtxoEntry)entry; UtxoEntry utxoEntry = (UtxoEntry)entry;
MixProgress mixProgress = whirlpool.getMixProgress(utxoEntry.getHashIndex()); MixProgress mixProgress = whirlpool.getMixProgress(utxoEntry.getHashIndex());
if(mixProgress != null || utxoEntry.getMixStatus() == null || (utxoEntry.getMixStatus().getMixFailReason() == null && utxoEntry.getMixStatus().getNextMixUtxo() == null)) {
utxoEntry.setMixProgress(mixProgress); utxoEntry.setMixProgress(mixProgress);
} }
} }
} }
}
public void updateUtxos() { public void updateUtxos() {
List<Entry> current = getWallet().getWalletUtxos().entrySet().stream().map(entry -> new UtxoEntry(getWallet(), entry.getKey(), HashIndexEntry.Type.OUTPUT, entry.getValue())).collect(Collectors.toList()); List<Entry> current = getWallet().getWalletUtxos().entrySet().stream().map(entry -> new UtxoEntry(getWallet(), entry.getKey(), HashIndexEntry.Type.OUTPUT, entry.getValue())).collect(Collectors.toList());
@ -62,6 +64,6 @@ public class WalletUtxosEntry extends Entry {
getChildren().removeAll(entriesRemoved); getChildren().removeAll(entriesRemoved);
calculateDuplicates(); calculateDuplicates();
retrieveMixProgress(); updateMixProgress();
} }
} }

View file

@ -25,18 +25,15 @@ import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Network;
import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.protocol.*;
import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.nightjar.http.JavaHttpClientService; import com.sparrowwallet.nightjar.http.JavaHttpClientService;
import com.sparrowwallet.nightjar.stomp.JavaStompClientService; import com.sparrowwallet.nightjar.stomp.JavaStompClientService;
import com.sparrowwallet.nightjar.tor.WhirlpoolTorClientService;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WhirlpoolMixEvent; import com.sparrowwallet.sparrow.event.WhirlpoolMixEvent;
import com.sparrowwallet.sparrow.event.WhirlpoolMixSuccessEvent; import com.sparrowwallet.sparrow.event.WhirlpoolMixSuccessEvent;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.UtxoEntry; import com.sparrowwallet.sparrow.wallet.UtxoEntry;
import com.sparrowwallet.sparrow.whirlpool.dataPersister.SparrowDataPersister; import com.sparrowwallet.sparrow.whirlpool.dataPersister.SparrowDataPersister;
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource;
@ -49,6 +46,7 @@ import javafx.beans.property.SimpleBooleanProperty;
import javafx.concurrent.ScheduledService; import javafx.concurrent.ScheduledService;
import javafx.concurrent.Service; import javafx.concurrent.Service;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.util.Duration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -75,6 +73,7 @@ public class Whirlpool {
private HD_Wallet hdWallet; private HD_Wallet hdWallet;
private String walletId; private String walletId;
private String mixToWalletId; private String mixToWalletId;
private boolean resyncMixesDone;
private StartupService startupService; private StartupService startupService;
@ -95,10 +94,14 @@ public class Whirlpool {
this.tx0Service = new Tx0Service(config); this.tx0Service = new Tx0Service(config);
WhirlpoolEventService.getInstance().register(this); WhirlpoolEventService.getInstance().register(this);
StatusReporterService statusReporterService = new StatusReporterService(this);
statusReporterService.setPeriod(Duration.minutes(1));
statusReporterService.start();
} }
private WhirlpoolWalletConfig computeWhirlpoolWalletConfig(HostAndPort torProxy) { private WhirlpoolWalletConfig computeWhirlpoolWalletConfig(HostAndPort torProxy) {
DataPersisterFactory dataPersisterFactory = (whirlpoolWallet, bip44w) -> new SparrowDataPersister(whirlpoolWallet); DataPersisterFactory dataPersisterFactory = (whirlpoolWallet, bip44w) -> new SparrowDataPersister(whirlpoolWallet, config.getPersistDelaySeconds());
DataSourceFactory dataSourceFactory = (whirlpoolWallet, bip44w, dataPersister) -> new SparrowDataSource(whirlpoolWallet, bip44w, dataPersister); DataSourceFactory dataSourceFactory = (whirlpoolWallet, bip44w, dataPersister) -> new SparrowDataSource(whirlpoolWallet, bip44w, dataPersister);
boolean onion = (torProxy != null); boolean onion = (torProxy != null);
@ -108,9 +111,20 @@ public class Whirlpool {
WhirlpoolWalletConfig whirlpoolWalletConfig = new WhirlpoolWalletConfig(dataSourceFactory, httpClientService, stompClientService, torClientService, serverApi, whirlpoolServer.getParams(), false); WhirlpoolWalletConfig whirlpoolWalletConfig = new WhirlpoolWalletConfig(dataSourceFactory, httpClientService, stompClientService, torClientService, serverApi, whirlpoolServer.getParams(), false);
whirlpoolWalletConfig.setDataPersisterFactory(dataPersisterFactory); whirlpoolWalletConfig.setDataPersisterFactory(dataPersisterFactory);
whirlpoolWalletConfig.setPartner("SPARROW"); whirlpoolWalletConfig.setPartner("SPARROW");
whirlpoolWalletConfig.setIndexRangePostmix(Config.get().getPostmixIndexRange());
return whirlpoolWalletConfig; return whirlpoolWalletConfig;
} }
public Pool getPool(String poolId) {
try {
return getPools(null).stream().filter(pool -> pool.getPoolId().equals(poolId)).findFirst().orElse(null);
} catch(Exception e) {
log.error("Error retrieving pools", e);
}
return null;
}
public Collection<Pool> getPools(Long totalUtxoValue) throws Exception { public Collection<Pool> getPools(Long totalUtxoValue) throws Exception {
this.poolSupplier.load(); this.poolSupplier.load();
if(totalUtxoValue == null) { if(totalUtxoValue == null) {
@ -233,18 +247,73 @@ public class Whirlpool {
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(utxo.getHashAsString(), (int)utxo.getIndex()); WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(utxo.getHashAsString(), (int)utxo.getIndex());
if(whirlpoolUtxo != null && whirlpoolUtxo.getUtxoState() != null) { if(whirlpoolUtxo != null && whirlpoolUtxo.getUtxoState() != null) {
return whirlpoolUtxo.getUtxoState().getMixProgress(); MixProgress mixProgress = whirlpoolUtxo.getUtxoState().getMixProgress();
if(mixProgress != null && !isMixing(utxo)) {
log.debug("Utxo " + utxo + " mix state is " + whirlpoolUtxo.getUtxoState() + " but utxo is not mixing");
return null;
}
return mixProgress;
} }
return null; return null;
} }
private boolean isMixing(BlockTransactionHashIndex utxo) {
if(whirlpoolWalletService.whirlpoolWallet() == null || !whirlpoolWalletService.whirlpoolWallet().isStarted()) {
return false;
}
return whirlpoolWalletService.whirlpoolWallet().getMixingState().getUtxosMixing().stream().map(WhirlpoolUtxo::getUtxo).anyMatch(uo -> uo.tx_hash.equals(utxo.getHashAsString()) && uo.tx_output_n == (int)utxo.getIndex());
}
public void refreshUtxos() { public void refreshUtxos() {
if(whirlpoolWalletService.whirlpoolWallet() != null) { if(whirlpoolWalletService.whirlpoolWallet() != null) {
whirlpoolWalletService.whirlpoolWallet().refreshUtxos(); whirlpoolWalletService.whirlpoolWallet().refreshUtxos();
} }
} }
private void resyncMixesDone(Whirlpool whirlpool, Wallet postmixWallet) {
Set<BlockTransactionHashIndex> receiveUtxos = postmixWallet.getWalletUtxos().entrySet().stream()
.filter(entry -> entry.getValue().getKeyPurpose() == KeyPurpose.RECEIVE).map(Map.Entry::getKey).collect(Collectors.toSet());
for(BlockTransactionHashIndex utxo : receiveUtxos) {
int mixesDone = recountMixesDone(postmixWallet, utxo);
whirlpool.setMixesDone(utxo, mixesDone);
}
}
public int recountMixesDone(Wallet postmixWallet, BlockTransactionHashIndex postmixUtxo) {
int mixesDone = 0;
Set<BlockTransactionHashIndex> walletTxos = postmixWallet.getWalletTxos().entrySet().stream()
.filter(entry -> entry.getValue().getKeyPurpose() == KeyPurpose.RECEIVE).map(Map.Entry::getKey).collect(Collectors.toSet());
BlockTransaction blkTx = postmixWallet.getTransactions().get(postmixUtxo.getHash());
while(blkTx != null) {
mixesDone++;
List<TransactionInput> inputs = blkTx.getTransaction().getInputs();
blkTx = null;
for(TransactionInput txInput : inputs) {
BlockTransaction inputTx = postmixWallet.getTransactions().get(txInput.getOutpoint().getHash());
if(inputTx != null && walletTxos.stream().anyMatch(txo -> txo.getHash().equals(inputTx.getHash()) && txo.getIndex() == txInput.getOutpoint().getIndex()) && inputTx.getTransaction() != null) {
blkTx = inputTx;
}
}
}
return mixesDone;
}
public void setMixesDone(BlockTransactionHashIndex utxo, int mixesDone) {
if(whirlpoolWalletService.whirlpoolWallet() == null) {
return;
}
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(utxo.getHashAsString(), (int)utxo.getIndex());
if(whirlpoolUtxo != null) {
whirlpoolUtxo.setMixsDone(mixesDone);
}
}
public boolean hasWallet() { public boolean hasWallet() {
return hdWallet != null; return hdWallet != null;
} }
@ -385,10 +454,6 @@ public class Whirlpool {
config.setScode(scode); config.setScode(scode);
} }
public Tx0FeeTarget getTx0FeeTarget() {
return tx0FeeTarget;
}
public void setTx0FeeTarget(Tx0FeeTarget tx0FeeTarget) { public void setTx0FeeTarget(Tx0FeeTarget tx0FeeTarget) {
this.tx0FeeTarget = tx0FeeTarget; this.tx0FeeTarget = tx0FeeTarget;
} }
@ -401,6 +466,10 @@ public class Whirlpool {
return mixToWalletId; return mixToWalletId;
} }
public void setResyncMixesDone(boolean resyncMixesDone) {
this.resyncMixesDone = resyncMixesDone;
}
public void setMixToWallet(String mixToWalletId, Integer minMixes) { public void setMixToWallet(String mixToWalletId, Integer minMixes) {
if(mixToWalletId == null) { if(mixToWalletId == null) {
config.setExternalDestination(null); config.setExternalDestination(null);
@ -488,6 +557,15 @@ public class Whirlpool {
if(e.getWhirlpoolWallet() == whirlpoolWalletService.whirlpoolWallet()) { if(e.getWhirlpoolWallet() == whirlpoolWalletService.whirlpoolWallet()) {
log.info("Mixing to " + e.getWhirlpoolWallet().getConfig().getExternalDestination()); log.info("Mixing to " + e.getWhirlpoolWallet().getConfig().getExternalDestination());
mixingProperty.set(true); mixingProperty.set(true);
if(resyncMixesDone) {
Wallet wallet = AppServices.get().getWallet(walletId);
if(wallet != null) {
Wallet postmixWallet = getStandardAccountWallet(WhirlpoolAccount.POSTMIX, wallet);
resyncMixesDone(this, postmixWallet);
resyncMixesDone = false;
}
}
} }
} }
@ -632,6 +710,54 @@ public class Whirlpool {
} }
} }
public static class RegisteredInputsService extends Service<Integer> {
private final Whirlpool whirlpool;
private final String poolId;
public RegisteredInputsService(Whirlpool whirlpool, String poolId) {
this.whirlpool = whirlpool;
this.poolId = poolId;
}
@Override
protected Task<Integer> createTask() {
return new Task<>() {
protected Integer call() {
Pool pool = whirlpool.getPool(poolId);
if(pool != null) {
return pool.getNbRegistered();
}
return null;
}
};
}
}
private static class StatusReporterService extends ScheduledService<Boolean> {
private final Whirlpool whirlpool;
public StatusReporterService(Whirlpool whirlpool) {
this.whirlpool = whirlpool;
}
@Override
protected Task<Boolean> createTask() {
return new Task<>() {
protected Boolean call() throws Exception {
if(whirlpool.mixingProperty().get()) {
WhirlpoolWallet whirlpoolWallet = whirlpool.getWhirlpoolWallet();
log.debug(whirlpool.walletId + ": " + whirlpoolWallet.getMixingState());
} else {
log.debug(whirlpool.walletId + ": Not mixing");
}
return true;
}
};
}
}
public static class WalletUtxo { public static class WalletUtxo {
public final Wallet wallet; public final Wallet wallet;
public final BlockTransactionHashIndex utxo; public final BlockTransactionHashIndex utxo;

View file

@ -156,6 +156,7 @@ public class WhirlpoolServices {
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId); Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId);
whirlpool.setScode(decryptedWallet.getMasterMixConfig().getScode()); whirlpool.setScode(decryptedWallet.getMasterMixConfig().getScode());
whirlpool.setHDWallet(walletId, decryptedWallet); whirlpool.setHDWallet(walletId, decryptedWallet);
whirlpool.setResyncMixesDone(true);
for(StandardAccount whirlpoolAccount : StandardAccount.WHIRLPOOL_ACCOUNTS) { for(StandardAccount whirlpoolAccount : StandardAccount.WHIRLPOOL_ACCOUNTS) {
if(decryptedWallet.getChildWallet(whirlpoolAccount) == null) { if(decryptedWallet.getChildWallet(whirlpoolAccount) == null) {

View file

@ -1,5 +1,6 @@
package com.sparrowwallet.sparrow.whirlpool.dataPersister; package com.sparrowwallet.sparrow.whirlpool.dataPersister;
import com.samourai.wallet.util.AbstractOrchestrator;
import com.samourai.whirlpool.client.wallet.WhirlpoolWallet; import com.samourai.whirlpool.client.wallet.WhirlpoolWallet;
import com.samourai.whirlpool.client.wallet.WhirlpoolWalletConfig; import com.samourai.whirlpool.client.wallet.WhirlpoolWalletConfig;
import com.samourai.whirlpool.client.wallet.data.dataPersister.DataPersister; import com.samourai.whirlpool.client.wallet.data.dataPersister.DataPersister;
@ -7,24 +8,49 @@ import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigPersistedS
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigSupplier; import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigSupplier;
import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier; import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier;
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowWalletStateSupplier; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowWalletStateSupplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SparrowDataPersister implements DataPersister { public class SparrowDataPersister implements DataPersister {
private static final Logger log = LoggerFactory.getLogger(SparrowDataPersister.class);
private final WalletStateSupplier walletStateSupplier; private final WalletStateSupplier walletStateSupplier;
private final UtxoConfigSupplier utxoConfigSupplier; private final UtxoConfigSupplier utxoConfigSupplier;
public SparrowDataPersister(WhirlpoolWallet whirlpoolWallet) throws Exception { private AbstractOrchestrator persistOrchestrator;
private final int persistDelaySeconds;
public SparrowDataPersister(WhirlpoolWallet whirlpoolWallet, int persistDelaySeconds) throws Exception {
WhirlpoolWalletConfig config = whirlpoolWallet.getConfig(); WhirlpoolWalletConfig config = whirlpoolWallet.getConfig();
String walletIdentifier = whirlpoolWallet.getWalletIdentifier(); String walletIdentifier = whirlpoolWallet.getWalletIdentifier();
this.walletStateSupplier = new SparrowWalletStateSupplier(walletIdentifier, config); this.walletStateSupplier = new SparrowWalletStateSupplier(walletIdentifier, config);
this.utxoConfigSupplier = new UtxoConfigPersistedSupplier(new SparrowUtxoConfigPersister(walletIdentifier)); this.utxoConfigSupplier = new UtxoConfigPersistedSupplier(new SparrowUtxoConfigPersister(walletIdentifier));
this.persistDelaySeconds = persistDelaySeconds;
} }
@Override @Override
public void open() throws Exception { public void open() throws Exception {
startPersistOrchestrator();
}
protected void startPersistOrchestrator() {
persistOrchestrator = new AbstractOrchestrator(persistDelaySeconds * 1000) {
@Override
protected void runOrchestrator() {
try {
persist(false);
} catch (Exception e) {
log.error("Error persisting Whirlpool data", e);
}
}
};
persistOrchestrator.start(true);
} }
@Override @Override
public void close() throws Exception { public void close() throws Exception {
persistOrchestrator.stop();
} }
@Override @Override

View file

@ -62,8 +62,10 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
wallet.getUtxoMixes().putAll(changedUtxoMixes); wallet.getUtxoMixes().putAll(changedUtxoMixes);
wallet.getUtxoMixes().keySet().removeAll(removedUtxoMixes.keySet()); wallet.getUtxoMixes().keySet().removeAll(removedUtxoMixes.keySet());
if(!changedUtxoMixes.isEmpty() || !removedUtxoMixes.isEmpty()) {
EventManager.get().post(new WalletUtxoMixesChangedEvent(wallet, changedUtxoMixes, removedUtxoMixes)); EventManager.get().post(new WalletUtxoMixesChangedEvent(wallet, changedUtxoMixes, removedUtxoMixes));
} }
}
private Wallet getWallet() { private Wallet getWallet() {
return AppServices.get().getOpenWallets().entrySet().stream().filter(entry -> entry.getValue().getWalletId(entry.getKey()).equals(walletId)).map(Map.Entry::getKey).findFirst().orElse(null); return AppServices.get().getOpenWallets().entrySet().stream().filter(entry -> entry.getValue().getWalletId(entry.getKey()).equals(walletId)).map(Map.Entry::getKey).findFirst().orElse(null);