mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
Merge pull request #183 from zeroleak/whirlpool-client-0.23.30-early4
Upgrade to whirlpool-client 0.23.30-early4 + extlibj 0.0.19-dsk3
This commit is contained in:
commit
f30c00ba8f
18 changed files with 387 additions and 312 deletions
13
build.gradle
13
build.gradle
|
@ -91,7 +91,7 @@ dependencies {
|
||||||
implementation('org.slf4j:jul-to-slf4j:1.7.30') {
|
implementation('org.slf4j:jul-to-slf4j:1.7.30') {
|
||||||
exclude group: 'org.slf4j'
|
exclude group: 'org.slf4j'
|
||||||
}
|
}
|
||||||
implementation('com.sparrowwallet.nightjar:nightjar:0.2.10')
|
implementation('com.sparrowwallet.nightjar:nightjar:0.2.11-SNAPSHOT')
|
||||||
testImplementation('junit:junit:4.12')
|
testImplementation('junit:junit:4.12')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ extraJavaModuleInfo {
|
||||||
module('cbor-0.9.jar', 'co.nstant.in.cbor', '0.9') {
|
module('cbor-0.9.jar', 'co.nstant.in.cbor', '0.9') {
|
||||||
exports('co.nstant.in.cbor')
|
exports('co.nstant.in.cbor')
|
||||||
}
|
}
|
||||||
module('nightjar-0.2.10.jar', 'com.sparrowwallet.nightjar', '0.2.10') {
|
module('nightjar-0.2.11-SNAPSHOT.jar', 'com.sparrowwallet.nightjar', '0.2.11-SNAPSHOT') {
|
||||||
requires('com.google.common')
|
requires('com.google.common')
|
||||||
requires('net.sourceforge.streamsupport')
|
requires('net.sourceforge.streamsupport')
|
||||||
requires('org.slf4j')
|
requires('org.slf4j')
|
||||||
|
@ -395,25 +395,30 @@ extraJavaModuleInfo {
|
||||||
requires('com.fasterxml.jackson.databind')
|
requires('com.fasterxml.jackson.databind')
|
||||||
requires('logback.classic')
|
requires('logback.classic')
|
||||||
requires('org.json')
|
requires('org.json')
|
||||||
exports('com.sparrowwallet.nightjar')
|
|
||||||
exports('com.samourai.http.client')
|
exports('com.samourai.http.client')
|
||||||
exports('com.samourai.tor.client')
|
exports('com.samourai.tor.client')
|
||||||
exports('com.samourai.wallet.api.backend')
|
exports('com.samourai.wallet.api.backend')
|
||||||
exports('com.samourai.wallet.api.backend.beans')
|
exports('com.samourai.wallet.api.backend.beans')
|
||||||
|
exports('com.samourai.wallet.client.indexHandler')
|
||||||
exports('com.samourai.wallet.hd')
|
exports('com.samourai.wallet.hd')
|
||||||
exports('com.samourai.wallet.hd.java')
|
|
||||||
exports('com.samourai.whirlpool.client.event')
|
exports('com.samourai.whirlpool.client.event')
|
||||||
exports('com.samourai.whirlpool.client.wallet')
|
exports('com.samourai.whirlpool.client.wallet')
|
||||||
exports('com.samourai.whirlpool.client.wallet.beans')
|
exports('com.samourai.whirlpool.client.wallet.beans')
|
||||||
|
exports('com.samourai.whirlpool.client.wallet.data.dataSource')
|
||||||
|
exports('com.samourai.whirlpool.client.wallet.data.dataPersister')
|
||||||
exports('com.samourai.whirlpool.client.whirlpool')
|
exports('com.samourai.whirlpool.client.whirlpool')
|
||||||
exports('com.samourai.whirlpool.client.whirlpool.beans')
|
exports('com.samourai.whirlpool.client.whirlpool.beans')
|
||||||
exports('com.samourai.whirlpool.client.wallet.data.pool')
|
exports('com.samourai.whirlpool.client.wallet.data.pool')
|
||||||
exports('com.samourai.whirlpool.client.wallet.data.utxo')
|
exports('com.samourai.whirlpool.client.wallet.data.utxo')
|
||||||
|
exports('com.samourai.whirlpool.client.wallet.data.utxoConfig')
|
||||||
|
exports('com.samourai.whirlpool.client.wallet.data.supplier')
|
||||||
|
exports('com.samourai.whirlpool.client.mix.handler')
|
||||||
exports('com.samourai.whirlpool.client.mix.listener')
|
exports('com.samourai.whirlpool.client.mix.listener')
|
||||||
exports('com.samourai.whirlpool.protocol.beans')
|
exports('com.samourai.whirlpool.protocol.beans')
|
||||||
exports('com.samourai.whirlpool.protocol.rest')
|
exports('com.samourai.whirlpool.protocol.rest')
|
||||||
exports('com.samourai.whirlpool.client.tx0')
|
exports('com.samourai.whirlpool.client.tx0')
|
||||||
exports('com.samourai.wallet.segwit.bech32')
|
exports('com.samourai.wallet.segwit.bech32')
|
||||||
|
exports('com.samourai.whirlpool.client.wallet.data.wallet')
|
||||||
exports('com.samourai.whirlpool.client.wallet.data.minerFee')
|
exports('com.samourai.whirlpool.client.wallet.data.minerFee')
|
||||||
exports('com.samourai.whirlpool.client.wallet.data.walletState')
|
exports('com.samourai.whirlpool.client.wallet.data.walletState')
|
||||||
exports('com.sparrowwallet.nightjar.http')
|
exports('com.sparrowwallet.nightjar.http')
|
||||||
|
|
|
@ -469,7 +469,7 @@ public class AppServices {
|
||||||
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
||||||
if(whirlpool == null) {
|
if(whirlpool == null) {
|
||||||
HostAndPort torProxy = AppServices.isTorRunning() ? HostAndPort.fromParts("localhost", TorService.PROXY_PORT) : (Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer()));
|
HostAndPort torProxy = AppServices.isTorRunning() ? HostAndPort.fromParts("localhost", TorService.PROXY_PORT) : (Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer()));
|
||||||
whirlpool = new Whirlpool(Network.get(), torProxy, Config.get().getScode(), 1, 15);
|
whirlpool = new Whirlpool(Network.get(), torProxy, Config.get().getScode());
|
||||||
whirlpoolMap.put(walletId, whirlpool);
|
whirlpoolMap.put(walletId, whirlpool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,9 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
setContextMenu(null);
|
setContextMenu(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mixStatus.getPoolId() != null) {
|
if(mixStatus.getMixProgress() != null) {
|
||||||
Tooltip tooltip = new Tooltip();
|
Tooltip tooltip = new Tooltip();
|
||||||
tooltip.setText("Pool: " + mixStatus.getPoolId().replace("btc", " BTC"));
|
tooltip.setText("Pool: " + mixStatus.getMixProgress().getPoolId().replace("btc", " BTC"));
|
||||||
setTooltip(tooltip);
|
setTooltip(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
private void setMixProgress(MixProgress mixProgress) {
|
private void setMixProgress(MixProgress mixProgress) {
|
||||||
if(mixProgress.getMixStep() != MixStep.FAIL) {
|
if(mixProgress.getMixStep() != MixStep.FAIL) {
|
||||||
ProgressIndicator progressIndicator = getProgressIndicator();
|
ProgressIndicator progressIndicator = getProgressIndicator();
|
||||||
progressIndicator.setProgress(mixProgress.getProgressPercent() == 100 ? -1 : mixProgress.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));
|
tt.setText(mixProgress.getMixStep().getMessage().substring(0, 1).toUpperCase() + mixProgress.getMixStep().getMessage().substring(1));
|
||||||
|
|
|
@ -13,20 +13,20 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface UtxoMixDataDao {
|
public interface UtxoMixDataDao {
|
||||||
@SqlQuery("select id, hash, poolId, mixesDone, forwarding from utxoMixData where wallet = ? order by id")
|
@SqlQuery("select id, hash, mixesDone, expired from utxoMixData where wallet = ? order by id")
|
||||||
@RegisterRowMapper(UtxoMixDataMapper.class)
|
@RegisterRowMapper(UtxoMixDataMapper.class)
|
||||||
Map<Sha256Hash, UtxoMixData> getForWalletId(Long id);
|
Map<Sha256Hash, UtxoMixData> getForWalletId(Long id);
|
||||||
|
|
||||||
@SqlQuery("select id, hash, poolId, mixesDone, forwarding from utxoMixData where hash = ?")
|
@SqlQuery("select id, hash, mixesDone, expired from utxoMixData where hash = ?")
|
||||||
@RegisterRowMapper(UtxoMixDataMapper.class)
|
@RegisterRowMapper(UtxoMixDataMapper.class)
|
||||||
Map<Sha256Hash, UtxoMixData> getForHash(byte[] hash);
|
Map<Sha256Hash, UtxoMixData> getForHash(byte[] hash);
|
||||||
|
|
||||||
@SqlUpdate("insert into utxoMixData (hash, poolId, mixesDone, forwarding, wallet) values (?, ?, ?, ?, ?)")
|
@SqlUpdate("insert into utxoMixData (hash, mixesDone, expired, wallet) values (?, ?, ?, ?)")
|
||||||
@GetGeneratedKeys("id")
|
@GetGeneratedKeys("id")
|
||||||
long insertUtxoMixData(byte[] hash, String poolId, int mixesDone, Long forwarding, long wallet);
|
long insertUtxoMixData(byte[] hash, int mixesDone, Long expired, long wallet);
|
||||||
|
|
||||||
@SqlUpdate("update utxoMixData set hash = ?, poolId = ?, mixesDone = ?, forwarding = ?, wallet = ? where id = ?")
|
@SqlUpdate("update utxoMixData set hash = ?, mixesDone = ?, expired = ?, wallet = ? where id = ?")
|
||||||
void updateUtxoMixData(byte[] hash, String poolId, int mixesDone, Long forwarding, long wallet, long id);
|
void updateUtxoMixData(byte[] hash, int mixesDone, Long expired, long wallet, long id);
|
||||||
|
|
||||||
@SqlUpdate("delete from utxoMixData where id in (<ids>)")
|
@SqlUpdate("delete from utxoMixData where id in (<ids>)")
|
||||||
void deleteUtxoMixData(@BindList("ids") List<Long> ids);
|
void deleteUtxoMixData(@BindList("ids") List<Long> ids);
|
||||||
|
@ -45,11 +45,11 @@ public interface UtxoMixDataDao {
|
||||||
Map<Sha256Hash, UtxoMixData> existing = getForHash(hash.getBytes());
|
Map<Sha256Hash, UtxoMixData> existing = getForHash(hash.getBytes());
|
||||||
|
|
||||||
if(existing.isEmpty() && utxoMixData.getId() == null) {
|
if(existing.isEmpty() && utxoMixData.getId() == null) {
|
||||||
long id = insertUtxoMixData(hash.getBytes(), utxoMixData.getPoolId(), utxoMixData.getMixesDone(), utxoMixData.getForwarding(), wallet.getId());
|
long id = insertUtxoMixData(hash.getBytes(), utxoMixData.getMixesDone(), utxoMixData.getExpired(), wallet.getId());
|
||||||
utxoMixData.setId(id);
|
utxoMixData.setId(id);
|
||||||
} else {
|
} else {
|
||||||
Long existingId = existing.get(hash) != null ? existing.get(hash).getId() : utxoMixData.getId();
|
Long existingId = existing.get(hash) != null ? existing.get(hash).getId() : utxoMixData.getId();
|
||||||
updateUtxoMixData(hash.getBytes(), utxoMixData.getPoolId(), utxoMixData.getMixesDone(), utxoMixData.getForwarding(), wallet.getId(), existingId);
|
updateUtxoMixData(hash.getBytes(), utxoMixData.getMixesDone(), utxoMixData.getExpired(), wallet.getId(), existingId);
|
||||||
utxoMixData.setId(existingId);
|
utxoMixData.setId(existingId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@ public class UtxoMixDataMapper implements RowMapper<Map.Entry<Sha256Hash, UtxoMi
|
||||||
public Map.Entry<Sha256Hash, UtxoMixData> map(ResultSet rs, StatementContext ctx) throws SQLException {
|
public Map.Entry<Sha256Hash, UtxoMixData> map(ResultSet rs, StatementContext ctx) throws SQLException {
|
||||||
Sha256Hash hash = Sha256Hash.wrap(rs.getBytes("hash"));
|
Sha256Hash hash = Sha256Hash.wrap(rs.getBytes("hash"));
|
||||||
|
|
||||||
Long forwarding = rs.getLong("forwarding");
|
Long expired = rs.getLong("expired");
|
||||||
if(rs.wasNull()) {
|
if(rs.wasNull()) {
|
||||||
forwarding = null;
|
expired = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
UtxoMixData utxoMixData = new UtxoMixData(rs.getString("poolId"), rs.getInt("mixesDone"), forwarding);
|
UtxoMixData utxoMixData = new UtxoMixData(rs.getInt("mixesDone"), expired);
|
||||||
utxoMixData.setId(rs.getLong("id"));
|
utxoMixData.setId(rs.getLong("id"));
|
||||||
|
|
||||||
return new Map.Entry<>() {
|
return new Map.Entry<>() {
|
||||||
|
|
|
@ -157,17 +157,13 @@ public class UtxoEntry extends HashIndexEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UtxoMixData("Unknown Pool", getUtxoEntry().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX ? 1 : 0, null);
|
return new UtxoMixData(getUtxoEntry().getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX ? 1 : 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMixesDone() {
|
public int getMixesDone() {
|
||||||
return getUtxoMixData().getMixesDone();
|
return getUtxoMixData().getMixesDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPoolId() {
|
|
||||||
return getUtxoMixData().getPoolId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MixProgress getMixProgress() {
|
public MixProgress getMixProgress() {
|
||||||
return mixProgress;
|
return mixProgress;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package com.sparrowwallet.sparrow.whirlpool;
|
|
||||||
|
|
||||||
import com.samourai.wallet.api.backend.MinerFee;
|
|
||||||
import com.samourai.whirlpool.client.wallet.data.minerFee.MinerFeeSupplier;
|
|
||||||
|
|
||||||
public class SparrowMinerFeeSupplier extends MinerFeeSupplier {
|
|
||||||
public SparrowMinerFeeSupplier(int feeMin, int feeMax, int feeFallback, MinerFee currentMinerFee) {
|
|
||||||
super(feeMin, feeMax, feeFallback);
|
|
||||||
setValue(currentMinerFee);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,19 +3,21 @@ package com.sparrowwallet.sparrow.whirlpool;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.google.common.net.HostAndPort;
|
import com.google.common.net.HostAndPort;
|
||||||
import com.samourai.tor.client.TorClientService;
|
import com.samourai.tor.client.TorClientService;
|
||||||
import com.samourai.wallet.api.backend.BackendApi;
|
|
||||||
import com.samourai.wallet.api.backend.beans.UnspentOutput;
|
import com.samourai.wallet.api.backend.beans.UnspentOutput;
|
||||||
import com.samourai.wallet.hd.HD_Wallet;
|
import com.samourai.wallet.hd.HD_Wallet;
|
||||||
import com.samourai.wallet.hd.java.HD_WalletFactoryJava;
|
import com.samourai.wallet.hd.HD_WalletFactoryGeneric;
|
||||||
import com.samourai.whirlpool.client.event.*;
|
import com.samourai.whirlpool.client.event.*;
|
||||||
import com.samourai.whirlpool.client.tx0.*;
|
import com.samourai.whirlpool.client.tx0.*;
|
||||||
import com.samourai.whirlpool.client.wallet.WhirlpoolEventService;
|
import com.samourai.whirlpool.client.wallet.WhirlpoolEventService;
|
||||||
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.WhirlpoolWalletService;
|
||||||
import com.samourai.whirlpool.client.wallet.beans.*;
|
import com.samourai.whirlpool.client.wallet.beans.*;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.dataPersister.DataPersisterFactory;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.dataSource.DataSourceFactory;
|
||||||
import com.samourai.whirlpool.client.wallet.data.pool.PoolData;
|
import com.samourai.whirlpool.client.wallet.data.pool.PoolData;
|
||||||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigPersisted;
|
|
||||||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoSupplier;
|
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoSupplier;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfig;
|
||||||
import com.samourai.whirlpool.client.whirlpool.ServerApi;
|
import com.samourai.whirlpool.client.whirlpool.ServerApi;
|
||||||
import com.samourai.whirlpool.client.whirlpool.beans.Pool;
|
import com.samourai.whirlpool.client.whirlpool.beans.Pool;
|
||||||
import com.sparrowwallet.drongo.ExtendedKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
|
@ -35,6 +37,9 @@ 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.wallet.UtxoEntry;
|
import com.sparrowwallet.sparrow.wallet.UtxoEntry;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.dataPersister.SparrowDataPersister;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowMinerFeeSupplier;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
@ -43,7 +48,10 @@ import javafx.concurrent.Task;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Whirlpool {
|
public class Whirlpool {
|
||||||
|
@ -54,32 +62,36 @@ public class Whirlpool {
|
||||||
private final JavaHttpClientService httpClientService;
|
private final JavaHttpClientService httpClientService;
|
||||||
private final JavaStompClientService stompClientService;
|
private final JavaStompClientService stompClientService;
|
||||||
private final TorClientService torClientService;
|
private final TorClientService torClientService;
|
||||||
private final SparrowWhirlpoolWalletService whirlpoolWalletService;
|
private final WhirlpoolWalletService whirlpoolWalletService;
|
||||||
private final WhirlpoolWalletConfig config;
|
private final WhirlpoolWalletConfig config;
|
||||||
private HD_Wallet hdWallet;
|
private HD_Wallet hdWallet;
|
||||||
|
private String walletId;
|
||||||
|
|
||||||
private BooleanProperty mixingProperty = new SimpleBooleanProperty(false);
|
private BooleanProperty mixingProperty = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
public Whirlpool(Network network, HostAndPort torProxy, String sCode, int maxClients, int clientDelay) {
|
public Whirlpool(Network network, HostAndPort torProxy, String sCode) {
|
||||||
this.torProxy = torProxy;
|
this.torProxy = torProxy;
|
||||||
this.whirlpoolServer = WhirlpoolServer.valueOf(network.getName().toUpperCase());
|
this.whirlpoolServer = WhirlpoolServer.valueOf(network.getName().toUpperCase());
|
||||||
this.httpClientService = new JavaHttpClientService(torProxy);
|
this.httpClientService = new JavaHttpClientService(torProxy);
|
||||||
this.stompClientService = new JavaStompClientService(httpClientService);
|
this.stompClientService = new JavaStompClientService(httpClientService);
|
||||||
this.torClientService = new WhirlpoolTorClientService();
|
this.torClientService = new WhirlpoolTorClientService();
|
||||||
this.whirlpoolWalletService = new SparrowWhirlpoolWalletService();
|
|
||||||
this.config = computeWhirlpoolWalletConfig(sCode, maxClients, clientDelay);
|
this.whirlpoolWalletService = new WhirlpoolWalletService();
|
||||||
|
this.config = computeWhirlpoolWalletConfig(sCode);
|
||||||
|
|
||||||
WhirlpoolEventService.getInstance().register(this);
|
WhirlpoolEventService.getInstance().register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WhirlpoolWalletConfig computeWhirlpoolWalletConfig(String sCode, int maxClients, int clientDelay) {
|
private WhirlpoolWalletConfig computeWhirlpoolWalletConfig(String sCode) {
|
||||||
|
DataPersisterFactory dataPersisterFactory = (whirlpoolWallet, bip44w) -> new SparrowDataPersister(whirlpoolWallet);
|
||||||
|
DataSourceFactory dataSourceFactory = (whirlpoolWallet, bip44w, dataPersister) -> new SparrowDataSource(whirlpoolWallet, bip44w, dataPersister);
|
||||||
|
|
||||||
boolean onion = (torProxy != null);
|
boolean onion = (torProxy != null);
|
||||||
String serverUrl = whirlpoolServer.getServerUrl(onion);
|
String serverUrl = whirlpoolServer.getServerUrl(onion);
|
||||||
|
|
||||||
ServerApi serverApi = new ServerApi(serverUrl, httpClientService);
|
ServerApi serverApi = new ServerApi(serverUrl, httpClientService);
|
||||||
BackendApi backendApi = new SparrowBackendApi();
|
|
||||||
|
|
||||||
WhirlpoolWalletConfig whirlpoolWalletConfig = new WhirlpoolWalletConfig(httpClientService, stompClientService, torClientService, serverApi, whirlpoolServer, false, backendApi);
|
WhirlpoolWalletConfig whirlpoolWalletConfig = new WhirlpoolWalletConfig(dataSourceFactory, httpClientService, stompClientService, torClientService, serverApi, whirlpoolServer.getParams(), false);
|
||||||
|
whirlpoolWalletConfig.setDataPersisterFactory(dataPersisterFactory);
|
||||||
whirlpoolWalletConfig.setScode(sCode);
|
whirlpoolWalletConfig.setScode(sCode);
|
||||||
|
|
||||||
return whirlpoolWalletConfig;
|
return whirlpoolWalletConfig;
|
||||||
|
@ -123,7 +135,7 @@ public class Whirlpool {
|
||||||
|
|
||||||
private Tx0ParamService getTx0ParamService() {
|
private Tx0ParamService getTx0ParamService() {
|
||||||
try {
|
try {
|
||||||
SparrowMinerFeeSupplier minerFeeSupplier = new SparrowMinerFeeSupplier(config.getFeeMin(), config.getFeeMax(), config.getFeeFallback(), config.getBackendApi().fetchMinerFee());
|
SparrowMinerFeeSupplier minerFeeSupplier = SparrowMinerFeeSupplier.getInstance();
|
||||||
return new Tx0ParamService(minerFeeSupplier, config);
|
return new Tx0ParamService(minerFeeSupplier, config);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.error("Error fetching miner fees", e);
|
log.error("Error fetching miner fees", e);
|
||||||
|
@ -143,10 +155,10 @@ public class Whirlpool {
|
||||||
int purpose = scriptType.getDefaultDerivation().get(0).num();
|
int purpose = scriptType.getDefaultDerivation().get(0).num();
|
||||||
List<String> words = keystore.getSeed().getMnemonicCode();
|
List<String> words = keystore.getSeed().getMnemonicCode();
|
||||||
String passphrase = keystore.getSeed().getPassphrase().asString();
|
String passphrase = keystore.getSeed().getPassphrase().asString();
|
||||||
HD_WalletFactoryJava hdWalletFactory = HD_WalletFactoryJava.getInstance();
|
HD_WalletFactoryGeneric hdWalletFactory = HD_WalletFactoryGeneric.getInstance();
|
||||||
byte[] seed = hdWalletFactory.computeSeedFromWords(words);
|
byte[] seed = hdWalletFactory.computeSeedFromWords(words);
|
||||||
whirlpoolWalletService.setWalletId(walletId);
|
this.walletId = walletId;
|
||||||
hdWallet = new HD_Wallet(purpose, words, whirlpoolServer, seed, passphrase, 1);
|
hdWallet = new HD_Wallet(purpose, words, config.getNetworkParameters(), seed, passphrase, 1);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new IllegalStateException("Could not create Whirlpool HD wallet ", e);
|
throw new IllegalStateException("Could not create Whirlpool HD wallet ", e);
|
||||||
}
|
}
|
||||||
|
@ -162,7 +174,8 @@ public class Whirlpool {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return whirlpoolWalletService.openWallet(config, Utils.hexToBytes(hdWallet.getSeedHex()), hdWallet.getPassphrase());
|
WhirlpoolWallet whirlpoolWallet = new WhirlpoolWallet(config, Utils.hexToBytes(hdWallet.getSeedHex()), hdWallet.getPassphrase(), walletId);
|
||||||
|
return whirlpoolWalletService.openWallet(whirlpoolWallet);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new WhirlpoolException("Could not create whirlpool wallet ", e);
|
throw new WhirlpoolException("Could not create whirlpool wallet ", e);
|
||||||
}
|
}
|
||||||
|
@ -176,23 +189,16 @@ public class Whirlpool {
|
||||||
|
|
||||||
public UtxoMixData getMixData(BlockTransactionHashIndex txo) {
|
public UtxoMixData getMixData(BlockTransactionHashIndex txo) {
|
||||||
if(whirlpoolWalletService.whirlpoolWallet() != null) {
|
if(whirlpoolWalletService.whirlpoolWallet() != null) {
|
||||||
UtxoConfigPersisted config = whirlpoolWalletService.whirlpoolWallet().getUtxoConfigSupplier().getUtxoConfigPersisted(txo.getHashAsString(), (int)txo.getIndex());
|
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(txo.getHashAsString(), (int)txo.getIndex());
|
||||||
if(config != null) {
|
if (whirlpoolUtxo != null) {
|
||||||
return new UtxoMixData(config.getPoolId(), config.getMixsDone(), config.getForwarding());
|
UtxoConfig utxoConfig = whirlpoolUtxo.getUtxoConfigOrDefault();
|
||||||
|
return new UtxoMixData(utxoConfig.getMixsDone(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persistMixData() {
|
|
||||||
try {
|
|
||||||
whirlpoolWalletService.whirlpoolWallet().getUtxoConfigSupplier().persist(true);
|
|
||||||
} catch(Exception e) {
|
|
||||||
log.error("Error persisting mix data", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mix(BlockTransactionHashIndex utxo) throws WhirlpoolException {
|
public void mix(BlockTransactionHashIndex utxo) throws WhirlpoolException {
|
||||||
if(whirlpoolWalletService.whirlpoolWallet() == null) {
|
if(whirlpoolWalletService.whirlpoolWallet() == null) {
|
||||||
throw new WhirlpoolException("Whirlpool wallet not yet created");
|
throw new WhirlpoolException("Whirlpool wallet not yet created");
|
||||||
|
@ -200,7 +206,7 @@ public class Whirlpool {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(utxo.getHashAsString(), (int)utxo.getIndex());
|
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(utxo.getHashAsString(), (int)utxo.getIndex());
|
||||||
whirlpoolWalletService.whirlpoolWallet().mixNow(whirlpoolUtxo);
|
whirlpoolWalletService.whirlpoolWallet().mix(whirlpoolUtxo);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new WhirlpoolException(e.getMessage(), e);
|
throw new WhirlpoolException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
@ -260,12 +266,9 @@ public class Whirlpool {
|
||||||
}
|
}
|
||||||
|
|
||||||
private WalletUtxo getUtxo(WhirlpoolUtxo whirlpoolUtxo) {
|
private WalletUtxo getUtxo(WhirlpoolUtxo whirlpoolUtxo) {
|
||||||
Wallet wallet = AppServices.get().getWallet(whirlpoolWalletService.getWalletId());
|
Wallet wallet = AppServices.get().getWallet(walletId);
|
||||||
if(wallet != null) {
|
if(wallet != null) {
|
||||||
StandardAccount standardAccount = getStandardAccount(whirlpoolUtxo.getAccount());
|
wallet = getStandardAccountWallet(whirlpoolUtxo.getAccount(), wallet);
|
||||||
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount)) {
|
|
||||||
wallet = wallet.getChildWallet(standardAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(BlockTransactionHashIndex utxo : wallet.getWalletUtxos().keySet()) {
|
for(BlockTransactionHashIndex utxo : wallet.getWalletUtxos().keySet()) {
|
||||||
if(utxo.getHashAsString().equals(whirlpoolUtxo.getUtxo().tx_hash) && utxo.getIndex() == whirlpoolUtxo.getUtxo().tx_output_n) {
|
if(utxo.getHashAsString().equals(whirlpoolUtxo.getUtxo().tx_hash) && utxo.getIndex() == whirlpoolUtxo.getUtxo().tx_output_n) {
|
||||||
|
@ -277,6 +280,18 @@ public class Whirlpool {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Wallet getWallet(String walletId) {
|
||||||
|
return AppServices.get().getOpenWallets().entrySet().stream().filter(entry -> entry.getValue().getWalletId(entry.getKey()).equals(walletId)).map(Map.Entry::getKey).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Wallet getStandardAccountWallet(WhirlpoolAccount whirlpoolAccount, Wallet wallet) {
|
||||||
|
StandardAccount standardAccount = getStandardAccount(whirlpoolAccount);
|
||||||
|
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount)) {
|
||||||
|
wallet = wallet.getChildWallet(standardAccount);
|
||||||
|
}
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
public static StandardAccount getStandardAccount(WhirlpoolAccount whirlpoolAccount) {
|
public static StandardAccount getStandardAccount(WhirlpoolAccount whirlpoolAccount) {
|
||||||
if(whirlpoolAccount == WhirlpoolAccount.PREMIX) {
|
if(whirlpoolAccount == WhirlpoolAccount.PREMIX) {
|
||||||
return StandardAccount.WHIRLPOOL_PREMIX;
|
return StandardAccount.WHIRLPOOL_PREMIX;
|
||||||
|
@ -346,15 +361,14 @@ public class Whirlpool {
|
||||||
public void onMixSuccess(MixSuccessEvent e) {
|
public void onMixSuccess(MixSuccessEvent e) {
|
||||||
WalletUtxo walletUtxo = getUtxo(e.getWhirlpoolUtxo());
|
WalletUtxo walletUtxo = getUtxo(e.getWhirlpoolUtxo());
|
||||||
if(walletUtxo != null) {
|
if(walletUtxo != null) {
|
||||||
log.debug("Mix success, new utxo " + e.getMixSuccess().getReceiveUtxo().getHash() + ":" + e.getMixSuccess().getReceiveUtxo().getIndex());
|
log.debug("Mix success, new utxo " + e.getReceiveUtxo().getHash() + ":" + e.getReceiveUtxo().getIndex());
|
||||||
persistMixData();
|
Platform.runLater(() -> EventManager.get().post(new WhirlpoolMixSuccessEvent(walletUtxo.wallet, walletUtxo.utxo, e.getReceiveUtxo(), getReceiveNode(e, walletUtxo))));
|
||||||
Platform.runLater(() -> EventManager.get().post(new WhirlpoolMixSuccessEvent(walletUtxo.wallet, walletUtxo.utxo, e.getMixSuccess().getReceiveUtxo(), getReceiveNode(e, walletUtxo))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private WalletNode getReceiveNode(MixSuccessEvent e, WalletUtxo walletUtxo) {
|
private WalletNode getReceiveNode(MixSuccessEvent e, WalletUtxo walletUtxo) {
|
||||||
for(WalletNode walletNode : walletUtxo.wallet.getNode(KeyPurpose.RECEIVE).getChildren()) {
|
for(WalletNode walletNode : walletUtxo.wallet.getNode(KeyPurpose.RECEIVE).getChildren()) {
|
||||||
if(walletUtxo.wallet.getAddress(walletNode).toString().equals(e.getMixSuccess().getReceiveAddress())) {
|
if(walletUtxo.wallet.getAddress(walletNode).toString().equals(e.getMixProgress().getDestination().getAddress())) {
|
||||||
return walletNode;
|
return walletNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.sparrowwallet.sparrow.whirlpool.dataPersister;
|
||||||
|
|
||||||
|
import com.samourai.whirlpool.client.wallet.WhirlpoolWallet;
|
||||||
|
import com.samourai.whirlpool.client.wallet.WhirlpoolWalletConfig;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.dataPersister.DataPersister;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigPersistedSupplier;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigSupplier;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowWalletStateSupplier;
|
||||||
|
|
||||||
|
public class SparrowDataPersister implements DataPersister {
|
||||||
|
private WalletStateSupplier walletStateSupplier;
|
||||||
|
private UtxoConfigSupplier utxoConfigSupplier;
|
||||||
|
|
||||||
|
public SparrowDataPersister(WhirlpoolWallet whirlpoolWallet) throws Exception {
|
||||||
|
WhirlpoolWalletConfig config = whirlpoolWallet.getConfig();
|
||||||
|
String walletIdentifier = whirlpoolWallet.getWalletIdentifier();
|
||||||
|
this.walletStateSupplier = new SparrowWalletStateSupplier(walletIdentifier, config.getExternalDestination());
|
||||||
|
this.utxoConfigSupplier = new UtxoConfigPersistedSupplier(new SparrowUtxoConfigPersister(walletIdentifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void open() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load() throws Exception {
|
||||||
|
utxoConfigSupplier.load();
|
||||||
|
walletStateSupplier.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void persist(boolean force) throws Exception {
|
||||||
|
utxoConfigSupplier.persist(force);
|
||||||
|
walletStateSupplier.persist(force);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WalletStateSupplier getWalletStateSupplier() {
|
||||||
|
return walletStateSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UtxoConfigSupplier getUtxoConfigSupplier() {
|
||||||
|
return utxoConfigSupplier;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
package com.sparrowwallet.sparrow.whirlpool;
|
package com.sparrowwallet.sparrow.whirlpool.dataPersister;
|
||||||
|
|
||||||
import com.google.common.collect.MapDifference;
|
import com.google.common.collect.MapDifference;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigData;
|
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigData;
|
||||||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigPersisted;
|
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigPersisted;
|
||||||
import com.samourai.whirlpool.client.wallet.data.utxo.UtxoConfigPersister;
|
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigPersister;
|
||||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
import com.sparrowwallet.drongo.wallet.UtxoMixData;
|
import com.sparrowwallet.drongo.wallet.UtxoMixData;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
@ -14,14 +14,14 @@ import com.sparrowwallet.sparrow.event.WalletUtxoMixesChangedEvent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
||||||
private static final Logger log = LoggerFactory.getLogger(SparrowUtxoConfigPersister.class);
|
private static final Logger log = LoggerFactory.getLogger(SparrowUtxoConfigPersister.class);
|
||||||
|
|
||||||
private final String walletId;
|
private final String walletId;
|
||||||
private long lastWrite;
|
|
||||||
|
|
||||||
public SparrowUtxoConfigPersister(String walletId) {
|
public SparrowUtxoConfigPersister(String walletId) {
|
||||||
super(walletId);
|
super(walletId);
|
||||||
|
@ -29,14 +29,14 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized UtxoConfigData load() throws Exception {
|
public synchronized UtxoConfigData read() throws Exception {
|
||||||
Wallet wallet = getWallet();
|
Wallet wallet = getWallet();
|
||||||
if(wallet == null) {
|
if(wallet == null) {
|
||||||
throw new IllegalStateException("Can't find wallet with walletId " + walletId);
|
throw new IllegalStateException("Can't find wallet with walletId " + walletId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, UtxoConfigPersisted> utxoConfigs = wallet.getUtxoMixes().entrySet().stream()
|
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()),
|
.collect(Collectors.toMap(entry -> entry.getKey().toString(), entry -> new UtxoConfigPersisted(entry.getValue().getMixesDone(), entry.getValue().getExpired()),
|
||||||
(u, v) -> { throw new IllegalStateException("Duplicate utxo config hashes"); },
|
(u, v) -> { throw new IllegalStateException("Duplicate utxo config hashes"); },
|
||||||
HashMap::new));
|
HashMap::new));
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void write(UtxoConfigData data) throws Exception {
|
protected void doWrite(UtxoConfigData data) throws Exception {
|
||||||
Wallet wallet = getWallet();
|
Wallet wallet = getWallet();
|
||||||
if(wallet == null) {
|
if(wallet == null) {
|
||||||
//Wallet is already closed
|
//Wallet is already closed
|
||||||
|
@ -53,7 +53,7 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
||||||
|
|
||||||
Map<String, UtxoConfigPersisted> currentData = new HashMap<>(data.getUtxoConfigs());
|
Map<String, UtxoConfigPersisted> currentData = new HashMap<>(data.getUtxoConfigs());
|
||||||
Map<Sha256Hash, UtxoMixData> changedUtxoMixes = currentData.entrySet().stream()
|
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()),
|
.collect(Collectors.toMap(entry -> Sha256Hash.wrap(entry.getKey()), entry -> new UtxoMixData(entry.getValue().getMixsDone(), entry.getValue().getExpired()),
|
||||||
(u, v) -> { throw new IllegalStateException("Duplicate utxo config hashes"); },
|
(u, v) -> { throw new IllegalStateException("Duplicate utxo config hashes"); },
|
||||||
HashMap::new));
|
HashMap::new));
|
||||||
|
|
||||||
|
@ -63,15 +63,9 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
||||||
wallet.getUtxoMixes().keySet().removeAll(removedUtxoMixes.keySet());
|
wallet.getUtxoMixes().keySet().removeAll(removedUtxoMixes.keySet());
|
||||||
|
|
||||||
EventManager.get().post(new WalletUtxoMixesChangedEvent(wallet, changedUtxoMixes, removedUtxoMixes));
|
EventManager.get().post(new WalletUtxoMixesChangedEvent(wallet, changedUtxoMixes, removedUtxoMixes));
|
||||||
lastWrite = System.currentTimeMillis();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getLastWrite() {
|
|
||||||
return lastWrite;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,108 +1,69 @@
|
||||||
package com.sparrowwallet.sparrow.whirlpool;
|
package com.sparrowwallet.sparrow.whirlpool.dataSource;
|
||||||
|
|
||||||
import com.samourai.wallet.api.backend.BackendApi;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.samourai.wallet.api.backend.MinerFee;
|
import com.samourai.wallet.api.backend.MinerFee;
|
||||||
import com.samourai.wallet.api.backend.MinerFeeTarget;
|
import com.samourai.wallet.api.backend.MinerFeeTarget;
|
||||||
import com.samourai.wallet.api.backend.beans.*;
|
import com.samourai.wallet.api.backend.beans.UnspentOutput;
|
||||||
|
import com.samourai.wallet.api.backend.beans.WalletResponse;
|
||||||
|
import com.samourai.wallet.hd.HD_Wallet;
|
||||||
|
import com.samourai.whirlpool.client.wallet.WhirlpoolWallet;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.dataPersister.DataPersister;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.dataSource.WalletResponseDataSource;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.minerFee.MinerFeeSupplier;
|
||||||
import com.sparrowwallet.drongo.ExtendedKey;
|
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.address.Address;
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
import com.sparrowwallet.drongo.protocol.*;
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
|
import com.sparrowwallet.drongo.protocol.TransactionInput;
|
||||||
|
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.NewBlockEvent;
|
||||||
|
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
|
||||||
|
import com.sparrowwallet.sparrow.event.WalletHistoryChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
public class SparrowDataSource extends WalletResponseDataSource {
|
||||||
public class SparrowBackendApi extends BackendApi {
|
private static final Logger log = LoggerFactory.getLogger(SparrowDataSource.class);
|
||||||
private static final Logger log = LoggerFactory.getLogger(SparrowBackendApi.class);
|
|
||||||
private static final int FALLBACK_FEE_RATE = 75;
|
|
||||||
|
|
||||||
public SparrowBackendApi() {
|
private final String walletIdentifierPrefix;
|
||||||
super(null, null);
|
|
||||||
|
public SparrowDataSource(
|
||||||
|
WhirlpoolWallet whirlpoolWallet,
|
||||||
|
HD_Wallet bip44w,
|
||||||
|
DataPersister dataPersister)
|
||||||
|
throws Exception {
|
||||||
|
super(whirlpoolWallet, bip44w, dataPersister);
|
||||||
|
|
||||||
|
// prefix matching <prefix>:master, :Premix, :Postmix
|
||||||
|
this.walletIdentifierPrefix = getWhirlpoolWallet().getWalletIdentifier().replace(":master", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TxsResponse fetchTxs(String[] zpubs, int page, int count) throws Exception {
|
public void open() throws Exception {
|
||||||
List<TxsResponse.Tx> txes = new ArrayList<>();
|
super.open();
|
||||||
|
EventManager.get().register(this);
|
||||||
for(String zpub : zpubs) {
|
|
||||||
Wallet wallet = getWallet(zpub);
|
|
||||||
if(wallet == null) {
|
|
||||||
log.debug("No wallet for " + zpub + " found");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(BlockTransaction blockTransaction : wallet.getTransactions().values()) {
|
|
||||||
TxsResponse.Tx tx = new TxsResponse.Tx();
|
|
||||||
tx.block_height = blockTransaction.getHeight();
|
|
||||||
tx.hash = blockTransaction.getHashAsString();
|
|
||||||
tx.locktime = blockTransaction.getTransaction().getLocktime();
|
|
||||||
tx.time = blockTransaction.getDate().getTime();
|
|
||||||
tx.version = (int)blockTransaction.getTransaction().getVersion();
|
|
||||||
|
|
||||||
tx.inputs = new TxsResponse.TxInput[blockTransaction.getTransaction().getInputs().size()];
|
|
||||||
for(int i = 0; i < blockTransaction.getTransaction().getInputs().size(); i++) {
|
|
||||||
TransactionInput txInput = blockTransaction.getTransaction().getInputs().get(i);
|
|
||||||
tx.inputs[i] = new TxsResponse.TxInput();
|
|
||||||
tx.inputs[i].vin = txInput.getIndex();
|
|
||||||
tx.inputs[i].sequence = txInput.getSequenceNumber();
|
|
||||||
tx.inputs[i].prev_out = new TxsResponse.TxOut();
|
|
||||||
tx.inputs[i].prev_out.txid = txInput.getOutpoint().getHash().toString();
|
|
||||||
tx.inputs[i].prev_out.vout = (int)txInput.getOutpoint().getIndex();
|
|
||||||
|
|
||||||
BlockTransaction spentTransaction = wallet.getTransactions().get(txInput.getOutpoint().getHash());
|
|
||||||
if(spentTransaction != null) {
|
|
||||||
TransactionOutput spentOutput = spentTransaction.getTransaction().getOutputs().get((int)txInput.getOutpoint().getIndex());
|
|
||||||
tx.inputs[i].prev_out.value = spentOutput.getValue();
|
|
||||||
Address[] addresses = spentOutput.getScript().getToAddresses();
|
|
||||||
if(addresses.length > 0) {
|
|
||||||
tx.inputs[i].prev_out.addr = addresses[0].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.out = new TxsResponse.TxOutput[blockTransaction.getTransaction().getOutputs().size()];
|
|
||||||
for(int i = 0; i < blockTransaction.getTransaction().getOutputs().size(); i++) {
|
|
||||||
TransactionOutput txOutput = blockTransaction.getTransaction().getOutputs().get(i);
|
|
||||||
tx.out[i].n = txOutput.getIndex();
|
|
||||||
tx.out[i].value = txOutput.getValue();
|
|
||||||
Address[] addresses = txOutput.getScript().getToAddresses();
|
|
||||||
if(addresses.length > 0) {
|
|
||||||
tx.out[i].addr = addresses[0].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
txes.add(tx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TxsResponse.Tx> pageTxes;
|
|
||||||
if(txes.size() < count) {
|
|
||||||
pageTxes = txes;
|
|
||||||
} else {
|
|
||||||
pageTxes = txes.subList(page * count, Math.min((page * count) + count, txes.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TxsResponse txsResponse = new TxsResponse();
|
|
||||||
txsResponse.n_tx = txes.size();
|
|
||||||
txsResponse.page = page;
|
|
||||||
txsResponse.n_tx_page = pageTxes.size();
|
|
||||||
txsResponse.txs = pageTxes.toArray(new TxsResponse.Tx[0]);
|
|
||||||
|
|
||||||
return txsResponse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WalletResponse fetchWallet(String[] zpubs) throws Exception {
|
public void close() throws Exception {
|
||||||
|
EventManager.get().unregister(this);
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected WalletResponse fetchWalletResponse() throws Exception {
|
||||||
WalletResponse walletResponse = new WalletResponse();
|
WalletResponse walletResponse = new WalletResponse();
|
||||||
walletResponse.wallet = new WalletResponse.Wallet();
|
walletResponse.wallet = new WalletResponse.Wallet();
|
||||||
|
|
||||||
|
@ -113,6 +74,7 @@ public class SparrowBackendApi extends BackendApi {
|
||||||
List<UnspentOutput> unspentOutputs = new ArrayList<>();
|
List<UnspentOutput> unspentOutputs = new ArrayList<>();
|
||||||
int storedBlockHeight = 0;
|
int storedBlockHeight = 0;
|
||||||
|
|
||||||
|
String[] zpubs = getWalletSupplier().getPubs(true);
|
||||||
for(String zpub : zpubs) {
|
for(String zpub : zpubs) {
|
||||||
Wallet wallet = getWallet(zpub);
|
Wallet wallet = getWallet(zpub);
|
||||||
if(wallet == null) {
|
if(wallet == null) {
|
||||||
|
@ -196,23 +158,14 @@ public class SparrowBackendApi extends BackendApi {
|
||||||
walletResponse.info.latest_block.time = AppServices.getLatestBlockHeader() == null ? 1 : AppServices.getLatestBlockHeader().getTime();
|
walletResponse.info.latest_block.time = AppServices.getLatestBlockHeader() == null ? 1 : AppServices.getLatestBlockHeader().getTime();
|
||||||
|
|
||||||
walletResponse.info.fees = new LinkedHashMap<>();
|
walletResponse.info.fees = new LinkedHashMap<>();
|
||||||
|
MinerFee minerFee = getMinerFeeSupplier().getValue();
|
||||||
for(MinerFeeTarget target : MinerFeeTarget.values()) {
|
for(MinerFeeTarget target : MinerFeeTarget.values()) {
|
||||||
walletResponse.info.fees.put(target.getValue(), AppServices.getTargetBlockFeeRates() == null ? FALLBACK_FEE_RATE : getMinimumFeeForTarget(Integer.parseInt(target.getValue())));
|
walletResponse.info.fees.put(target.getValue(), minerFee.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
return walletResponse;
|
return walletResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MinerFee fetchMinerFee() throws Exception {
|
|
||||||
Map<String, Integer> fees = new LinkedHashMap<>();
|
|
||||||
for(MinerFeeTarget target : MinerFeeTarget.values()) {
|
|
||||||
fees.put(target.getValue(), AppServices.getTargetBlockFeeRates() == null ? FALLBACK_FEE_RATE : getMinimumFeeForTarget(Integer.parseInt(target.getValue())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MinerFee(fees);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pushTx(String txHex) throws Exception {
|
public void pushTx(String txHex) throws Exception {
|
||||||
Transaction transaction = new Transaction(Utils.hexToBytes(txHex));
|
Transaction transaction = new Transaction(Utils.hexToBytes(txHex));
|
||||||
|
@ -221,25 +174,8 @@ public class SparrowBackendApi extends BackendApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean testConnectivity() {
|
public MinerFeeSupplier getMinerFeeSupplier() {
|
||||||
return AppServices.isConnected();
|
return SparrowMinerFeeSupplier.getInstance();
|
||||||
}
|
|
||||||
|
|
||||||
private Integer getMinimumFeeForTarget(int targetBlocks) {
|
|
||||||
List<Map.Entry<Integer, Double>> feeRates = new ArrayList<>(AppServices.getTargetBlockFeeRates().entrySet());
|
|
||||||
Collections.reverse(feeRates);
|
|
||||||
for(Map.Entry<Integer, Double> feeRate : feeRates) {
|
|
||||||
if(feeRate.getKey() <= targetBlocks) {
|
|
||||||
return feeRate.getValue().intValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return feeRates.get(0).getValue().intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initBip84(String zpub) throws Exception {
|
|
||||||
//nothing required
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Wallet getWallet(String zpub) {
|
private Wallet getWallet(String zpub) {
|
||||||
|
@ -255,23 +191,33 @@ public class SparrowBackendApi extends BackendApi {
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Subscribe
|
||||||
public List<UnspentOutput> fetchUtxos(String zpub) throws Exception {
|
public void walletHistoryChanged(WalletHistoryChangedEvent event) {
|
||||||
throw new UnsupportedOperationException();
|
refreshWallet(event.getWalletId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Subscribe
|
||||||
public List<UnspentOutput> fetchUtxos(String[] zpubs) throws Exception {
|
public void walletAddressesChanged(WalletAddressesChangedEvent event) {
|
||||||
throw new UnsupportedOperationException();
|
refreshWallet(event.getWalletId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Subscribe
|
||||||
public Map<String, MultiAddrResponse.Address> fetchAddresses(String[] zpubs) throws Exception {
|
public void newBlock(NewBlockEvent event) {
|
||||||
throw new UnsupportedOperationException();
|
try {
|
||||||
|
refresh();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void refreshWallet(String eventWalletId) {
|
||||||
public MultiAddrResponse.Address fetchAddress(String zpub) throws Exception {
|
try {
|
||||||
throw new UnsupportedOperationException();
|
// match <prefix>:master, :Premix, :Postmix
|
||||||
|
if (eventWalletId.startsWith(walletIdentifierPrefix)) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.sparrowwallet.sparrow.whirlpool.dataSource;
|
||||||
|
|
||||||
|
import com.samourai.wallet.client.indexHandler.AbstractIndexHandler;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
|
|
||||||
|
public class SparrowIndexHandler extends AbstractIndexHandler {
|
||||||
|
private WalletNode walletNode;
|
||||||
|
private int defaultValue;
|
||||||
|
|
||||||
|
public SparrowIndexHandler(WalletNode walletNode) {
|
||||||
|
this(walletNode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SparrowIndexHandler(WalletNode walletNode, int defaultValue) {
|
||||||
|
this.walletNode = walletNode;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int get() {
|
||||||
|
Integer currentIndex = walletNode.getHighestUsedIndex();
|
||||||
|
int nextIndex = currentIndex == null ? defaultValue : currentIndex + 1;
|
||||||
|
return nextIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getAndIncrement() {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void set(int value) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.sparrowwallet.sparrow.whirlpool.dataSource;
|
||||||
|
|
||||||
|
import com.samourai.wallet.api.backend.MinerFee;
|
||||||
|
import com.samourai.wallet.api.backend.MinerFeeTarget;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.minerFee.MinerFeeSupplier;
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class SparrowMinerFeeSupplier implements MinerFeeSupplier {
|
||||||
|
private static final int FALLBACK_FEE_RATE = 75;
|
||||||
|
|
||||||
|
public static SparrowMinerFeeSupplier instance;
|
||||||
|
|
||||||
|
public static SparrowMinerFeeSupplier getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new SparrowMinerFeeSupplier();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SparrowMinerFeeSupplier() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFee(MinerFeeTarget feeTarget) {
|
||||||
|
if (AppServices.getTargetBlockFeeRates() == null) {
|
||||||
|
return FALLBACK_FEE_RATE;
|
||||||
|
}
|
||||||
|
return getMinimumFeeForTarget(Integer.parseInt(feeTarget.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MinerFee getValue() {
|
||||||
|
Map<String, Integer> fees = new LinkedHashMap<>();
|
||||||
|
for (MinerFeeTarget minerFeeTarget : MinerFeeTarget.values()) {
|
||||||
|
fees.put(minerFeeTarget.getValue(), getFee(minerFeeTarget));
|
||||||
|
}
|
||||||
|
return new MinerFee(fees);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getMinimumFeeForTarget(int targetBlocks) {
|
||||||
|
List<Map.Entry<Integer, Double>> feeRates = new ArrayList<>(AppServices.getTargetBlockFeeRates().entrySet());
|
||||||
|
Collections.reverse(feeRates);
|
||||||
|
for(Map.Entry<Integer, Double> feeRate : feeRates) {
|
||||||
|
if(feeRate.getKey() <= targetBlocks) {
|
||||||
|
return feeRate.getValue().intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return feeRates.get(0).getValue().intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.sparrowwallet.sparrow.whirlpool.dataSource;
|
||||||
|
|
||||||
|
import com.samourai.wallet.client.indexHandler.IIndexHandler;
|
||||||
|
import com.samourai.wallet.hd.AddressType;
|
||||||
|
import com.samourai.wallet.hd.Chain;
|
||||||
|
import com.samourai.whirlpool.client.wallet.beans.ExternalDestination;
|
||||||
|
import com.samourai.whirlpool.client.wallet.beans.WhirlpoolAccount;
|
||||||
|
import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier;
|
||||||
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SparrowWalletStateSupplier implements WalletStateSupplier {
|
||||||
|
private String walletId;
|
||||||
|
private Map<String, IIndexHandler> indexHandlerWallets;
|
||||||
|
// private int externalIndexDefault;
|
||||||
|
|
||||||
|
public SparrowWalletStateSupplier(String walletId, ExternalDestination externalDestination) throws Exception {
|
||||||
|
this.walletId = walletId;
|
||||||
|
this.indexHandlerWallets = new LinkedHashMap();
|
||||||
|
// this.externalIndexDefault = externalDestination != null ? externalDestination.getStartIndex() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIndexHandler getIndexHandlerWallet(WhirlpoolAccount whirlpoolAccount, AddressType addressType, Chain chain) {
|
||||||
|
String key = mapKey(whirlpoolAccount, addressType, chain);
|
||||||
|
IIndexHandler indexHandler = indexHandlerWallets.get(key);
|
||||||
|
if (indexHandler == null) {
|
||||||
|
WalletNode walletNode = findWalletNode(whirlpoolAccount, addressType, chain);
|
||||||
|
indexHandler = new SparrowIndexHandler(walletNode, 0);
|
||||||
|
indexHandlerWallets.put(key, indexHandler);
|
||||||
|
}
|
||||||
|
return indexHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIndexHandler getIndexHandlerExternal() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInitialized(boolean b) {
|
||||||
|
// nothing required
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWalletIndex(WhirlpoolAccount whirlpoolAccount, AddressType addressType, Chain chain, int i) throws Exception {
|
||||||
|
// nothing required
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load() throws Exception {
|
||||||
|
// nothing required
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean persist(boolean b) throws Exception {
|
||||||
|
// nothing required
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mapKey(WhirlpoolAccount whirlpoolAccount, AddressType addressType, Chain chain) {
|
||||||
|
return whirlpoolAccount.name()+"_"+addressType.getPurpose()+"_"+chain.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private WalletNode findWalletNode(WhirlpoolAccount whirlpoolAccount, AddressType addressType, Chain chain) {
|
||||||
|
Wallet wallet = getWallet();
|
||||||
|
if(wallet == null) {
|
||||||
|
throw new IllegalStateException("Can't find wallet with walletId " + walletId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// account
|
||||||
|
Wallet childWallet = Whirlpool.getStandardAccountWallet(whirlpoolAccount, wallet);
|
||||||
|
|
||||||
|
// purpose
|
||||||
|
KeyPurpose keyPurpose = chain == Chain.RECEIVE ? KeyPurpose.RECEIVE : KeyPurpose.CHANGE;
|
||||||
|
return childWallet.getNode(keyPurpose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Wallet getWallet() {
|
||||||
|
return Whirlpool.getWallet(walletId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
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);
|
drop table if exists utxoMixData;
|
||||||
|
create table utxoMixData (id identity not null, hash binary(32) not null, mixesDone integer not null default 0, expired bigint, wallet bigint not null);
|
||||||
|
|
Loading…
Reference in a new issue