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());
} else if(blockTransactions.size() > 1) {
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) {
text = "New mempool transactions: " + event.getValueAsText(event.getTotalMempoolValue()) + " total";
} 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 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;
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)
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")
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 = ?")
void updateMixConfig(String scode, Boolean mixOnStartup, String mixToWalletFile, String mixToWalletName, Integer minMixes, long wallet, long 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, int receiveIndex, int changeIndex, long wallet, long id);
default void addMixConfig(Wallet wallet) {
if(wallet.getMixConfig() != null) {
@ -32,10 +32,10 @@ public interface MixConfigDao {
}
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);
} 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;
}
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"));
return mixConfig;
}

View file

@ -6,7 +6,7 @@ import com.sparrowwallet.drongo.wallet.StandardAccount;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent;
import com.sparrowwallet.sparrow.event.WalletMasterMixConfigChangedEvent;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
@ -72,14 +72,14 @@ public class MixToController implements Initializable {
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();
minMixes.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 10000, initialMinMixes));
minMixes.valueProperty().addListener((observable, oldValue, 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);
EventManager.get().post(new WalletMixConfigChangedEvent(getWalletForm().getWallet()));
EventManager.get().post(new WalletMasterMixConfigChangedEvent(getWalletForm().getWallet()));
}
public void stopMixing(ActionEvent event) {
@ -343,7 +343,7 @@ public class UtxosController extends WalletFormController implements Initializab
}
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) {
@ -359,7 +359,7 @@ public class UtxosController extends WalletFormController implements Initializab
} catch(NoSuchElementException e) {
mixConfig.setMixToWalletName(null);
mixConfig.setMixToWalletFile(null);
EventManager.get().post(new WalletMixConfigChangedEvent(getWalletForm().getWallet()));
EventManager.get().post(new WalletMasterMixConfigChangedEvent(getWalletForm().getWallet()));
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.EventManager;
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.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.UtxoEntry;
@ -95,9 +95,13 @@ public class WhirlpoolController {
step4.setVisible(false);
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) -> {
mixConfig.setScode(newValue);
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
});
if(mixConfig.getScode() != null) {
@ -232,7 +236,7 @@ public class WhirlpoolController {
private void fetchTx0Preview(Pool pool) {
if(mixConfig.getScode() == null) {
mixConfig.setScode("");
EventManager.get().post(new WalletMixConfigChangedEvent(wallet));
EventManager.get().post(new WalletMasterMixConfigChangedEvent(wallet));
}
Whirlpool whirlpool = AppServices.getWhirlpoolServices().getWhirlpool(walletId);

View file

@ -1,33 +1,70 @@
package com.sparrowwallet.sparrow.whirlpool.dataSource;
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.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletMixConfigChangedEvent;
public class SparrowIndexHandler extends AbstractIndexHandler {
private final Wallet wallet;
private final WalletNode walletNode;
private final int defaultValue;
public SparrowIndexHandler(WalletNode walletNode) {
this(walletNode, 0);
public SparrowIndexHandler(Wallet wallet, WalletNode walletNode) {
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.defaultValue = defaultValue;
}
@Override
public synchronized int get() {
Integer currentIndex = walletNode.getHighestUsedIndex();
return currentIndex == null ? defaultValue : currentIndex + 1;
return Math.max(getCurrentIndex(), getStoredIndex());
}
@Override
public synchronized int getAndIncrement() {
return get();
int index = get();
set(index + 1);
return index;
}
@Override
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.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.crypto.ChildNumber;
import com.sparrowwallet.drongo.wallet.MixConfig;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
@ -32,10 +33,21 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier {
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);
Wallet wallet = findWallet(whirlpoolAccount);
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);
}
return indexHandler;
}
@ -53,7 +65,7 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier {
KeyPurpose keyPurpose = KeyPurpose.fromChildNumber(new ChildNumber(externalDestination.getChain()));
WalletNode externalNode = externalWallet.getNode(keyPurpose);
externalIndexHandler = new SparrowIndexHandler(externalNode, externalDestination.getStartIndex());
externalIndexHandler = new SparrowIndexHandler(externalWallet, externalNode, externalDestination.getStartIndex());
}
return externalIndexHandler;
@ -89,18 +101,13 @@ public class SparrowWalletStateSupplier implements WalletStateSupplier {
return whirlpoolAccount.name()+"_"+addressType.getPurpose()+"_"+chain.getIndex();
}
private WalletNode findWalletNode(WhirlpoolAccount whirlpoolAccount, AddressType addressType, Chain chain) {
private Wallet findWallet(WhirlpoolAccount whirlpoolAccount) {
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);
return Whirlpool.getStandardAccountWallet(whirlpoolAccount, wallet);
}
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);