stop labelling a heavily reused address from freezing the ui, and commit receive tab labels on enter so the whole label propagates
What changed, and why it matters
This commit fixes two user-interface bugs in the Sparrow bitcoin wallet. First, it prevents the app from freezing when a user labels an address that has been reused for many transactions. Second, it makes the Receive tab commit the whole label text when the user presses Enter or leaves the field, so the label is properly saved and propagated to related transactions. There is no direct evidence this is a security vulnerability— it reads as a performance/usability fix.
Treat as a routine bug-fix / performance improvement. No immediate security response is indicated by the diff or supplied references. If reviewing for release, verify the UI freeze is resolved and label propagation behaves correctly in regression tests.
Security signals we found
Performance fix: batched label updates reduce algorithmic complexity for reused addresses
UI consistency fix: Receive tab now commits full label on Enter/focus loss instead of relying on bidirectional binding
No input validation, cryptography, or privilege changes observed
No network, serialization, or external-process changes observed
No vendor security disclosure or CVE references present in commit or supplied references
Evidence from the diff
The patch refactors label-update handling from single-Entry updates to batched Collection
Changed components
Sparrow Wallet UI label propagationAddressTreeTable, TransactionsTreeTable, UtxosTreeTableReceiveControllerWalletForm label-change handlerWalletUtxosEntry dust-attack calculationdrongo submoduleInspect captured patch +220 / −69
### drongo
@@ -1 +1 @@
-Subproject commit 13059ebc2979640b30bc1534b4584e2191578672
+Subproject commit 2c703d2048135b23c9faffe4c7cafc57768f26f9
### src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java
@@ -172,9 +172,9 @@ public void updateHistory(List<WalletNode> updatedNodes) {
refresh();
}
- public void updateLabel(Entry entry) {
+ public void updateLabels(Collection<Entry> entries) {
Entry rootEntry = getRoot().getValue();
- rootEntry.updateLabel(entry);
+ rootEntry.updateLabels(entries);
}
public void showTransactionsCount(boolean show) {
### src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java
@@ -7,6 +7,8 @@
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.control.TreeTableColumn;
+import java.util.Collection;
+
public class TransactionsTreeTable extends CoinTreeTable {
public void initialize(WalletTransactionsEntry rootEntry) {
getStyleClass().add("transactions-treetable");
@@ -71,8 +73,8 @@ public void updateHistory() {
resetSortColumn();
}
- public void updateLabel(Entry entry) {
+ public void updateLabels(Collection<Entry> entries) {
Entry rootEntry = getRoot().getValue();
- rootEntry.updateLabel(entry);
+ rootEntry.updateLabels(entries);
}
}
### src/main/java/com/sparrowwallet/sparrow/control/UtxosTreeTable.java
@@ -6,6 +6,7 @@
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeTableColumn;
+import java.util.Collection;
import java.util.Comparator;
public class UtxosTreeTable extends CoinTreeTable {
@@ -112,8 +113,8 @@ public void updateHistory() {
}
}
- public void updateLabel(Entry entry) {
+ public void updateLabels(Collection<Entry> entries) {
Entry rootEntry = getRoot().getValue();
- rootEntry.updateLabel(entry);
+ rootEntry.updateLabels(entries);
}
}
### src/main/java/com/sparrowwallet/sparrow/paynym/PayNymAddressesController.java
@@ -7,7 +7,6 @@
import com.sparrowwallet.sparrow.control.AddressTreeTable;
import com.sparrowwallet.sparrow.event.WalletEntryLabelsChangedEvent;
import com.sparrowwallet.sparrow.event.WalletHistoryChangedEvent;
-import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.WalletForm;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
@@ -76,10 +75,8 @@ public void walletHistoryChanged(WalletHistoryChangedEvent event) {
@Subscribe
public void walletEntryLabelChanged(WalletEntryLabelsChangedEvent event) {
if(event.getWallet().equals(payNymWalletForms.getValue().getWallet())) {
- for(Entry entry : event.getEntries()) {
- receiveTable.updateLabel(entry);
- sendTable.updateLabel(entry);
- }
+ receiveTable.updateLabels(event.getEntries());
+ sendTable.updateLabels(event.getEntries());
}
}
}
### src/main/java/com/sparrowwallet/sparrow/wallet/AddressesController.java
@@ -80,10 +80,8 @@ public void walletHistoryChanged(WalletHistoryChangedEvent event) {
@Subscribe
public void walletEntryLabelChanged(WalletEntryLabelsChangedEvent event) {
if(event.getWallet().equals(walletForm.getWallet())) {
- for(Entry entry : event.getEntries()) {
- receiveTable.updateLabel(entry);
- changeTable.updateLabel(entry);
- }
+ receiveTable.updateLabels(event.getEntries());
+ changeTable.updateLabels(event.getEntries());
}
}
### src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java
@@ -5,7 +5,7 @@
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
-import java.util.List;
+import java.util.*;
public abstract class Entry {
private final Wallet wallet;
@@ -56,13 +56,34 @@ public boolean equals(Object o) {
|| (entry.wallet.isNested() && wallet.getChildWallets().contains(entry.wallet));
}
- public void updateLabel(Entry entry) {
- if(this.equals(entry)) {
- labelProperty.set(entry.getLabel());
+ public void updateLabels(Collection<Entry> entries) {
+ Map<Object, List<Entry>> entriesByKey = new HashMap<>();
+ for(Entry entry : entries) {
+ entriesByKey.computeIfAbsent(entry.getLabelKey(), key -> new ArrayList<>()).add(entry);
+ }
+
+ updateLabels(entriesByKey);
+ }
+
+ private void updateLabels(Map<Object, List<Entry>> entriesByKey) {
+ for(Entry entry : entriesByKey.getOrDefault(getLabelKey(), Collections.emptyList())) {
+ if(this.equals(entry)) {
+ labelProperty.set(entry.getLabel());
+ }
}
for(Entry child : getChildren()) {
- child.updateLabel(entry);
+ child.updateLabels(entriesByKey);
}
}
+
+ //Entries are equal across nested wallets and regardless of child count, so they are grouped by what they label and compared within each group
+ private Object getLabelKey() {
+ return switch(this) {
+ case TransactionEntry transactionEntry -> transactionEntry.getBlockTransaction().getHash();
+ case HashIndexEntry hashIndexEntry -> hashIndexEntry.getHashIndex().getHash();
+ case NodeEntry nodeEntry -> nodeEntry.getNode();
+ default -> getClass();
+ };
+ }
}
### src/main/java/com/sparrowwallet/sparrow/wallet/ReceiveController.java
@@ -24,6 +24,7 @@
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
@@ -108,6 +109,8 @@ public class ReceiveController extends WalletFormController implements Initializ
private NodeEntry currentEntry;
+ private final ChangeListener<String> entryLabelListener = (_, _, newValue) -> label.setText(newValue);
+
private QRDisplayDialog addressQrDialog;
@Override
@@ -137,6 +140,14 @@ public void initializeView() {
initializeScriptField(scriptPubKeyArea);
+ //Commit the label once entered as the address tables do, since an address label is passed on to its unlabelled transactions as soon as it is set
+ label.setOnAction(_ -> commitLabel());
+ label.focusedProperty().addListener((_, _, focused) -> {
+ if(!focused) {
+ commitLabel();
+ }
+ });
+
displayAddress.managedProperty().bind(displayAddress.visibleProperty());
displayAddress.setVisible(false);
@@ -170,13 +181,12 @@ public void updateFromWalletPolicy() {
}
public void setNodeEntry(NodeEntry nodeEntry) {
- if(currentEntry != null) {
- label.textProperty().unbindBidirectional(currentEntry.labelProperty());
- }
+ releaseCurrentEntry();
this.currentEntry = nodeEntry;
address.setText(nodeEntry.getAddress().toString());
- label.textProperty().bindBidirectional(nodeEntry.labelProperty());
+ label.setText(nodeEntry.getLabel());
+ nodeEntry.labelProperty().addListener(entryLabelListener);
updateDerivationPath(nodeEntry);
updateLastUsed();
@@ -195,6 +205,22 @@ public void setNodeEntry(NodeEntry nodeEntry) {
updateDisplayAddress(AppServices.getDevices());
}
+ private void commitLabel() {
+ if(currentEntry != null) {
+ String text = label.getText() == null ? "" : label.getText();
+ if(!text.equals(currentEntry.getLabel() == null ? "" : currentEntry.getLabel())) {
+ currentEntry.labelProperty().set(text);
+ }
+ }
+ }
+
+ private void releaseCurrentEntry() {
+ if(currentEntry != null) {
+ commitLabel();
+ currentEntry.labelProperty().removeListener(entryLabelListener);
+ }
+ }
+
private void updateDerivationPath(NodeEntry nodeEntry) {
derivationPath.setText(getDerivationPath(nodeEntry.getNode()));
}
@@ -375,9 +401,7 @@ private List<Keystore> getDeviceRegistrationKeystores(Device device) {
}
public void clear() {
- if(currentEntry != null) {
- label.textProperty().unbindBidirectional(currentEntry.labelProperty());
- }
+ releaseCurrentEntry();
address.setText("");
label.setText("");
@@ -426,10 +450,8 @@ public void receiveTo(ReceiveToEvent event) {
@Subscribe
public void walletNodesChanged(WalletNodesChangedEvent event) {
if(event.getWallet().equals(walletForm.getWallet())) {
- if(currentEntry != null) {
- label.textProperty().unbindBidirectional(currentEntry.labelProperty());
- currentEntry = null;
- }
+ releaseCurrentEntry();
+ currentEntry = null;
refreshAddress();
}
}
### src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java
@@ -178,9 +178,7 @@ public void walletHistoryChanged(WalletHistoryChangedEvent event) {
@Subscribe
public void walletEntryLabelChanged(WalletEntryLabelsChangedEvent event) {
if(event.fromThisOrNested(walletForm.getWallet())) {
- for(Entry entry : event.getEntries()) {
- transactionsTable.updateLabel(entry);
- }
+ transactionsTable.updateLabels(event.getEntries());
balanceChart.update(getWalletForm().getWalletTransactionsEntry());
}
}
### src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java
@@ -256,9 +256,7 @@ public void walletHistoryChanged(WalletHistoryChangedEvent event) {
@Subscribe
public void walletEntryLabelChanged(WalletEntryLabelsChangedEvent event) {
if(event.fromThisOrNested(walletForm.getWallet())) {
- for(Entry entry : event.getEntries()) {
- utxosTable.updateLabel(entry);
- }
+ utxosTable.updateLabels(event.getEntries());
utxosChart.update(getWalletForm().getWalletUtxosEntry());
}
}
### src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java
@@ -712,36 +712,12 @@ public void walletHistoryChanged(WalletHistoryChangedEvent event) {
public void walletLabelsChanged(WalletEntryLabelsChangedEvent event) {
if(event.toThisOrNested(wallet)) {
Map<Entry, Entry> labelChangedEntries = new LinkedHashMap<>();
+ Map<Sha256Hash, Entry> transactionEntries = new LinkedHashMap<>();
Collection<Entry> entries = event.propagate() ? event.getEntries() : Collections.emptyList();
for(Entry entry : entries) {
if(entry.getLabel() != null && !entry.getLabel().isEmpty()) {
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())) {
- String prevRefLabel = "";
- if((receivedRef.getLabel() == null || receivedRef.getLabel().isEmpty()
- || receivedRef.getLabel().endsWith(" (sent)") || receivedRef.getLabel().endsWith(" (change)") || receivedRef.getLabel().endsWith(" (received)"))
- && wallet.getStandardAccountType() != StandardAccount.WHIRLPOOL_PREMIX) {
- prevRefLabel = receivedRef.getLabel() == null ? "" : receivedRef.getLabel();
- receivedRef.setLabel(entry.getLabel() + (keyPurpose == KeyPurpose.CHANGE ? (event.getWallet().isBip47() ? " (sent)" : " (change)") : " (received)"));
- labelChangedEntries.put(new HashIndexEntry(event.getWallet(), receivedRef, HashIndexEntry.Type.OUTPUT, keyPurpose), entry);
- }
- if(childNode.getLabel() == null || childNode.getLabel().isEmpty()
- || prevRefLabel.equals(childNode.getLabel() + " (sent)") || prevRefLabel.equals(childNode.getLabel() + " (change)") || prevRefLabel.equals(childNode.getLabel() + " (received)")) {
- childNode.setLabel(entry.getLabel());
- 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().getLabel().endsWith(" (input)"))) {
- receivedRef.getSpentBy().setLabel(entry.getLabel() + " (input)");
- labelChangedEntries.put(new HashIndexEntry(event.getWallet(), receivedRef.getSpentBy(), HashIndexEntry.Type.INPUT, keyPurpose), entry);
- }
- }
- }
- }
+ transactionEntries.put(transactionEntry.getBlockTransaction().getHash(), entry);
}
if(entry instanceof NodeEntry nodeEntry) {
for(BlockTransactionHashIndex receivedRef : nodeEntry.getNode().getTransactionOutputs()) {
@@ -763,6 +739,36 @@ public void walletLabelsChanged(WalletEntryLabelsChangedEvent event) {
}
}
+ if(!transactionEntries.isEmpty()) {
+ for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) {
+ for(WalletNode childNode : wallet.getNode(keyPurpose).getChildren()) {
+ for(BlockTransactionHashIndex receivedRef : childNode.getTransactionOutputs()) {
+ Entry entry = transactionEntries.get(receivedRef.getHash());
+ if(entry != null) {
+ String prevRefLabel = "";
+ if((receivedRef.getLabel() == null || receivedRef.getLabel().isEmpty()
+ || receivedRef.getLabel().endsWith(" (sent)") || receivedRef.getLabel().endsWith(" (change)") || receivedRef.getLabel().endsWith(" (received)"))
+ && wallet.getStandardAccountType() != StandardAccount.WHIRLPOOL_PREMIX) {
+ prevRefLabel = receivedRef.getLabel() == null ? "" : receivedRef.getLabel();
+ receivedRef.setLabel(entry.getLabel() + (keyPurpose == KeyPurpose.CHANGE ? (event.getWallet().isBip47() ? " (sent)" : " (change)") : " (received)"));
+ labelChangedEntries.put(new HashIndexEntry(event.getWallet(), receivedRef, HashIndexEntry.Type.OUTPUT, keyPurpose), entry);
+ }
+ if(childNode.getLabel() == null || childNode.getLabel().isEmpty()
+ || prevRefLabel.equals(childNode.getLabel() + " (sent)") || prevRefLabel.equals(childNode.getLabel() + " (change)") || prevRefLabel.equals(childNode.getLabel() + " (received)")) {
+ childNode.setLabel(entry.getLabel());
+ labelChangedEntries.put(new NodeEntry(event.getWallet(), childNode, false), entry);
+ }
+ }
+ Entry spendingEntry = receivedRef.isSpent() ? transactionEntries.get(receivedRef.getSpentBy().getHash()) : null;
+ if(spendingEntry != null && (receivedRef.getSpentBy().getLabel() == null || receivedRef.getSpentBy().getLabel().isEmpty() || receivedRef.getSpentBy().getLabel().endsWith(" (input)"))) {
+ receivedRef.getSpentBy().setLabel(spendingEntry.getLabel() + " (input)");
+ labelChangedEntries.put(new HashIndexEntry(event.getWallet(), receivedRef.getSpentBy(), HashIndexEntry.Type.INPUT, keyPurpose), spendingEntry);
+ }
+ }
+ }
+ }
+ }
+
if(!labelChangedEntries.isEmpty()) {
Platform.runLater(() -> EventManager.get().post(new WalletEntryLabelsChangedEvent(wallet, labelChangedEntries)));
} else {
### src/main/java/com/sparrowwallet/sparrow/wallet/WalletUtxosEntry.java
@@ -1,6 +1,9 @@
package com.sparrowwallet.sparrow.wallet;
import com.sparrowwallet.drongo.policy.PolicyType;
+import com.sparrowwallet.drongo.protocol.Sha256Hash;
+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.io.Config;
@@ -52,11 +55,13 @@ protected void calculateDuplicates() {
}
protected void calculateDust() {
+ Map<Wallet, Map<Sha256Hash, BlockTransaction>> walletTransactions = new HashMap<>();
+ Map<Wallet, Map<BlockTransactionHashIndex, WalletNode>> walletTxos = new HashMap<>();
if(getWallet().getPolicyType() == PolicyType.SINGLE_SP) {
long dustAttackThreshold = Config.get().getDustAttackThresholdSp();
for(Entry entry : getChildren()) {
UtxoEntry utxoEntry = (UtxoEntry) entry;
- utxoEntry.setDustAttack(utxoEntry.getValue() <= dustAttackThreshold && !utxoEntry.getWallet().allInputsFromWallet(utxoEntry.getHashIndex().getHash()));
+ utxoEntry.setDustAttack(utxoEntry.getValue() <= dustAttackThreshold && !allInputsFromWallet(utxoEntry, walletTransactions, walletTxos));
}
} else {
long dustAttackThreshold = Config.get().getDustAttackThreshold();
@@ -66,11 +71,18 @@ protected void calculateDust() {
for(Entry entry : getChildren()) {
UtxoEntry utxoEntry = (UtxoEntry) entry;
- utxoEntry.setDustAttack(utxoEntry.getValue() <= dustAttackThreshold && duplicateNodes.contains(utxoEntry.getNode()) && !utxoEntry.getWallet().allInputsFromWallet(utxoEntry.getHashIndex().getHash()));
+ utxoEntry.setDustAttack(utxoEntry.getValue() <= dustAttackThreshold && duplicateNodes.contains(utxoEntry.getNode()) && !allInputsFromWallet(utxoEntry, walletTransactions, walletTxos));
}
}
}
+ private static boolean allInputsFromWallet(UtxoEntry utxoEntry, Map<Wallet, Map<Sha256Hash, BlockTransaction>> walletTransactions,
+ Map<Wallet, Map<BlockTransactionHashIndex, WalletNode>> walletTxos) {
+ Wallet wallet = utxoEntry.getWallet();
+ return wallet.allInputsFromWallet(walletTransactions.computeIfAbsent(wallet, Wallet::getWalletTransactions), walletTxos.computeIfAbsent(wallet, Wallet::getWalletTxos),
+ utxoEntry.getHashIndex().getHash());
+ }
+
public void updateUtxos() {
List<Entry> current = getWallet().getWalletUtxos().entrySet().stream().map(entry -> new UtxoEntry(entry.getValue().getWallet(), entry.getKey(), HashIndexEntry.Type.OUTPUT, entry.getValue())).collect(Collectors.toList());
List<Entry> previous = new ArrayList<>(getChildren());
### src/test/java/com/sparrowwallet/sparrow/wallet/EntryTest.java
@@ -0,0 +1,96 @@
+package com.sparrowwallet.sparrow.wallet;
+
+import com.sparrowwallet.drongo.KeyPurpose;
+import com.sparrowwallet.drongo.Network;
+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.SparrowWallet;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+
+import static com.sparrowwallet.sparrow.wallet.WalletTransactionsEntryTest.receive;
+import static com.sparrowwallet.sparrow.wallet.WalletTransactionsEntryTest.receiveNode;
+import static com.sparrowwallet.sparrow.wallet.WalletTransactionsEntryTest.testWallet;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * A label change arrives as entries built outside the tables, which relabel the matching entries held in each table's tree.
+ */
+public class EntryTest {
+ @TempDir
+ private static Path tempHome;
+
+ @BeforeAll
+ public static void setup() {
+ System.setProperty(SparrowWallet.APP_HOME_PROPERTY, tempHome.toString());
+ Network.set(Network.MAINNET);
+ }
+
+ @AfterAll
+ public static void tearDown() {
+ System.clearProperty(SparrowWallet.APP_HOME_PROPERTY);
+ Network.set(null);
+ }
+
+ @Test
+ public void changedEntriesRelabelTheirMatchesInTheTree() {
+ Wallet wallet = testWallet();
+ BlockTransaction labelled = receive(wallet, 0, 100000L);
+ BlockTransaction unlabelled = receive(wallet, 1, 200000L);
+ WalletNode labelledNode = receiveNode(wallet, 0);
+ BlockTransactionHashIndex labelledOutput = labelledNode.getTransactionOutputs().iterator().next();
+
+ WalletTransactionsEntry transactionsRoot = new WalletTransactionsEntry(wallet);
+ NodeEntry addressesRoot = new NodeEntry(wallet, wallet.getNode(KeyPurpose.RECEIVE));
+
+ labelled.setLabel("Payment");
+ labelledOutput.setLabel("Payment (received)");
+ labelledNode.setLabel("Payment");
+ //Built as WalletForm builds them: the transaction entry has no children, unlike the one held in the transactions tree
+ List<Entry> changed = List.of(new TransactionEntry(wallet, labelled, Collections.emptyMap(), Collections.emptyMap()),
+ new HashIndexEntry(wallet, labelledOutput, HashIndexEntry.Type.OUTPUT, KeyPurpose.RECEIVE), new NodeEntry(wallet, labelledNode, false));
+
+ transactionsRoot.updateLabels(changed);
+ addressesRoot.updateLabels(changed);
+
+ TransactionEntry labelledEntry = transactionEntry(transactionsRoot, labelled);
+ assertEquals("Payment", labelledEntry.getLabel());
+ assertEquals("Payment (received)", labelledEntry.getChildren().getFirst().getLabel());
+ assertNull(transactionEntry(transactionsRoot, unlabelled).getLabel());
+
+ NodeEntry labelledNodeEntry = nodeEntry(addressesRoot, labelledNode);
+ assertEquals("Payment", labelledNodeEntry.getLabel());
+ assertEquals("Payment (received)", labelledNodeEntry.getChildren().getFirst().getLabel());
+ assertNull(nodeEntry(addressesRoot, receiveNode(wallet, 1)).getLabel());
+ }
+
+ @Test
+ public void entriesFromAnotherWalletLeaveTheTreeUnchanged() {
+ Wallet wallet = testWallet();
+ BlockTransaction received = receive(wallet, 0, 100000L);
+ WalletTransactionsEntry transactionsRoot = new WalletTransactionsEntry(wallet);
+
+ Wallet otherWallet = testWallet();
+ received.setLabel("Payment");
+ transactionsRoot.updateLabels(List.of(new TransactionEntry(otherWallet, received, Collections.emptyMap(), Collections.emptyMap())));
+
+ assertNull(transactionEntry(transactionsRoot, received).getLabel());
+ }
+
+ private static TransactionEntry transactionEntry(WalletTransactionsEntry root, BlockTransaction blockTransaction) {
+ return root.getChildren().stream().map(TransactionEntry.class::cast).filter(entry -> entry.getBlockTransaction().equals(blockTransaction)).findFirst().orElseThrow();
+ }
+
+ private static NodeEntry nodeEntry(NodeEntry root, WalletNode node) {
+ return root.getChildren().stream().map(NodeEntry.class::cast).filter(entry -> entry.getNode().equals(node)).findFirst().orElseThrow();
+ }
+}
### src/test/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntryTest.java
@@ -252,7 +252,7 @@ private static TransactionEntry entryFor(WalletTransactionsEntry walletTransacti
.filter(entry -> entry.getBlockTransaction().getHash().equals(blockTransaction.getHash())).findFirst().orElseThrow();
}
- private static BlockTransaction receive(Wallet wallet, int index, long value) {
+ static BlockTransaction receive(Wallet wallet, int index, long value) {
WalletNode node = receiveNode(wallet, index);
Transaction transaction = new Transaction();
transaction.addInput(FUNDING_TXID, index, new Script(new byte[0]));
@@ -265,11 +265,11 @@ private static BlockTransaction receive(Wallet wallet, int index, long value) {
return blockTransaction;
}
- private static WalletNode receiveNode(Wallet wallet, int index) {
+ static WalletNode receiveNode(Wallet wallet, int index) {
return wallet.getNode(KeyPurpose.RECEIVE).getChildren().stream().filter(node -> node.getIndex() == index).findFirst().orElseThrow();
}
- private static Wallet testWallet() {
+ static Wallet testWallet() {
Wallet wallet = new Wallet();
wallet.setPolicyType(PolicyType.SINGLE_HD);
wallet.setScriptType(ScriptType.P2WPKH);Why this scored 25/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.