support using stored mix indexes to avoid resending a utxo to the coordinator

This commit is contained in:
Craig Raw 2021-09-02 14:12:59 +02:00
parent e8af7c70bd
commit 6e6111b47c
12 changed files with 94 additions and 37 deletions

2
drongo

@ -1 +1 @@
Subproject commit b4f4cc8726de3e7b5f875816affe1e0f78f2fa25 Subproject commit 94d22b875868760a95222e0254ec10b59c71e04f

View file

@ -1721,7 +1721,7 @@ public class AppController implements Initializable {
text += event.getValueAsText(event.getTotalValue()); text += event.getValueAsText(event.getTotalValue());
} else if(blockTransactions.size() > 1) { } else if(blockTransactions.size() > 1) {
if(event.getTotalBlockchainValue() > 0 && event.getTotalMempoolValue() > 0) { if(event.getTotalBlockchainValue() > 0 && event.getTotalMempoolValue() > 0) {
text = "New transactions: " + event.getValueAsText(event.getTotalValue()) + " total (" + event.getValueAsText(event.getTotalMempoolValue()) + " in mempool)"; text = "New transactions: " + event.getValueAsText(event.getTotalValue()) + " total";
} else if(event.getTotalMempoolValue() > 0) { } else if(event.getTotalMempoolValue() > 0) {
text = "New mempool transactions: " + event.getValueAsText(event.getTotalMempoolValue()) + " total"; text = "New mempool transactions: " + event.getValueAsText(event.getTotalMempoolValue()) + " total";
} else { } else {

View file

@ -0,0 +1,9 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.Wallet;
public class WalletMasterMixConfigChangedEvent extends WalletMixConfigChangedEvent {
public WalletMasterMixConfigChangedEvent(Wallet wallet) {
super(wallet.isMasterWallet() ? wallet : wallet.getMasterWallet());
}
}

View file

@ -4,6 +4,6 @@ import com.sparrowwallet.drongo.wallet.Wallet;
public class WalletMixConfigChangedEvent extends WalletChangedEvent { public class WalletMixConfigChangedEvent extends WalletChangedEvent {
public WalletMixConfigChangedEvent(Wallet wallet) { public WalletMixConfigChangedEvent(Wallet wallet) {
super(wallet.isMasterWallet() ? wallet : wallet.getMasterWallet()); super(wallet);
} }
} }

View file

@ -8,16 +8,16 @@ import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.jdbi.v3.sqlobject.statement.SqlUpdate; import org.jdbi.v3.sqlobject.statement.SqlUpdate;
public interface MixConfigDao { public interface MixConfigDao {
@SqlQuery("select id, scode, mixOnStartup, mixToWalletFile, mixToWalletName, minMixes from mixConfig where wallet = ?") @SqlQuery("select id, scode, mixOnStartup, mixToWalletFile, mixToWalletName, minMixes, receiveIndex, changeIndex from mixConfig where wallet = ?")
@RegisterRowMapper(MixConfigMapper.class) @RegisterRowMapper(MixConfigMapper.class)
MixConfig getForWalletId(Long id); MixConfig getForWalletId(Long id);
@SqlUpdate("insert into mixConfig (scode, mixOnStartup, mixToWalletFile, mixToWalletName, minMixes, wallet) values (?, ?, ?, ?, ?, ?)") @SqlUpdate("insert into mixConfig (scode, mixOnStartup, mixToWalletFile, mixToWalletName, minMixes, receiveIndex, changeIndex, wallet) values (?, ?, ?, ?, ?, ?, ?, ?)")
@GetGeneratedKeys("id") @GetGeneratedKeys("id")
long insertMixConfig(String scode, Boolean mixOnStartup, String mixToWalletFile, String mixToWalletName, Integer minMixes, long wallet); long insertMixConfig(String scode, Boolean mixOnStartup, String mixToWalletFile, String mixToWalletName, Integer minMixes, int receiveIndex, int changeIndex, long wallet);
@SqlUpdate("update mixConfig set scode = ?, mixOnStartup = ?, mixToWalletFile = ?, mixToWalletName = ?, minMixes = ?, wallet = ? where id = ?") @SqlUpdate("update mixConfig set scode = ?, mixOnStartup = ?, mixToWalletFile = ?, mixToWalletName = ?, minMixes = ?, receiveIndex = ?, changeIndex = ?, wallet = ? where id = ?")
void updateMixConfig(String scode, Boolean mixOnStartup, String mixToWalletFile, String mixToWalletName, Integer minMixes, long wallet, long id); void updateMixConfig(String scode, Boolean mixOnStartup, String mixToWalletFile, String mixToWalletName, Integer minMixes, int receiveIndex, int changeIndex, long wallet, long id);
default void addMixConfig(Wallet wallet) { default void addMixConfig(Wallet wallet) {
if(wallet.getMixConfig() != null) { if(wallet.getMixConfig() != null) {
@ -32,10 +32,10 @@ public interface MixConfigDao {
} }
if(mixConfig.getId() == null) { if(mixConfig.getId() == null) {
long id = insertMixConfig(mixConfig.getScode(), mixConfig.getMixOnStartup(), mixToWalletFile, mixConfig.getMixToWalletName(), mixConfig.getMinMixes(), wallet.getId()); long id = insertMixConfig(mixConfig.getScode(), mixConfig.getMixOnStartup(), mixToWalletFile, mixConfig.getMixToWalletName(), mixConfig.getMinMixes(), mixConfig.getReceiveIndex(), mixConfig.getChangeIndex(), wallet.getId());
mixConfig.setId(id); mixConfig.setId(id);
} else { } else {
updateMixConfig(mixConfig.getScode(), mixConfig.getMixOnStartup(), mixToWalletFile, mixConfig.getMixToWalletName(), mixConfig.getMinMixes(), wallet.getId(), mixConfig.getId()); updateMixConfig(mixConfig.getScode(), mixConfig.getMixOnStartup(), mixToWalletFile, mixConfig.getMixToWalletName(), mixConfig.getMinMixes(), mixConfig.getReceiveIndex(), mixConfig.getChangeIndex(), wallet.getId(), mixConfig.getId());
} }
} }
} }

View file

@ -26,7 +26,7 @@ public class MixConfigMapper implements RowMapper<MixConfig> {
minMixes = null; minMixes = null;
} }
MixConfig mixConfig = new MixConfig(scode, mixOnStartup, mixToWalletFile == null ? null : new File(mixToWalletFile), mixToWalletName, minMixes); MixConfig mixConfig = new MixConfig(scode, mixOnStartup, mixToWalletFile == null ? null : new File(mixToWalletFile), mixToWalletName, minMixes, rs.getInt("receiveIndex"), rs.getInt("changeIndex"));
mixConfig.setId(rs.getLong("id")); mixConfig.setId(rs.getLong("id"));
return mixConfig; return mixConfig;
} }

View file

@ -6,7 +6,7 @@ import com.sparrowwallet.drongo.wallet.StandardAccount;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent; import com.sparrowwallet.sparrow.event.WalletMasterMixConfigChangedEvent;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@ -72,14 +72,14 @@ public class MixToController implements Initializable {
mixConfig.setMixToWalletFile(AppServices.get().getOpenWallets().get(newValue).getWalletFile()); mixConfig.setMixToWalletFile(AppServices.get().getOpenWallets().get(newValue).getWalletFile());
} }
EventManager.get().post(new WalletMixConfigChangedEvent(wallet)); EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
}); });
int initialMinMixes = mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes(); int initialMinMixes = mixConfig.getMinMixes() == null ? Whirlpool.DEFAULT_MIXTO_MIN_MIXES : mixConfig.getMinMixes();
minMixes.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 10000, initialMinMixes)); minMixes.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 10000, initialMinMixes));
minMixes.valueProperty().addListener((observable, oldValue, newValue) -> { minMixes.valueProperty().addListener((observable, oldValue, newValue) -> {
mixConfig.setMinMixes(newValue); mixConfig.setMinMixes(newValue);
EventManager.get().post(new WalletMixConfigChangedEvent(wallet)); EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
}); });
} }
} }

View file

@ -322,7 +322,7 @@ public class UtxosController extends WalletFormController implements Initializab
} }
getWalletForm().getWallet().getMasterMixConfig().setMixOnStartup(Boolean.TRUE); getWalletForm().getWallet().getMasterMixConfig().setMixOnStartup(Boolean.TRUE);
EventManager.get().post(new WalletMixConfigChangedEvent(getWalletForm().getWallet())); EventManager.get().post(new WalletMasterMixConfigChangedEvent(getWalletForm().getWallet()));
} }
public void stopMixing(ActionEvent event) { public void stopMixing(ActionEvent event) {
@ -343,7 +343,7 @@ public class UtxosController extends WalletFormController implements Initializab
} }
getWalletForm().getWallet().getMasterMixConfig().setMixOnStartup(Boolean.FALSE); getWalletForm().getWallet().getMasterMixConfig().setMixOnStartup(Boolean.FALSE);
EventManager.get().post(new WalletMixConfigChangedEvent(getWalletForm().getWallet())); EventManager.get().post(new WalletMasterMixConfigChangedEvent(getWalletForm().getWallet()));
} }
public void showMixToDialog(ActionEvent event) { public void showMixToDialog(ActionEvent event) {
@ -359,7 +359,7 @@ public class UtxosController extends WalletFormController implements Initializab
} catch(NoSuchElementException e) { } catch(NoSuchElementException e) {
mixConfig.setMixToWalletName(null); mixConfig.setMixToWalletName(null);
mixConfig.setMixToWalletFile(null); mixConfig.setMixToWalletFile(null);
EventManager.get().post(new WalletMixConfigChangedEvent(getWalletForm().getWallet())); EventManager.get().post(new WalletMasterMixConfigChangedEvent(getWalletForm().getWallet()));
whirlpool.setMixToWallet(null, null); whirlpool.setMixToWallet(null, null);
} }

View file

@ -9,7 +9,7 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.CoinLabel; import com.sparrowwallet.sparrow.control.CoinLabel;
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent; import com.sparrowwallet.sparrow.event.WalletMasterMixConfigChangedEvent;
import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.UtxoEntry; import com.sparrowwallet.sparrow.wallet.UtxoEntry;
@ -95,9 +95,13 @@ public class WhirlpoolController {
step4.setVisible(false); step4.setVisible(false);
scode.setText(mixConfig.getScode() == null ? "" : mixConfig.getScode()); scode.setText(mixConfig.getScode() == null ? "" : mixConfig.getScode());
scode.setTextFormatter(new TextFormatter<>((change) -> {
change.setText(change.getText().toUpperCase());
return change;
}));
scode.textProperty().addListener((observable, oldValue, newValue) -> { scode.textProperty().addListener((observable, oldValue, newValue) -> {
mixConfig.setScode(newValue); mixConfig.setScode(newValue);
EventManager.get().post(new WalletMixConfigChangedEvent(wallet)); EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
}); });
if(mixConfig.getScode() != null) { if(mixConfig.getScode() != null) {
@ -232,7 +236,7 @@ public class WhirlpoolController {
private void fetchTx0Preview(Pool pool) { private void fetchTx0Preview(Pool pool) {
if(mixConfig.getScode() == null) { if(mixConfig.getScode() == null) {
mixConfig.setScode(""); mixConfig.setScode("");
EventManager.get().post(new WalletMixConfigChangedEvent(wallet)); EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
} }
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId); Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId);

View file

@ -1,33 +1,70 @@
package com.sparrowwallet.sparrow.whirlpool.dataSource; package com.sparrowwallet.sparrow.whirlpool.dataSource;
import com.samourai.wallet.client.indexHandler.AbstractIndexHandler; import com.samourai.wallet.client.indexHandler.AbstractIndexHandler;
import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent;
public class SparrowIndexHandler extends AbstractIndexHandler { public class SparrowIndexHandler extends AbstractIndexHandler {
private final Wallet wallet;
private final WalletNode walletNode; private final WalletNode walletNode;
private final int defaultValue; private final int defaultValue;
public SparrowIndexHandler(WalletNode walletNode) { public SparrowIndexHandler(Wallet wallet, WalletNode walletNode) {
this(walletNode, 0); this(wallet, walletNode, 0);
} }
public SparrowIndexHandler(WalletNode walletNode, int defaultValue) { public SparrowIndexHandler(Wallet wallet, WalletNode walletNode, int defaultValue) {
this.wallet = wallet;
this.walletNode = walletNode; this.walletNode = walletNode;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
@Override @Override
public synchronized int get() { public synchronized int get() {
Integer currentIndex = walletNode.getHighestUsedIndex(); return Math.max(getCurrentIndex(), getStoredIndex());
return currentIndex == null ? defaultValue : currentIndex + 1;
} }
@Override @Override
public synchronized int getAndIncrement() { public synchronized int getAndIncrement() {
return get(); int index = get();
set(index + 1);
return index;
} }
@Override @Override
public synchronized void set(int value) { public synchronized void set(int value) {
setStoredIndex(value);
}
private int getCurrentIndex() {
Integer currentIndex = walletNode.getHighestUsedIndex();
return currentIndex == null ? defaultValue : currentIndex + 1;
}
private int getStoredIndex() {
if(wallet.getMixConfig() != null) {
if(walletNode.getKeyPurpose() == KeyPurpose.RECEIVE) {
return wallet.getMixConfig().getReceiveIndex();
} else if(walletNode.getKeyPurpose() == KeyPurpose.CHANGE) {
return wallet.getMixConfig().getChangeIndex();
}
}
return defaultValue;
}
private void setStoredIndex(int index) {
if(wallet.getMixConfig() != null) {
if(walletNode.getKeyPurpose() == KeyPurpose.RECEIVE) {
wallet.getMixConfig().setReceiveIndex(index);
} else if(walletNode.getKeyPurpose() == KeyPurpose.CHANGE) {
wallet.getMixConfig().setChangeIndex(index);
}
}
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
} }
} }

View file

@ -8,6 +8,7 @@ import com.samourai.whirlpool.client.wallet.beans.WhirlpoolAccount;
import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier; import com.samourai.whirlpool.client.wallet.data.walletState.WalletStateSupplier;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.crypto.ChildNumber; import com.sparrowwallet.drongo.crypto.ChildNumber;
import com.sparrowwallet.drongo.wallet.MixConfig;
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.whirlpool.Whirlpool; import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
@ -32,10 +33,21 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier {
String key = mapKey(whirlpoolAccount, addressType, chain); String key = mapKey(whirlpoolAccount, addressType, chain);
IIndexHandler indexHandler = indexHandlerWallets.get(key); IIndexHandler indexHandler = indexHandlerWallets.get(key);
if (indexHandler == null) { if (indexHandler == null) {
WalletNode walletNode = findWalletNode(whirlpoolAccount, addressType, chain); Wallet wallet = findWallet(whirlpoolAccount);
indexHandler = new SparrowIndexHandler(walletNode, 0); KeyPurpose keyPurpose = (chain == Chain.RECEIVE ? KeyPurpose.RECEIVE : KeyPurpose.CHANGE);
WalletNode walletNode = wallet.getNode(keyPurpose);
//Ensure mix config is present
MixConfig mixConfig = wallet.getMixConfig();
if(mixConfig == null) {
mixConfig = new MixConfig();
wallet.setMixConfig(mixConfig);
}
indexHandler = new SparrowIndexHandler(wallet, walletNode, 0);
indexHandlerWallets.put(key, indexHandler); indexHandlerWallets.put(key, indexHandler);
} }
return indexHandler; return indexHandler;
} }
@ -53,7 +65,7 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier {
KeyPurpose keyPurpose = KeyPurpose.fromChildNumber(new ChildNumber(externalDestination.getChain())); KeyPurpose keyPurpose = KeyPurpose.fromChildNumber(new ChildNumber(externalDestination.getChain()));
WalletNode externalNode = externalWallet.getNode(keyPurpose); WalletNode externalNode = externalWallet.getNode(keyPurpose);
externalIndexHandler = new SparrowIndexHandler(externalNode, externalDestination.getStartIndex()); externalIndexHandler = new SparrowIndexHandler(externalWallet, externalNode, externalDestination.getStartIndex());
} }
return externalIndexHandler; return externalIndexHandler;
@ -89,18 +101,13 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier {
return whirlpoolAccount.name()+"_"+addressType.getPurpose()+"_"+chain.getIndex(); return whirlpoolAccount.name()+"_"+addressType.getPurpose()+"_"+chain.getIndex();
} }
private WalletNode findWalletNode(WhirlpoolAccount whirlpoolAccount, AddressType addressType, Chain chain) { private Wallet findWallet(WhirlpoolAccount whirlpoolAccount) {
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);
} }
// account return Whirlpool.getStandardAccountWallet(whirlpoolAccount, wallet);
Wallet childWallet = Whirlpool.getStandardAccountWallet(whirlpoolAccount, wallet);
// purpose
KeyPurpose keyPurpose = chain == Chain.RECEIVE ? KeyPurpose.RECEIVE : KeyPurpose.CHANGE;
return childWallet.getNode(keyPurpose);
} }
private Wallet getWallet() { private Wallet getWallet() {

View file

@ -1,2 +1,2 @@
create table mixConfig (id identity not null, scode varchar(255), mixOnStartup boolean, mixToWalletFile varchar(1024), mixToWalletName varchar(255), minMixes integer, wallet bigint not null); create table mixConfig (id identity not null, scode varchar(255), mixOnStartup boolean, mixToWalletFile varchar(1024), mixToWalletName varchar(255), minMixes integer, receiveIndex integer not null, changeIndex integer not null, wallet bigint not null);
create table utxoMixData (id identity not null, hash binary(32) not null, mixesDone integer not null default 0, expired bigint, wallet bigint not null); create table utxoMixData (id identity not null, hash binary(32) not null, mixesDone integer not null default 0, expired bigint, wallet bigint not null);