Browse Source

detach and store labels before a wallet refresh, and label matching entries from this store as the wallet is updated

terminal
Craig Raw 3 years ago
parent
commit
7aeca7ebd3
  1. 2
      drongo
  2. 26
      src/main/java/com/sparrowwallet/sparrow/event/WalletEntryLabelsChangedEvent.java
  3. 2
      src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java
  4. 38
      src/main/java/com/sparrowwallet/sparrow/io/db/DetachedLabelDao.java
  5. 33
      src/main/java/com/sparrowwallet/sparrow/io/db/DetachedLabelMapper.java
  6. 9
      src/main/java/com/sparrowwallet/sparrow/io/db/WalletDao.java
  7. 4
      src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java
  8. 4
      src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java
  9. 26
      src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java
  10. 1
      src/main/resources/com/sparrowwallet/sparrow/sql/V5__DetachedLabel.sql

2
drongo

@ -1 +1 @@
Subproject commit ee732fb2235fbe242d75366fc37d3f53e2082519
Subproject commit de87ab1102db12cad8bbfe814a1346078cf957a5

26
src/main/java/com/sparrowwallet/sparrow/event/WalletEntryLabelsChangedEvent.java

@ -3,25 +3,37 @@ package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.wallet.Entry;
import java.util.List;
import java.util.*;
/**
* This event is fired when a wallet entry (transaction, txi or txo) label is changed.
*/
public class WalletEntryLabelsChangedEvent extends WalletChangedEvent {
private final List<Entry> entries;
//Contains the changed entry mapped to the entry that changed it, if changed recursively (otherwise null)
private final Map<Entry, Entry> entrySourceMap;
public WalletEntryLabelsChangedEvent(Wallet wallet, Entry entry) {
super(wallet);
this.entries = List.of(entry);
this(wallet, List.of(entry));
}
public WalletEntryLabelsChangedEvent(Wallet wallet, List<Entry> entries) {
super(wallet);
this.entries = entries;
this.entrySourceMap = new LinkedHashMap<>();
for(Entry entry : entries) {
entrySourceMap.put(entry, null);
}
}
public WalletEntryLabelsChangedEvent(Wallet wallet, Map<Entry, Entry> entrySourceMap) {
super(wallet);
this.entrySourceMap = entrySourceMap;
}
public Collection<Entry> getEntries() {
return entrySourceMap.keySet();
}
public List<Entry> getEntries() {
return entries;
public Entry getSource(Entry entry) {
return entrySourceMap.get(entry);
}
}

2
src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java

@ -227,6 +227,8 @@ public class DbPersistence implements Persistence {
if(dirtyPersistables.clearHistory) {
WalletNodeDao walletNodeDao = handle.attach(WalletNodeDao.class);
BlockTransactionDao blockTransactionDao = handle.attach(BlockTransactionDao.class);
DetachedLabelDao detachedLabelDao = handle.attach(DetachedLabelDao.class);
detachedLabelDao.clearAndAddAll(wallet);
walletNodeDao.clearHistory(wallet);
blockTransactionDao.clear(wallet.getId());
}

38
src/main/java/com/sparrowwallet/sparrow/io/db/DetachedLabelDao.java

@ -0,0 +1,38 @@
package com.sparrowwallet.sparrow.io.db;
import com.sparrowwallet.drongo.wallet.Wallet;
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
import org.jdbi.v3.sqlobject.statement.SqlBatch;
import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
import java.util.*;
public interface DetachedLabelDao {
@SqlQuery("select entry, label from detachedLabel")
@RegisterRowMapper(DetachedLabelMapper.class)
Map<String, String> getAll();
@SqlBatch("insert into detachedLabel (entry, label) values (?, ?)")
void insertDetachedLabels(List<String> entries, List<String> labels);
@SqlUpdate("delete from detachedLabel")
void clear();
default void clearAndAddAll(Wallet wallet) {
clear();
List<String> entries = new ArrayList<>();
List<String> labels = new ArrayList<>();
for(Map.Entry<String, String> labelEntry : new HashSet<>(wallet.getDetachedLabels().entrySet())) {
entries.add(truncate(labelEntry.getKey(), 80));
labels.add(truncate(labelEntry.getValue(), 255));
}
insertDetachedLabels(entries, labels);
}
default String truncate(String label, int length) {
return (label != null && label.length() > length ? label.substring(0, length) : label);
}
}

33
src/main/java/com/sparrowwallet/sparrow/io/db/DetachedLabelMapper.java

@ -0,0 +1,33 @@
package com.sparrowwallet.sparrow.io.db;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.statement.StatementContext;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
public class DetachedLabelMapper implements RowMapper<Map.Entry<String, String>> {
@Override
public Map.Entry<String, String> map(ResultSet rs, StatementContext ctx) throws SQLException {
String entry = rs.getString("entry");
String label = rs.getString("label");
return new Map.Entry<>() {
@Override
public String getKey() {
return entry;
}
@Override
public String getValue() {
return label;
}
@Override
public String setValue(String value) {
return null;
}
};
}
}

9
src/main/java/com/sparrowwallet/sparrow/io/db/WalletDao.java

@ -30,6 +30,9 @@ public interface WalletDao {
@CreateSqlObject
BlockTransactionDao createBlockTransactionDao();
@CreateSqlObject
DetachedLabelDao createDetachedLabelDao();
@CreateSqlObject
MixConfigDao createMixConfigDao();
@ -100,9 +103,12 @@ public interface WalletDao {
List<WalletNode> walletNodes = createWalletNodeDao().getForWalletId(wallet.getId());
wallet.getPurposeNodes().addAll(walletNodes.stream().filter(walletNode -> walletNode.getDerivation().size() == 1).collect(Collectors.toList()));
Map<Sha256Hash, BlockTransaction> blockTransactions = createBlockTransactionDao().getForWalletId(wallet.getId()); //.stream().collect(Collectors.toMap(BlockTransaction::getHash, Function.identity(), (existing, replacement) -> existing, LinkedHashMap::new));
Map<Sha256Hash, BlockTransaction> blockTransactions = createBlockTransactionDao().getForWalletId(wallet.getId());
wallet.updateTransactions(blockTransactions);
Map<String, String> detachedLabels = createDetachedLabelDao().getAll();
wallet.getDetachedLabels().putAll(detachedLabels);
wallet.setMixConfig(createMixConfigDao().getForWalletId(wallet.getId()));
Map<Sha256Hash, UtxoMixData> utxoMixes = createUtxoMixDataDao().getForWalletId(wallet.getId());
@ -120,6 +126,7 @@ public interface WalletDao {
createKeystoreDao().addKeystores(wallet);
createWalletNodeDao().addWalletNodes(wallet);
createBlockTransactionDao().addBlockTransactions(wallet);
createDetachedLabelDao().clearAndAddAll(wallet);
createMixConfigDao().addMixConfig(wallet);
createUtxoMixDataDao().addUtxoMixData(wallet);
} finally {

4
src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java

@ -320,7 +320,7 @@ public class ElectrumServer {
//The gap limit size takes the highest used index in the retrieved history and adds the gap limit (plus one to be comparable to the number of children since index is zero based)
int gapLimitSize = getGapLimitSize(wallet, nodeTransactionMap);
while(historySize < gapLimitSize) {
purposeNode.fillToIndex(gapLimitSize - 1);
purposeNode.fillToIndex(wallet, gapLimitSize - 1);
subscribeWalletNodes(wallet, getAddressNodes(wallet, purposeNode), nodeTransactionMap, historySize);
getReferences(wallet, nodeTransactionMap.keySet(), nodeTransactionMap, historySize);
getReferencedTransactions(wallet, nodeTransactionMap);
@ -718,7 +718,7 @@ public class ElectrumServer {
}
if(!transactionOutputs.equals(node.getTransactionOutputs())) {
node.updateTransactionOutputs(transactionOutputs);
node.updateTransactionOutputs(wallet, transactionOutputs);
copyPostmixLabels(wallet, transactionOutputs);
}
}

4
src/main/java/com/sparrowwallet/sparrow/wallet/SettingsWalletForm.java

@ -64,8 +64,8 @@ public class SettingsWalletForm extends WalletForm {
AppServices.clearTransactionHistoryCache(wallet);
}
//Clear node tree
walletCopy.clearNodes();
//Clear node tree, detaching and saving any labels from the existing wallet
walletCopy.clearNodes(wallet);
Integer childIndex = wallet.isMasterWallet() ? null : wallet.getMasterWallet().getChildWallets().indexOf(wallet);

26
src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java

@ -472,48 +472,46 @@ public class WalletForm {
@Subscribe
public void walletLabelsChanged(WalletEntryLabelsChangedEvent event) {
if(event.getWallet() == wallet) {
List<Entry> labelChangedEntries = new ArrayList<>();
Map<Entry, Entry> labelChangedEntries = new LinkedHashMap<>();
for(Entry entry : event.getEntries()) {
if(entry.getLabel() != null && !entry.getLabel().isEmpty()) {
if(entry instanceof TransactionEntry) {
TransactionEntry transactionEntry = (TransactionEntry)entry;
if(entry instanceof TransactionEntry transactionEntry) {
for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) {
for(WalletNode childNode : wallet.getNode(keyPurpose).getChildren()) {
for(BlockTransactionHashIndex receivedRef : childNode.getTransactionOutputs()) {
if(receivedRef.getHash().equals(transactionEntry.getBlockTransaction().getHash())) {
if((receivedRef.getLabel() == null || receivedRef.getLabel().isEmpty()) && wallet.getStandardAccountType() != StandardAccount.WHIRLPOOL_PREMIX) {
receivedRef.setLabel(entry.getLabel() + (keyPurpose == KeyPurpose.CHANGE ? " (change)" : " (received)"));
labelChangedEntries.add(new HashIndexEntry(event.getWallet(), receivedRef, HashIndexEntry.Type.OUTPUT, keyPurpose));
labelChangedEntries.put(new HashIndexEntry(event.getWallet(), receivedRef, HashIndexEntry.Type.OUTPUT, keyPurpose), entry);
}
if(childNode.getLabel() == null || childNode.getLabel().isEmpty()) {
//Avoid recursive changes to address labels - only initial transaction label changes can change address labels
if((childNode.getLabel() == null || childNode.getLabel().isEmpty()) && event.getSource(entry) == null) {
childNode.setLabel(entry.getLabel());
labelChangedEntries.add(new NodeEntry(event.getWallet(), childNode));
labelChangedEntries.put(new NodeEntry(event.getWallet(), childNode), entry);
}
}
if(receivedRef.isSpent() && receivedRef.getSpentBy().getHash().equals(transactionEntry.getBlockTransaction().getHash()) && (receivedRef.getSpentBy().getLabel() == null || receivedRef.getSpentBy().getLabel().isEmpty())) {
receivedRef.getSpentBy().setLabel(entry.getLabel() + " (input)");
labelChangedEntries.add(new HashIndexEntry(event.getWallet(), receivedRef.getSpentBy(), HashIndexEntry.Type.INPUT, keyPurpose));
labelChangedEntries.put(new HashIndexEntry(event.getWallet(), receivedRef.getSpentBy(), HashIndexEntry.Type.INPUT, keyPurpose), entry);
}
}
}
}
}
if(entry instanceof NodeEntry) {
NodeEntry nodeEntry = (NodeEntry)entry;
if(entry instanceof NodeEntry nodeEntry) {
for(BlockTransactionHashIndex receivedRef : nodeEntry.getNode().getTransactionOutputs()) {
BlockTransaction blockTransaction = event.getWallet().getTransactions().get(receivedRef.getHash());
if(blockTransaction.getLabel() == null || blockTransaction.getLabel().isEmpty()) {
blockTransaction.setLabel(entry.getLabel());
labelChangedEntries.add(new TransactionEntry(event.getWallet(), blockTransaction, Collections.emptyMap(), Collections.emptyMap()));
labelChangedEntries.put(new TransactionEntry(event.getWallet(), blockTransaction, Collections.emptyMap(), Collections.emptyMap()), entry);
}
}
}
if(entry instanceof HashIndexEntry) {
HashIndexEntry hashIndexEntry = (HashIndexEntry)entry;
if(entry instanceof HashIndexEntry hashIndexEntry) {
BlockTransaction blockTransaction = hashIndexEntry.getBlockTransaction();
if(blockTransaction.getLabel() == null || blockTransaction.getLabel().isEmpty()) {
blockTransaction.setLabel(entry.getLabel());
labelChangedEntries.add(new TransactionEntry(event.getWallet(), blockTransaction, Collections.emptyMap(), Collections.emptyMap()));
labelChangedEntries.put(new TransactionEntry(event.getWallet(), blockTransaction, Collections.emptyMap(), Collections.emptyMap()), entry);
}
}
}
@ -573,7 +571,7 @@ public class WalletForm {
Optional<WalletNode> optPurposeNode = wallet.getPurposeNodes().stream().filter(node -> node.getKeyPurpose() == keyPurpose).findFirst();
if(optPurposeNode.isPresent()) {
WalletNode purposeNode = optPurposeNode.get();
newNodes.addAll(purposeNode.fillToIndex(wallet.getLookAheadIndex(purposeNode)));
newNodes.addAll(purposeNode.fillToIndex(wallet, wallet.getLookAheadIndex(purposeNode)));
}
}

1
src/main/resources/com/sparrowwallet/sparrow/sql/V5__DetachedLabel.sql

@ -0,0 +1 @@
create table detachedLabel (entry varchar(80) primary key not null, label varchar(255) not null);
Loading…
Cancel
Save