mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
upgrade to whirlpool-client 0.23.30-early4 + extlibj 0.0.19-dsk3
This commit is contained in:
parent
772370808c
commit
050c4fc31e
17 changed files with 331 additions and 284 deletions
|
@ -91,7 +91,7 @@ dependencies {
|
|||
implementation('org.slf4j:jul-to-slf4j:1.7.30') {
|
||||
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')
|
||||
}
|
||||
|
||||
|
@ -387,7 +387,7 @@ extraJavaModuleInfo {
|
|||
module('cbor-0.9.jar', 'co.nstant.in.cbor', '0.9') {
|
||||
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('net.sourceforge.streamsupport')
|
||||
requires('org.slf4j')
|
||||
|
@ -399,6 +399,7 @@ extraJavaModuleInfo {
|
|||
exports('com.samourai.tor.client')
|
||||
exports('com.samourai.wallet.api.backend')
|
||||
exports('com.samourai.wallet.api.backend.beans')
|
||||
exports('com.samourai.wallet.client.indexHandler')
|
||||
exports('com.samourai.wallet.hd')
|
||||
exports('com.samourai.whirlpool.client.event')
|
||||
exports('com.samourai.whirlpool.client.wallet')
|
||||
|
@ -411,6 +412,7 @@ extraJavaModuleInfo {
|
|||
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.protocol.beans')
|
||||
exports('com.samourai.whirlpool.protocol.rest')
|
||||
|
|
|
@ -469,7 +469,7 @@ public class AppServices {
|
|||
Whirlpool whirlpool = whirlpoolMap.get(walletId);
|
||||
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()));
|
||||
whirlpool = new Whirlpool(Network.get(), torProxy, Config.get().getScode(), 1);
|
||||
whirlpool = new Whirlpool(Network.get(), torProxy, Config.get().getScode());
|
||||
whirlpoolMap.put(walletId, whirlpool);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,9 +40,9 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
|||
setContextMenu(null);
|
||||
}
|
||||
|
||||
if(mixStatus.getPoolId() != null) {
|
||||
if(mixStatus.getMixProgress() != null) {
|
||||
Tooltip tooltip = new Tooltip();
|
||||
tooltip.setText("Pool: " + mixStatus.getPoolId().replace("btc", " BTC"));
|
||||
tooltip.setText("Pool: " + mixStatus.getMixProgress().getPoolId().replace("btc", " BTC"));
|
||||
setTooltip(tooltip);
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
|||
private void setMixProgress(MixProgress mixProgress) {
|
||||
if(mixProgress.getMixStep() != MixStep.FAIL) {
|
||||
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);
|
||||
Tooltip tt = new Tooltip();
|
||||
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;
|
||||
|
||||
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)
|
||||
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)
|
||||
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")
|
||||
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 = ?")
|
||||
void updateUtxoMixData(byte[] hash, String poolId, int mixesDone, Long forwarding, long wallet, long id);
|
||||
@SqlUpdate("update utxoMixData set hash = ?, mixesDone = ?, expired = ?, wallet = ? where id = ?")
|
||||
void updateUtxoMixData(byte[] hash, int mixesDone, Long expired, long wallet, long id);
|
||||
|
||||
@SqlUpdate("delete from utxoMixData where id in (<ids>)")
|
||||
void deleteUtxoMixData(@BindList("ids") List<Long> ids);
|
||||
|
@ -45,11 +45,11 @@ public interface UtxoMixDataDao {
|
|||
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());
|
||||
long id = insertUtxoMixData(hash.getBytes(), utxoMixData.getMixesDone(), utxoMixData.getExpired(), 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);
|
||||
updateUtxoMixData(hash.getBytes(), utxoMixData.getMixesDone(), utxoMixData.getExpired(), wallet.getId(), 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 {
|
||||
Sha256Hash hash = Sha256Hash.wrap(rs.getBytes("hash"));
|
||||
|
||||
Long forwarding = rs.getLong("forwarding");
|
||||
Long expired = rs.getLong("expired");
|
||||
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"));
|
||||
|
||||
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() {
|
||||
return getUtxoMixData().getMixesDone();
|
||||
}
|
||||
|
||||
public String getPoolId() {
|
||||
return getUtxoMixData().getPoolId();
|
||||
}
|
||||
|
||||
public MixProgress getMixProgress() {
|
||||
return mixProgress;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -6,21 +6,19 @@ import com.samourai.tor.client.TorClientService;
|
|||
import com.samourai.wallet.api.backend.beans.UnspentOutput;
|
||||
import com.samourai.wallet.hd.HD_Wallet;
|
||||
import com.samourai.wallet.hd.HD_WalletFactoryGeneric;
|
||||
import com.samourai.whirlpool.client.event.MixFailEvent;
|
||||
import com.samourai.whirlpool.client.event.MixSuccessEvent;
|
||||
import com.samourai.whirlpool.client.event.WalletStartEvent;
|
||||
import com.samourai.whirlpool.client.event.WalletStopEvent;
|
||||
import com.samourai.whirlpool.client.tx0.*;
|
||||
import com.samourai.whirlpool.client.event.*;
|
||||
import com.samourai.whirlpool.client.tx0.Tx0;
|
||||
import com.samourai.whirlpool.client.tx0.Tx0Config;
|
||||
import com.samourai.whirlpool.client.tx0.Tx0Preview;
|
||||
import com.samourai.whirlpool.client.wallet.WhirlpoolEventService;
|
||||
import com.samourai.whirlpool.client.wallet.WhirlpoolWallet;
|
||||
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.data.dataPersister.DataPersisterFactory;
|
||||
import com.samourai.whirlpool.client.wallet.data.dataPersister.FileDataPersister;
|
||||
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.utxo.UtxoConfigPersisted;
|
||||
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.beans.Pool;
|
||||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
|
@ -40,6 +38,8 @@ import com.sparrowwallet.sparrow.EventManager;
|
|||
import com.sparrowwallet.sparrow.event.WhirlpoolMixEvent;
|
||||
import com.sparrowwallet.sparrow.event.WhirlpoolMixSuccessEvent;
|
||||
import com.sparrowwallet.sparrow.wallet.UtxoEntry;
|
||||
import com.sparrowwallet.sparrow.whirlpool.dataPersister.SparrowDataPersister;
|
||||
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -50,6 +50,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -61,54 +62,55 @@ public class Whirlpool {
|
|||
private final JavaHttpClientService httpClientService;
|
||||
private final JavaStompClientService stompClientService;
|
||||
private final TorClientService torClientService;
|
||||
private final SparrowWhirlpoolWalletService whirlpoolWalletService;
|
||||
private final WhirlpoolWalletService whirlpoolWalletService;
|
||||
private final WhirlpoolWalletConfig config;
|
||||
private HD_Wallet hdWallet;
|
||||
private String walletId;
|
||||
|
||||
private BooleanProperty mixingProperty = new SimpleBooleanProperty(false);
|
||||
|
||||
public Whirlpool(Network network, HostAndPort torProxy, String sCode, int maxClients) {
|
||||
public Whirlpool(Network network, HostAndPort torProxy, String sCode) {
|
||||
this.torProxy = torProxy;
|
||||
this.whirlpoolServer = WhirlpoolServer.valueOf(network.getName().toUpperCase());
|
||||
this.httpClientService = new JavaHttpClientService(torProxy);
|
||||
this.stompClientService = new JavaStompClientService(httpClientService);
|
||||
this.torClientService = new WhirlpoolTorClientService();
|
||||
|
||||
DataPersisterFactory dataPersisterFactory = (config, bip44w, walletIdentifier) -> new FileDataPersister(config, bip44w, walletIdentifier);
|
||||
DataSourceFactory dataSourceFactory = (config, bip44w, walletIdentifier, dataPersister) -> new SparrowDataSource(config, bip44w, walletIdentifier, dataPersister);
|
||||
this.whirlpoolWalletService = new SparrowWhirlpoolWalletService(dataPersisterFactory, dataSourceFactory);
|
||||
this.config = computeWhirlpoolWalletConfig(sCode, maxClients);
|
||||
this.whirlpoolWalletService = new WhirlpoolWalletService();
|
||||
this.config = computeWhirlpoolWalletConfig(sCode);
|
||||
|
||||
WhirlpoolEventService.getInstance().register(this);
|
||||
}
|
||||
|
||||
private WhirlpoolWalletConfig computeWhirlpoolWalletConfig(String sCode, int maxClients) {
|
||||
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);
|
||||
String serverUrl = whirlpoolServer.getServerUrl(onion);
|
||||
ServerApi serverApi = new ServerApi(serverUrl, httpClientService);
|
||||
|
||||
WhirlpoolWalletConfig whirlpoolWalletConfig = new WhirlpoolWalletConfig(httpClientService, stompClientService, torClientService, serverApi, whirlpoolServer.getParams(), false);
|
||||
WhirlpoolWalletConfig whirlpoolWalletConfig = new WhirlpoolWalletConfig(dataSourceFactory, httpClientService, stompClientService, torClientService, serverApi, whirlpoolServer.getParams(), false);
|
||||
whirlpoolWalletConfig.setDataPersisterFactory(dataPersisterFactory);
|
||||
whirlpoolWalletConfig.setScode(sCode);
|
||||
|
||||
return whirlpoolWalletConfig;
|
||||
}
|
||||
|
||||
public Collection<Pool> getPools() throws Exception {
|
||||
Tx0ParamService tx0ParamService = getTx0ParamService();
|
||||
PoolData poolData = new PoolData(config.getServerApi().fetchPools(), tx0ParamService);
|
||||
return poolData.getPools();
|
||||
WhirlpoolWallet whirlpoolWallet = getWhirlpoolWallet();
|
||||
return whirlpoolWallet.getPoolSupplier().getPools();
|
||||
}
|
||||
|
||||
public Tx0Preview getTx0Preview(Pool pool, Collection<UnspentOutput> utxos) throws Exception {
|
||||
WhirlpoolWallet whirlpoolWallet = getWhirlpoolWallet();
|
||||
|
||||
Tx0Config tx0Config = new Tx0Config();
|
||||
tx0Config.setChangeWallet(WhirlpoolAccount.BADBANK);
|
||||
Tx0FeeTarget tx0FeeTarget = Tx0FeeTarget.BLOCKS_4;
|
||||
Tx0FeeTarget mixFeeTarget = Tx0FeeTarget.BLOCKS_4;
|
||||
|
||||
Tx0ParamService tx0ParamService = getTx0ParamService();
|
||||
|
||||
Tx0Service tx0Service = new Tx0Service(config);
|
||||
return tx0Service.tx0Preview(utxos, tx0Config, tx0ParamService.getTx0Param(pool, tx0FeeTarget, mixFeeTarget));
|
||||
return whirlpoolWallet.tx0Preview(pool, tx0Config, utxos, tx0FeeTarget, mixFeeTarget);
|
||||
}
|
||||
|
||||
public Tx0 broadcastTx0(Pool pool, Collection<BlockTransactionHashIndex> utxos) throws Exception {
|
||||
|
@ -129,17 +131,6 @@ public class Whirlpool {
|
|||
return whirlpoolWallet.tx0(whirlpoolUtxos, pool, tx0Config, tx0FeeTarget, mixFeeTarget);
|
||||
}
|
||||
|
||||
private Tx0ParamService getTx0ParamService() {
|
||||
try {
|
||||
SparrowMinerFeeSupplier minerFeeSupplier = SparrowMinerFeeSupplier.getInstance();
|
||||
return new Tx0ParamService(minerFeeSupplier, config);
|
||||
} catch(Exception e) {
|
||||
log.error("Error fetching miner fees", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setHDWallet(String walletId, Wallet wallet) {
|
||||
if(wallet.isEncrypted()) {
|
||||
throw new IllegalStateException("Wallet cannot be encrypted");
|
||||
|
@ -153,7 +144,7 @@ public class Whirlpool {
|
|||
String passphrase = keystore.getSeed().getPassphrase().asString();
|
||||
HD_WalletFactoryGeneric hdWalletFactory = HD_WalletFactoryGeneric.getInstance();
|
||||
byte[] seed = hdWalletFactory.computeSeedFromWords(words);
|
||||
whirlpoolWalletService.setWalletId(walletId);
|
||||
this.walletId = walletId;
|
||||
hdWallet = new HD_Wallet(purpose, words, config.getNetworkParameters(), seed, passphrase, 1);
|
||||
} catch(Exception e) {
|
||||
throw new IllegalStateException("Could not create Whirlpool HD wallet ", e);
|
||||
|
@ -161,8 +152,8 @@ public class Whirlpool {
|
|||
}
|
||||
|
||||
public WhirlpoolWallet getWhirlpoolWallet() throws WhirlpoolException {
|
||||
if(whirlpoolWalletService.getWhirlpoolWalletOrNull() != null) {
|
||||
return whirlpoolWalletService.getWhirlpoolWalletOrNull();
|
||||
if(whirlpoolWalletService.whirlpoolWallet() != null) {
|
||||
return whirlpoolWalletService.whirlpoolWallet();
|
||||
}
|
||||
|
||||
if(hdWallet == null) {
|
||||
|
@ -170,7 +161,8 @@ public class Whirlpool {
|
|||
}
|
||||
|
||||
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) {
|
||||
throw new WhirlpoolException("Could not create whirlpool wallet ", e);
|
||||
}
|
||||
|
@ -184,23 +176,16 @@ public class Whirlpool {
|
|||
|
||||
public UtxoMixData getMixData(BlockTransactionHashIndex txo) {
|
||||
if(whirlpoolWalletService.whirlpoolWallet() != null) {
|
||||
UtxoConfigPersisted config = whirlpoolWalletService.whirlpoolWallet().getUtxoConfigSupplier().getUtxoConfigPersisted(txo.getHashAsString(), (int)txo.getIndex());
|
||||
if(config != null) {
|
||||
return new UtxoMixData(config.getPoolId(), config.getMixsDone(), config.getForwarding());
|
||||
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(txo.getHashAsString(), (int)txo.getIndex());
|
||||
if (whirlpoolUtxo != null) {
|
||||
UtxoConfig utxoConfig = whirlpoolUtxo.getUtxoConfigOrDefault();
|
||||
return new UtxoMixData(utxoConfig.getMixsDone(), 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 {
|
||||
if(whirlpoolWalletService.whirlpoolWallet() == null) {
|
||||
throw new WhirlpoolException("Whirlpool wallet not yet created");
|
||||
|
@ -208,7 +193,7 @@ public class Whirlpool {
|
|||
|
||||
try {
|
||||
WhirlpoolUtxo whirlpoolUtxo = whirlpoolWalletService.whirlpoolWallet().getUtxoSupplier().findUtxo(utxo.getHashAsString(), (int)utxo.getIndex());
|
||||
whirlpoolWalletService.whirlpoolWallet().mixNow(whirlpoolUtxo);
|
||||
whirlpoolWalletService.whirlpoolWallet().mix(whirlpoolUtxo);
|
||||
} catch(Exception e) {
|
||||
throw new WhirlpoolException(e.getMessage(), e);
|
||||
}
|
||||
|
@ -255,11 +240,11 @@ public class Whirlpool {
|
|||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
if(whirlpoolWalletService.getWhirlpoolWalletOrNull() == null) {
|
||||
if(whirlpoolWalletService.whirlpoolWallet() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return whirlpoolWalletService.getWhirlpoolWalletOrNull().isStarted();
|
||||
return whirlpoolWalletService.whirlpoolWallet().isStarted();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
@ -268,12 +253,9 @@ public class Whirlpool {
|
|||
}
|
||||
|
||||
private WalletUtxo getUtxo(WhirlpoolUtxo whirlpoolUtxo) {
|
||||
Wallet wallet = AppServices.get().getWallet(whirlpoolWalletService.getWalletId());
|
||||
Wallet wallet = AppServices.get().getWallet(walletId);
|
||||
if(wallet != null) {
|
||||
StandardAccount standardAccount = getStandardAccount(whirlpoolUtxo.getAccount());
|
||||
if(StandardAccount.WHIRLPOOL_ACCOUNTS.contains(standardAccount)) {
|
||||
wallet = wallet.getChildWallet(standardAccount);
|
||||
}
|
||||
wallet = getStandardAccountWallet(whirlpoolUtxo.getAccount(), wallet);
|
||||
|
||||
for(BlockTransactionHashIndex utxo : wallet.getWalletUtxos().keySet()) {
|
||||
if(utxo.getHashAsString().equals(whirlpoolUtxo.getUtxo().tx_hash) && utxo.getIndex() == whirlpoolUtxo.getUtxo().tx_output_n) {
|
||||
|
@ -285,6 +267,18 @@ public class Whirlpool {
|
|||
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) {
|
||||
if(whirlpoolAccount == WhirlpoolAccount.PREMIX) {
|
||||
return StandardAccount.WHIRLPOOL_PREMIX;
|
||||
|
@ -354,15 +348,14 @@ public class Whirlpool {
|
|||
public void onMixSuccess(MixSuccessEvent e) {
|
||||
WalletUtxo walletUtxo = getUtxo(e.getWhirlpoolUtxo());
|
||||
if(walletUtxo != null) {
|
||||
log.debug("Mix success, new utxo " + e.getMixSuccess().getReceiveUtxo().getHash() + ":" + e.getMixSuccess().getReceiveUtxo().getIndex());
|
||||
persistMixData();
|
||||
Platform.runLater(() -> EventManager.get().post(new WhirlpoolMixSuccessEvent(walletUtxo.wallet, walletUtxo.utxo, e.getMixSuccess().getReceiveUtxo(), getReceiveNode(e, walletUtxo))));
|
||||
log.debug("Mix success, new utxo " + e.getReceiveUtxo().getHash() + ":" + e.getReceiveUtxo().getIndex());
|
||||
Platform.runLater(() -> EventManager.get().post(new WhirlpoolMixSuccessEvent(walletUtxo.wallet, walletUtxo.utxo, e.getReceiveUtxo(), getReceiveNode(e, walletUtxo))));
|
||||
}
|
||||
}
|
||||
|
||||
private WalletNode getReceiveNode(MixSuccessEvent e, WalletUtxo walletUtxo) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigData;
|
||||
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigPersisted;
|
||||
import com.samourai.whirlpool.client.wallet.data.utxoConfig.UtxoConfigPersister;
|
||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||
import com.sparrowwallet.drongo.wallet.UtxoMixData;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
@ -14,14 +14,14 @@ import com.sparrowwallet.sparrow.event.WalletUtxoMixesChangedEvent;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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);
|
||||
|
@ -29,14 +29,14 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized UtxoConfigData load() throws Exception {
|
||||
public synchronized UtxoConfigData read() 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()),
|
||||
.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"); },
|
||||
HashMap::new));
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(UtxoConfigData data) throws Exception {
|
||||
protected void doWrite(UtxoConfigData data) throws Exception {
|
||||
Wallet wallet = getWallet();
|
||||
if(wallet == null) {
|
||||
//Wallet is already closed
|
||||
|
@ -53,7 +53,7 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
|||
|
||||
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()),
|
||||
.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"); },
|
||||
HashMap::new));
|
||||
|
||||
|
@ -63,15 +63,9 @@ public class SparrowUtxoConfigPersister extends UtxoConfigPersister {
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
package com.sparrowwallet.sparrow.whirlpool;
|
||||
package com.sparrowwallet.sparrow.whirlpool.dataSource;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.samourai.wallet.api.backend.MinerFee;
|
||||
import com.samourai.wallet.api.backend.MinerFeeTarget;
|
||||
import com.samourai.wallet.api.backend.beans.TxsResponse;
|
||||
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.WhirlpoolWalletConfig;
|
||||
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;
|
||||
|
@ -13,14 +14,21 @@ import com.sparrowwallet.drongo.ExtendedKey;
|
|||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.Network;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||
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.BlockTransactionHashIndex;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.NewBlockEvent;
|
||||
import com.sparrowwallet.sparrow.event.NewWalletTransactionsEvent;
|
||||
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
|
||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -30,12 +38,23 @@ public class SparrowDataSource extends WalletResponseDataSource {
|
|||
private static final Logger log = LoggerFactory.getLogger(SparrowDataSource.class);
|
||||
|
||||
public SparrowDataSource(
|
||||
WhirlpoolWalletConfig config,
|
||||
WhirlpoolWallet whirlpoolWallet,
|
||||
HD_Wallet bip44w,
|
||||
String walletIdentifier,
|
||||
DataPersister dataPersister)
|
||||
throws Exception {
|
||||
super(config, bip44w, walletIdentifier, dataPersister);
|
||||
super(whirlpoolWallet, bip44w, dataPersister);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws Exception {
|
||||
super.open();
|
||||
EventManager.get().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
EventManager.get().unregister(this);
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,8 +153,9 @@ public class SparrowDataSource extends WalletResponseDataSource {
|
|||
walletResponse.info.latest_block.time = AppServices.getLatestBlockHeader() == null ? 1 : AppServices.getLatestBlockHeader().getTime();
|
||||
|
||||
walletResponse.info.fees = new LinkedHashMap<>();
|
||||
MinerFee minerFee = getMinerFeeSupplier().getValue();
|
||||
for(MinerFeeTarget target : MinerFeeTarget.values()) {
|
||||
walletResponse.info.fees.put(target.getValue(), getMinerFeeSupplier().getFee(target));
|
||||
walletResponse.info.fees.put(target.getValue(), minerFee.get(target));
|
||||
}
|
||||
|
||||
return walletResponse;
|
||||
|
@ -148,77 +168,6 @@ public class SparrowDataSource extends WalletResponseDataSource {
|
|||
electrumServer.broadcastTransactionPrivately(transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TxsResponse fetchTxs(String[] zpubs, int page, int count) throws Exception {
|
||||
List<TxsResponse.Tx> txes = new ArrayList<>();
|
||||
|
||||
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
|
||||
public MinerFeeSupplier getMinerFeeSupplier() {
|
||||
return SparrowMinerFeeSupplier.getInstance();
|
||||
|
@ -236,4 +185,31 @@ public class SparrowDataSource extends WalletResponseDataSource {
|
|||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void newWalletTransactions(NewWalletTransactionsEvent event) {
|
||||
try {
|
||||
refresh();
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletAddressesChanged(WalletAddressesChangedEvent event) {
|
||||
try {
|
||||
refresh();
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void newBlock(NewBlockEvent event) {
|
||||
try {
|
||||
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) {
|
||||
}
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
package com.sparrowwallet.sparrow.whirlpool;
|
||||
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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class SparrowMinerFeeSupplier implements MinerFeeSupplier {
|
||||
private static final int FALLBACK_FEE_RATE = 75;
|
||||
|
@ -32,6 +30,15 @@ public class SparrowMinerFeeSupplier implements MinerFeeSupplier {
|
|||
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);
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
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