Browse Source
# Conflicts: # src/main/java/com/sparrowwallet/sparrow/AppServices.java # src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.javaterminal
zeroleak
3 years ago
52 changed files with 1379 additions and 184 deletions
@ -1 +1 @@ |
|||
Subproject commit 2eedd2290cbe1dd559247f1ee934cece81fa7419 |
|||
Subproject commit 81c202198e8b057271414d15259df556a90bc6f1 |
@ -0,0 +1,167 @@ |
|||
package com.sparrowwallet.sparrow.control; |
|||
|
|||
import com.samourai.whirlpool.client.mix.listener.MixFailReason; |
|||
import com.samourai.whirlpool.client.mix.listener.MixStep; |
|||
import com.samourai.whirlpool.client.wallet.beans.MixProgress; |
|||
import com.samourai.whirlpool.protocol.beans.Utxo; |
|||
import com.sparrowwallet.sparrow.AppServices; |
|||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; |
|||
import com.sparrowwallet.sparrow.wallet.Entry; |
|||
import com.sparrowwallet.sparrow.wallet.UtxoEntry; |
|||
import com.sparrowwallet.sparrow.whirlpool.Whirlpool; |
|||
import com.sparrowwallet.sparrow.whirlpool.WhirlpoolException; |
|||
import javafx.geometry.Pos; |
|||
import javafx.scene.control.*; |
|||
import org.controlsfx.glyphfont.Glyph; |
|||
|
|||
public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> { |
|||
public MixStatusCell() { |
|||
super(); |
|||
setAlignment(Pos.CENTER_RIGHT); |
|||
setContentDisplay(ContentDisplay.LEFT); |
|||
setGraphicTextGap(8); |
|||
getStyleClass().add("mixstatus-cell"); |
|||
} |
|||
|
|||
@Override |
|||
protected void updateItem(UtxoEntry.MixStatus mixStatus, boolean empty) { |
|||
super.updateItem(mixStatus, empty); |
|||
|
|||
EntryCell.applyRowStyles(this, mixStatus == null ? null : mixStatus.getUtxoEntry()); |
|||
|
|||
if(empty || mixStatus == null) { |
|||
setText(null); |
|||
setGraphic(null); |
|||
} else { |
|||
setText(Integer.toString(mixStatus.getMixesDone())); |
|||
if(mixStatus.getNextMixUtxo() == null) { |
|||
setContextMenu(new MixStatusContextMenu(mixStatus.getUtxoEntry(), mixStatus.getMixProgress() != null && mixStatus.getMixProgress().getMixStep() != MixStep.FAIL)); |
|||
} else { |
|||
setContextMenu(null); |
|||
} |
|||
|
|||
if(mixStatus.getPoolId() != null) { |
|||
Tooltip tooltip = new Tooltip(); |
|||
tooltip.setText("Pool: " + mixStatus.getPoolId().replace("btc", " BTC")); |
|||
setTooltip(tooltip); |
|||
} |
|||
|
|||
if(mixStatus.getNextMixUtxo() != null) { |
|||
setMixSuccess(mixStatus.getNextMixUtxo()); |
|||
} else if(mixStatus.getMixFailReason() != null) { |
|||
setMixFail(mixStatus.getMixFailReason()); |
|||
} else if(mixStatus.getMixProgress() != null) { |
|||
setMixProgress(mixStatus.getMixProgress()); |
|||
} else { |
|||
setGraphic(null); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void setMixSuccess(Utxo nextMixUtxo) { |
|||
ProgressIndicator progressIndicator = getProgressIndicator(); |
|||
progressIndicator.setProgress(-1); |
|||
setGraphic(progressIndicator); |
|||
Tooltip tt = new Tooltip(); |
|||
tt.setText("Waiting for broadcast of " + nextMixUtxo.getHash().substring(0, 8) + "..." + ":" + nextMixUtxo.getIndex() ); |
|||
setTooltip(tt); |
|||
} |
|||
|
|||
private void setMixFail(MixFailReason mixFailReason) { |
|||
if(mixFailReason != MixFailReason.CANCEL) { |
|||
setGraphic(getFailGlyph()); |
|||
Tooltip tt = new Tooltip(); |
|||
tt.setText(mixFailReason.getMessage()); |
|||
setTooltip(tt); |
|||
} else { |
|||
setGraphic(null); |
|||
} |
|||
} |
|||
|
|||
private void setMixProgress(MixProgress mixProgress) { |
|||
if(mixProgress.getMixStep() != MixStep.FAIL) { |
|||
ProgressIndicator progressIndicator = getProgressIndicator(); |
|||
progressIndicator.setProgress(mixProgress.getProgressPercent() == 100 ? -1 : mixProgress.getProgressPercent() / 100.0); |
|||
setGraphic(progressIndicator); |
|||
Tooltip tt = new Tooltip(); |
|||
tt.setText(mixProgress.getMixStep().getMessage().substring(0, 1).toUpperCase() + mixProgress.getMixStep().getMessage().substring(1)); |
|||
setTooltip(tt); |
|||
} else { |
|||
setGraphic(null); |
|||
} |
|||
} |
|||
|
|||
private ProgressIndicator getProgressIndicator() { |
|||
ProgressIndicator progressIndicator; |
|||
if(getGraphic() instanceof ProgressIndicator) { |
|||
progressIndicator = (ProgressIndicator)getGraphic(); |
|||
} else { |
|||
progressIndicator = new ProgressBar(); |
|||
} |
|||
|
|||
return progressIndicator; |
|||
} |
|||
|
|||
private static Glyph getMixGlyph() { |
|||
Glyph copyGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.RANDOM); |
|||
copyGlyph.setFontSize(12); |
|||
return copyGlyph; |
|||
} |
|||
|
|||
private static Glyph getStopGlyph() { |
|||
Glyph copyGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.STOP_CIRCLE); |
|||
copyGlyph.setFontSize(12); |
|||
return copyGlyph; |
|||
} |
|||
|
|||
public static Glyph getFailGlyph() { |
|||
Glyph failGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_CIRCLE); |
|||
failGlyph.getStyleClass().add("fail-warning"); |
|||
failGlyph.setFontSize(12); |
|||
return failGlyph; |
|||
} |
|||
|
|||
private static class MixStatusContextMenu extends ContextMenu { |
|||
public MixStatusContextMenu(UtxoEntry utxoEntry, boolean isMixing) { |
|||
Whirlpool pool = AppServices.get().getWhirlpool(utxoEntry.getWallet()); |
|||
if(isMixing) { |
|||
MenuItem mixStop = new MenuItem("Stop Mixing"); |
|||
if(pool != null) { |
|||
mixStop.disableProperty().bind(pool.mixingProperty().not()); |
|||
} |
|||
mixStop.setGraphic(getStopGlyph()); |
|||
mixStop.setOnAction(event -> { |
|||
hide(); |
|||
Whirlpool whirlpool = AppServices.get().getWhirlpool(utxoEntry.getWallet()); |
|||
if(whirlpool != null) { |
|||
try { |
|||
whirlpool.mixStop(utxoEntry.getHashIndex()); |
|||
} catch(WhirlpoolException e) { |
|||
AppServices.showErrorDialog("Error stopping mixing UTXO", e.getMessage()); |
|||
} |
|||
} |
|||
}); |
|||
getItems().add(mixStop); |
|||
} else { |
|||
MenuItem mixNow = new MenuItem("Mix Now"); |
|||
if(pool != null) { |
|||
mixNow.disableProperty().bind(pool.mixingProperty().not()); |
|||
} |
|||
|
|||
mixNow.setGraphic(getMixGlyph()); |
|||
mixNow.setOnAction(event -> { |
|||
hide(); |
|||
Whirlpool whirlpool = AppServices.get().getWhirlpool(utxoEntry.getWallet()); |
|||
if(whirlpool != null) { |
|||
try { |
|||
whirlpool.mix(utxoEntry.getHashIndex()); |
|||
} catch(WhirlpoolException e) { |
|||
AppServices.showErrorDialog("Error mixing UTXO", e.getMessage()); |
|||
} |
|||
} |
|||
}); |
|||
getItems().add(mixNow); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.sparrowwallet.sparrow.event; |
|||
|
|||
import com.sparrowwallet.drongo.protocol.Sha256Hash; |
|||
import com.sparrowwallet.drongo.wallet.UtxoMixData; |
|||
import com.sparrowwallet.drongo.wallet.Wallet; |
|||
|
|||
import java.util.Map; |
|||
|
|||
public class WalletUtxoMixesChangedEvent extends WalletChangedEvent { |
|||
private final Map<Sha256Hash, UtxoMixData> changedUtxoMixes; |
|||
private final Map<Sha256Hash, UtxoMixData> removedUtxoMixes; |
|||
|
|||
public WalletUtxoMixesChangedEvent(Wallet wallet, Map<Sha256Hash, UtxoMixData> changedUtxoMixes, Map<Sha256Hash, UtxoMixData> removedUtxoMixes) { |
|||
super(wallet); |
|||
this.changedUtxoMixes = changedUtxoMixes; |
|||
this.removedUtxoMixes = removedUtxoMixes; |
|||
} |
|||
|
|||
public Map<Sha256Hash, UtxoMixData> getChangedUtxoMixes() { |
|||
return changedUtxoMixes; |
|||
} |
|||
|
|||
public Map<Sha256Hash, UtxoMixData> getRemovedUtxoMixes() { |
|||
return removedUtxoMixes; |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
package com.sparrowwallet.sparrow.event; |
|||
|
|||
import com.samourai.whirlpool.client.mix.listener.MixFailReason; |
|||
import com.samourai.whirlpool.client.wallet.beans.MixProgress; |
|||
import com.samourai.whirlpool.protocol.beans.Utxo; |
|||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; |
|||
import com.sparrowwallet.drongo.wallet.Wallet; |
|||
|
|||
public class WhirlpoolMixEvent { |
|||
private final Wallet wallet; |
|||
private final BlockTransactionHashIndex utxo; |
|||
private final MixProgress mixProgress; |
|||
private final Utxo nextUtxo; |
|||
private final MixFailReason mixFailReason; |
|||
|
|||
public WhirlpoolMixEvent(Wallet wallet, BlockTransactionHashIndex utxo, MixProgress mixProgress) { |
|||
this.wallet = wallet; |
|||
this.utxo = utxo; |
|||
this.mixProgress = mixProgress; |
|||
this.nextUtxo = null; |
|||
this.mixFailReason = null; |
|||
} |
|||
|
|||
public WhirlpoolMixEvent(Wallet wallet, BlockTransactionHashIndex utxo, Utxo nextUtxo) { |
|||
this.wallet = wallet; |
|||
this.utxo = utxo; |
|||
this.mixProgress = null; |
|||
this.nextUtxo = nextUtxo; |
|||
this.mixFailReason = null; |
|||
} |
|||
|
|||
public WhirlpoolMixEvent(Wallet wallet, BlockTransactionHashIndex utxo, MixFailReason mixFailReason) { |
|||
this.wallet = wallet; |
|||
this.utxo = utxo; |
|||
this.mixProgress = null; |
|||
this.nextUtxo = null; |
|||
this.mixFailReason = mixFailReason; |
|||
} |
|||
|
|||
public Wallet getWallet() { |
|||
return wallet; |
|||
} |
|||
|
|||
public BlockTransactionHashIndex getUtxo() { |
|||
return utxo; |
|||
} |
|||
|
|||
public MixProgress getMixProgress() { |
|||
return mixProgress; |
|||
} |
|||
|
|||
public Utxo getNextUtxo() { |
|||
return nextUtxo; |
|||
} |
|||
|
|||
public MixFailReason getMixFailReason() { |
|||
return mixFailReason; |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.sparrowwallet.sparrow.event; |
|||
|
|||
import com.samourai.whirlpool.protocol.beans.Utxo; |
|||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; |
|||
import com.sparrowwallet.drongo.wallet.Wallet; |
|||
import com.sparrowwallet.drongo.wallet.WalletNode; |
|||
|
|||
public class WhirlpoolMixSuccessEvent extends WhirlpoolMixEvent { |
|||
private final WalletNode walletNode; |
|||
|
|||
public WhirlpoolMixSuccessEvent(Wallet wallet, BlockTransactionHashIndex utxo, Utxo nextUtxo, WalletNode walletNode) { |
|||
super(wallet, utxo, nextUtxo); |
|||
this.walletNode = walletNode; |
|||
} |
|||
|
|||
public WalletNode getWalletNode() { |
|||
return walletNode; |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
package com.sparrowwallet.sparrow.io.db; |
|||
|
|||
import com.sparrowwallet.drongo.protocol.Sha256Hash; |
|||
import com.sparrowwallet.drongo.wallet.UtxoMixData; |
|||
import com.sparrowwallet.drongo.wallet.Wallet; |
|||
import org.jdbi.v3.sqlobject.config.RegisterRowMapper; |
|||
import org.jdbi.v3.sqlobject.customizer.BindList; |
|||
import org.jdbi.v3.sqlobject.statement.GetGeneratedKeys; |
|||
import org.jdbi.v3.sqlobject.statement.SqlQuery; |
|||
import org.jdbi.v3.sqlobject.statement.SqlUpdate; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
public interface UtxoMixDataDao { |
|||
@SqlQuery("select id, hash, poolId, mixesDone, forwarding from utxoMixData where wallet = ? order by id") |
|||
@RegisterRowMapper(UtxoMixDataMapper.class) |
|||
Map<Sha256Hash, UtxoMixData> getForWalletId(Long id); |
|||
|
|||
@SqlQuery("select id, hash, poolId, mixesDone, forwarding from utxoMixData where hash = ?") |
|||
@RegisterRowMapper(UtxoMixDataMapper.class) |
|||
Map<Sha256Hash, UtxoMixData> getForHash(byte[] hash); |
|||
|
|||
@SqlUpdate("insert into utxoMixData (hash, poolId, mixesDone, forwarding, wallet) values (?, ?, ?, ?, ?)") |
|||
@GetGeneratedKeys("id") |
|||
long insertUtxoMixData(byte[] hash, String poolId, int mixesDone, Long forwarding, long wallet); |
|||
|
|||
@SqlUpdate("update utxoMixData set hash = ?, poolId = ?, mixesDone = ?, forwarding = ?, wallet = ? where id = ?") |
|||
void updateUtxoMixData(byte[] hash, String poolId, int mixesDone, Long forwarding, long wallet, long id); |
|||
|
|||
@SqlUpdate("delete from utxoMixData where id in (<ids>)") |
|||
void deleteUtxoMixData(@BindList("ids") List<Long> ids); |
|||
|
|||
@SqlUpdate("delete from utxoMixData where wallet = ?") |
|||
void clear(long wallet); |
|||
|
|||
default void addUtxoMixData(Wallet wallet) { |
|||
for(Map.Entry<Sha256Hash, UtxoMixData> utxoMixDataEntry : wallet.getUtxoMixes().entrySet()) { |
|||
utxoMixDataEntry.getValue().setId(null); |
|||
addOrUpdate(wallet, utxoMixDataEntry.getKey(), utxoMixDataEntry.getValue()); |
|||
} |
|||
} |
|||
|
|||
default void addOrUpdate(Wallet wallet, Sha256Hash hash, UtxoMixData utxoMixData) { |
|||
Map<Sha256Hash, UtxoMixData> existing = getForHash(hash.getBytes()); |
|||
|
|||
if(existing.isEmpty() && utxoMixData.getId() == null) { |
|||
long id = insertUtxoMixData(hash.getBytes(), utxoMixData.getPoolId(), utxoMixData.getMixesDone(), utxoMixData.getForwarding(), wallet.getId()); |
|||
utxoMixData.setId(id); |
|||
} else { |
|||
Long existingId = existing.get(hash) != null ? existing.get(hash).getId() : utxoMixData.getId(); |
|||
updateUtxoMixData(hash.getBytes(), utxoMixData.getPoolId(), utxoMixData.getMixesDone(), utxoMixData.getForwarding(), wallet.getId(), existingId); |
|||
utxoMixData.setId(existingId); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
package com.sparrowwallet.sparrow.io.db; |
|||
|
|||
import com.sparrowwallet.drongo.protocol.Sha256Hash; |
|||
import com.sparrowwallet.drongo.wallet.UtxoMixData; |
|||
import org.jdbi.v3.core.mapper.RowMapper; |
|||
import org.jdbi.v3.core.statement.StatementContext; |
|||
|
|||
import java.sql.ResultSet; |
|||
import java.sql.SQLException; |
|||
import java.util.Map; |
|||
|
|||
public class UtxoMixDataMapper implements RowMapper<Map.Entry<Sha256Hash, UtxoMixData>> { |
|||
@Override |
|||
public Map.Entry<Sha256Hash, UtxoMixData> map(ResultSet rs, StatementContext ctx) throws SQLException { |
|||
Sha256Hash hash = Sha256Hash.wrap(rs.getBytes("hash")); |
|||
|
|||
Long forwarding = rs.getLong("forwarding"); |
|||
if(rs.wasNull()) { |
|||
forwarding = null; |
|||
} |
|||
|
|||
UtxoMixData utxoMixData = new UtxoMixData(rs.getString("poolId"), rs.getInt("mixesDone"), forwarding); |
|||
utxoMixData.setId(rs.getLong("id")); |
|||
|
|||
return new Map.Entry<>() { |
|||
@Override |
|||
public Sha256Hash getKey() { |
|||
return hash; |
|||
} |
|||
|
|||
@Override |
|||
public UtxoMixData getValue() { |
|||
return utxoMixData; |
|||
} |
|||
|
|||
@Override |
|||
public UtxoMixData setValue(UtxoMixData value) { |
|||
return null; |
|||
} |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,77 @@ |
|||
package com.sparrowwallet.sparrow.whirlpool; |
|||
|
|||
import com.google.common.collect.MapDifference; |
|||
import com.google.common.collect.Maps; |
|||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigData; |
|||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigPersisted; |
|||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigPersister; |
|||
import com.sparrowwallet.drongo.protocol.Sha256Hash; |
|||
import com.sparrowwallet.drongo.wallet.UtxoMixData; |
|||
import com.sparrowwallet.drongo.wallet.Wallet; |
|||
import com.sparrowwallet.sparrow.AppServices; |
|||
import com.sparrowwallet.sparrow.EventManager; |
|||
import com.sparrowwallet.sparrow.event.WalletUtxoMixesChangedEvent; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
import java.util.*; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class SparrowUtxoConfigPersister extends UtxoConfigPersister { |
|||
private static final Logger log = LoggerFactory.getLogger(SparrowUtxoConfigPersister.class); |
|||
|
|||
private final String walletId; |
|||
private long lastWrite; |
|||
|
|||
public SparrowUtxoConfigPersister(String walletId) { |
|||
super(walletId); |
|||
this.walletId = walletId; |
|||
} |
|||
|
|||
@Override |
|||
public synchronized UtxoConfigData load() throws Exception { |
|||
Wallet wallet = getWallet(); |
|||
if(wallet == null) { |
|||
throw new IllegalStateException("Can't find wallet with walletId " + walletId); |
|||
} |
|||
|
|||
Map<String, UtxoConfigPersisted> utxoConfigs = wallet.getUtxoMixes().entrySet().stream() |
|||
.collect(Collectors.toMap(entry -> entry.getKey().toString(), entry -> new UtxoConfigPersisted(entry.getValue().getPoolId(), entry.getValue().getMixesDone(), entry.getValue().getForwarding()), |
|||
(u, v) -> { throw new IllegalStateException("Duplicate utxo config hashes"); }, |
|||
HashMap::new)); |
|||
|
|||
return new UtxoConfigData(utxoConfigs); |
|||
} |
|||
|
|||
@Override |
|||
public synchronized void write(UtxoConfigData data) throws Exception { |
|||
Wallet wallet = getWallet(); |
|||
if(wallet == null) { |
|||
//Wallet is already closed
|
|||
return; |
|||
} |
|||
|
|||
Map<String, UtxoConfigPersisted> currentData = new HashMap<>(data.getUtxoConfigs()); |
|||
Map<Sha256Hash, UtxoMixData> changedUtxoMixes = currentData.entrySet().stream() |
|||
.collect(Collectors.toMap(entry -> Sha256Hash.wrap(entry.getKey()), entry -> new UtxoMixData(entry.getValue().getPoolId(), entry.getValue().getMixsDone(), entry.getValue().getForwarding()), |
|||
(u, v) -> { throw new IllegalStateException("Duplicate utxo config hashes"); }, |
|||
HashMap::new)); |
|||
|
|||
MapDifference<Sha256Hash, UtxoMixData> mapDifference = Maps.difference(changedUtxoMixes, wallet.getUtxoMixes()); |
|||
Map<Sha256Hash, UtxoMixData> removedUtxoMixes = mapDifference.entriesOnlyOnRight(); |
|||
wallet.getUtxoMixes().putAll(changedUtxoMixes); |
|||
wallet.getUtxoMixes().keySet().removeAll(removedUtxoMixes.keySet()); |
|||
|
|||
EventManager.get().post(new WalletUtxoMixesChangedEvent(wallet, changedUtxoMixes, removedUtxoMixes)); |
|||
lastWrite = System.currentTimeMillis(); |
|||
} |
|||
|
|||
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); |
|||
} |
|||
|
|||
@Override |
|||
public long getLastWrite() { |
|||
return lastWrite; |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.sparrowwallet.sparrow.whirlpool; |
|||
|
|||
import com.samourai.wallet.hd.HD_Wallet; |
|||
import com.samourai.whirlpool.client.wallet.WhirlpoolWalletConfig; |
|||
import com.samourai.whirlpool.client.wallet.data.minerFee.BackendWalletDataSupplier; |
|||
import com.samourai.whirlpool.client.wallet.data.minerFee.WalletSupplier; |
|||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigPersister; |
|||
|
|||
public class SparrowWalletDataSupplier extends BackendWalletDataSupplier { |
|||
public SparrowWalletDataSupplier(int refreshUtxoDelay, WhirlpoolWalletConfig config, HD_Wallet bip44w, String walletIdentifier) throws Exception { |
|||
super(refreshUtxoDelay, config, bip44w, walletIdentifier); |
|||
} |
|||
|
|||
@Override |
|||
protected WalletSupplier computeWalletSupplier(WhirlpoolWalletConfig config, HD_Wallet bip44w, String walletIdentifier) throws Exception { |
|||
int externalIndexDefault = config.getExternalDestination() != null ? config.getExternalDestination().getStartIndex() : 0; |
|||
return new WalletSupplier(new SparrowWalletStatePersister(walletIdentifier), config.getBackendApi(), bip44w, externalIndexDefault); |
|||
} |
|||
|
|||
@Override |
|||
protected UtxoConfigPersister computeUtxoConfigPersister(String walletIdentifier) throws Exception { |
|||
return new SparrowUtxoConfigPersister(walletIdentifier); |
|||
} |
|||
} |
@ -0,0 +1,53 @@ |
|||
package com.sparrowwallet.sparrow.whirlpool; |
|||
|
|||
import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateData; |
|||
import com.samourai.whirlpool.client.wallet.data.walletState.WalletStatePersister; |
|||
import com.sparrowwallet.drongo.KeyPurpose; |
|||
import com.sparrowwallet.drongo.protocol.ScriptType; |
|||
import com.sparrowwallet.drongo.wallet.StandardAccount; |
|||
import com.sparrowwallet.drongo.wallet.Wallet; |
|||
import com.sparrowwallet.sparrow.AppServices; |
|||
|
|||
import java.util.LinkedHashMap; |
|||
import java.util.Map; |
|||
|
|||
public class SparrowWalletStatePersister extends WalletStatePersister { |
|||
private final String walletId; |
|||
|
|||
public SparrowWalletStatePersister(String walletId) { |
|||
super(walletId); |
|||
this.walletId = walletId; |
|||
} |
|||
|
|||
@Override |
|||
public synchronized WalletStateData load() throws Exception { |
|||
Wallet wallet = AppServices.get().getOpenWallets().entrySet().stream().filter(entry -> entry.getValue().getWalletId(entry.getKey()).equals(walletId)).map(Map.Entry::getKey).findFirst().orElseThrow(); |
|||
|
|||
Map<String, Integer> values = new LinkedHashMap<>(); |
|||
values.put("init", 1); |
|||
putValues("DEPOSIT", wallet, values); |
|||
|
|||
for(StandardAccount whirlpoolAccount : StandardAccount.WHIRLPOOL_ACCOUNTS) { |
|||
putValues(whirlpoolAccount.getName().toUpperCase(), wallet.getChildWallet(whirlpoolAccount), values); |
|||
} |
|||
|
|||
return new WalletStateData(values); |
|||
} |
|||
|
|||
private void putValues(String prefix, Wallet wallet, Map<String, Integer> values) { |
|||
for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) { |
|||
Integer index = wallet.getNode(keyPurpose).getHighestUsedIndex(); |
|||
values.put(prefix + "_" + getPurpose(wallet) + "_" + keyPurpose.getPathIndex().num(), index == null ? 0 : index + 1); |
|||
} |
|||
} |
|||
|
|||
private int getPurpose(Wallet wallet) { |
|||
ScriptType scriptType = wallet.getScriptType(); |
|||
return scriptType.getDefaultDerivation().get(0).num(); |
|||
} |
|||
|
|||
@Override |
|||
public synchronized void write(WalletStateData data) throws Exception { |
|||
//nothing required
|
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
package com.sparrowwallet.sparrow.whirlpool; |
|||
|
|||
import com.samourai.wallet.hd.HD_Wallet; |
|||
import com.samourai.whirlpool.client.wallet.WhirlpoolWalletConfig; |
|||
import com.samourai.whirlpool.client.wallet.WhirlpoolWalletService; |
|||
import com.samourai.whirlpool.client.wallet.data.minerFee.WalletDataSupplier; |
|||
|
|||
public class SparrowWhirlpoolWalletService extends WhirlpoolWalletService { |
|||
private String walletId; |
|||
|
|||
@Override |
|||
protected WalletDataSupplier computeWalletDataSupplier(WhirlpoolWalletConfig config, HD_Wallet bip44w, String walletIdentifier) throws Exception { |
|||
return new SparrowWalletDataSupplier(config.getRefreshUtxoDelay(), config, bip44w, walletId); |
|||
} |
|||
|
|||
public String getWalletId() { |
|||
return walletId; |
|||
} |
|||
|
|||
public void setWalletId(String walletId) { |
|||
this.walletId = walletId; |
|||
} |
|||
} |
@ -0,0 +1 @@ |
|||
create table utxoMixData (id identity not null, hash binary(32) not null, poolId varchar(32), mixesDone integer not null default 0, forwarding bigint, wallet bigint not null); |
Loading…
Reference in new issue