From 4e68815fa977a45a7caddead35e5d0f90f5e8fd6 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Fri, 17 Oct 2025 10:25:52 +0200 Subject: [PATCH] use declarative style to indicate consolidation payments and include their bip32 derivations in psbt outputs --- .../com/sparrowwallet/drongo/psbt/PSBT.java | 4 +- .../sparrowwallet/drongo/wallet/Wallet.java | 5 +- .../drongo/wallet/WalletNodePayment.java | 18 ++++++ .../drongo/wallet/WalletTransaction.java | 62 ++++++++----------- 4 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/drongo/wallet/WalletNodePayment.java diff --git a/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java b/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java index 58ce8d2..97d3266 100644 --- a/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java +++ b/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java @@ -155,8 +155,8 @@ public class PSBT { psbtOutput = new PSBTOutput(this, outputIndex, null, null, null, Collections.emptyMap(), Collections.emptyMap(), null, silentPaymentOutput.getSilentPayment().getSilentPaymentAddress(), silentPaymentOutput.getDnsSecProof()); } else if(output instanceof WalletTransaction.PaymentOutput paymentOutput) { psbtOutput = new PSBTOutput(this, outputIndex, null, null, null, Collections.emptyMap(), Collections.emptyMap(), null, null, paymentOutput.getDnsSecProof()); - } else if(output instanceof WalletTransaction.ChangeOutput changeOutput) { - WalletNode outputNode = changeOutput.getWalletNode(); + } else if(output instanceof WalletTransaction.WalletNodeOutput walletNodeOutput) { + WalletNode outputNode = walletNodeOutput.getWalletNode(); TransactionOutput txOutput = transaction.getOutputs().get(outputIndex); Wallet recipientWallet = outputNode.getWallet(); diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index d228ec3..ff8f977 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -1090,7 +1090,7 @@ public class Wallet extends Persistable implements Comparable { for(int i = 1; i < numSets; i+=2) { WalletNode mixNode = getFreshNode(getChangeKeyPurpose()); txExcludedChangeNodes.add(mixNode); - Payment fakeMixPayment = new Payment(mixNode.getAddress(), ".." + mixNode + " (Fake Mix)", totalPaymentAmount, false); + Payment fakeMixPayment = new WalletNodePayment(mixNode, ".." + mixNode + " (Fake Mix)", totalPaymentAmount, false); fakeMixPayment.setType(Payment.Type.FAKE_MIX); txPayments.add(fakeMixPayment); } @@ -1100,6 +1100,9 @@ public class Wallet extends Persistable implements Comparable { if(payment instanceof SilentPayment silentPayment) { TransactionOutput output = transaction.addOutput(payment.getAmount(), new Script(new byte[0])); outputs.add(new WalletTransaction.SilentPaymentOutput(output, silentPayment)); + } else if(payment instanceof WalletNodePayment walletNodePayment) { + TransactionOutput output = transaction.addOutput(payment.getAmount(), payment.getAddress()); + outputs.add(new WalletTransaction.ConsolidationOutput(output, walletNodePayment, payment.getAmount())); } else { TransactionOutput output = transaction.addOutput(payment.getAmount(), payment.getAddress()); outputs.add(new WalletTransaction.PaymentOutput(output, payment)); diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/WalletNodePayment.java b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNodePayment.java new file mode 100644 index 0000000..cba7594 --- /dev/null +++ b/src/main/java/com/sparrowwallet/drongo/wallet/WalletNodePayment.java @@ -0,0 +1,18 @@ +package com.sparrowwallet.drongo.wallet; + +public class WalletNodePayment extends Payment { + private final WalletNode walletNode; + + public WalletNodePayment(WalletNode walletNode, String label, long amount, boolean sendMax) { + this(walletNode, label, amount, sendMax, Type.DEFAULT); + } + + public WalletNodePayment(WalletNode walletNode, String label, long amount, boolean sendMax, Type type) { + super(walletNode.getAddress(), label, amount, sendMax, type); + this.walletNode = walletNode; + } + + public WalletNode getWalletNode() { + return walletNode; + } +} diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/WalletTransaction.java b/src/main/java/com/sparrowwallet/drongo/wallet/WalletTransaction.java index 9426237..db2a9d0 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/WalletTransaction.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/WalletTransaction.java @@ -25,8 +25,6 @@ public class WalletTransaction { private final Map inputTransactions; private final List outputs; - private Map> addressNodeMap = new HashMap<>(); - public WalletTransaction(Wallet wallet, Transaction transaction, List utxoSelectors, List> selectedUtxoSets, List payments, List outputs, long fee) { this(wallet, transaction, utxoSelectors, selectedUtxoSets, payments, outputs, Collections.emptyMap(), fee); } @@ -137,10 +135,6 @@ public class WalletTransaction { return !utxoSelectors.isEmpty() && utxoSelectors.get(0) instanceof StonewallUtxoSelector; } - public boolean isConsolidationSend(Payment payment) { - return isWalletSend(getWallet(), payment); - } - public boolean isPremixSend(Payment payment) { return isWalletSend(StandardAccount.WHIRLPOOL_PREMIX, payment); } @@ -167,7 +161,7 @@ public class WalletTransaction { return false; } - return getAddressNodeMap(wallet).get(payment.getAddress()) != null; + return wallet.getWalletAddresses().get(payment.getAddress()) != null; } private String getOutputLabel(Payment payment) { @@ -212,35 +206,12 @@ public class WalletTransaction { .anyMatch(p -> payment.getAddress() != null && payment.getAddress().equals(p.getAddress())); } - public void updateAddressNodeMap(Map> addressNodeMap, Wallet wallet) { - this.addressNodeMap = addressNodeMap; - getAddressNodeMap(wallet); + public List getExternalPayments() { + return payments.stream().filter(payment -> !(payment instanceof WalletNodePayment)).collect(Collectors.toList()); } - public Map getAddressNodeMap() { - return getAddressNodeMap(getWallet()); - } - - public Map getAddressNodeMap(Wallet wallet) { - Map walletAddresses = null; - - Map walletAddressNodeMap = addressNodeMap.computeIfAbsent(wallet, w -> new LinkedHashMap<>()); - for(Payment payment : payments) { - if(walletAddressNodeMap.containsKey(payment.getAddress())) { - continue; - } - - if(payment.getAddress() != null && wallet != null) { - if(walletAddresses == null) { - walletAddresses = wallet.getWalletAddresses(); - } - - WalletNode walletNode = walletAddresses.get(payment.getAddress()); - walletAddressNodeMap.put(payment.getAddress(), walletNode); - } - } - - return walletAddressNodeMap; + public List getWalletNodePayments() { + return payments.stream().filter(payment -> payment instanceof WalletNodePayment).map(payment -> (WalletNodePayment)payment).collect(Collectors.toList()); } public static class Output { @@ -301,11 +272,11 @@ public class WalletTransaction { } } - public static class ChangeOutput extends Output { + public static class WalletNodeOutput extends Output { private final WalletNode walletNode; private final Long value; - public ChangeOutput(TransactionOutput transactionOutput, WalletNode walletNode, Long value) { + public WalletNodeOutput(TransactionOutput transactionOutput, WalletNode walletNode, Long value) { super(transactionOutput); this.walletNode = walletNode; this.value = value; @@ -319,4 +290,23 @@ public class WalletTransaction { return value; } } + + public static class ConsolidationOutput extends WalletNodeOutput { + private final WalletNodePayment walletNodePayment; + + public ConsolidationOutput(TransactionOutput transactionOutput, WalletNodePayment walletNodePayment, Long value) { + super(transactionOutput, walletNodePayment.getWalletNode(), value); + this.walletNodePayment = walletNodePayment; + } + + public WalletNodePayment getWalletNodePayment() { + return walletNodePayment; + } + } + + public static class ChangeOutput extends WalletNodeOutput { + public ChangeOutput(TransactionOutput transactionOutput, WalletNode walletNode, Long value) { + super(transactionOutput, walletNode, value); + } + } }