use declarative style to indicate consolidation payments and include their bip32 derivations in psbt outputs

This commit is contained in:
Craig Raw 2025-10-17 10:25:52 +02:00
parent 286e04ad25
commit 4e68815fa9
4 changed files with 50 additions and 39 deletions

View file

@ -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()); 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) { } else if(output instanceof WalletTransaction.PaymentOutput paymentOutput) {
psbtOutput = new PSBTOutput(this, outputIndex, null, null, null, Collections.emptyMap(), Collections.emptyMap(), null, null, paymentOutput.getDnsSecProof()); psbtOutput = new PSBTOutput(this, outputIndex, null, null, null, Collections.emptyMap(), Collections.emptyMap(), null, null, paymentOutput.getDnsSecProof());
} else if(output instanceof WalletTransaction.ChangeOutput changeOutput) { } else if(output instanceof WalletTransaction.WalletNodeOutput walletNodeOutput) {
WalletNode outputNode = changeOutput.getWalletNode(); WalletNode outputNode = walletNodeOutput.getWalletNode();
TransactionOutput txOutput = transaction.getOutputs().get(outputIndex); TransactionOutput txOutput = transaction.getOutputs().get(outputIndex);
Wallet recipientWallet = outputNode.getWallet(); Wallet recipientWallet = outputNode.getWallet();

View file

@ -1090,7 +1090,7 @@ public class Wallet extends Persistable implements Comparable<Wallet> {
for(int i = 1; i < numSets; i+=2) { for(int i = 1; i < numSets; i+=2) {
WalletNode mixNode = getFreshNode(getChangeKeyPurpose()); WalletNode mixNode = getFreshNode(getChangeKeyPurpose());
txExcludedChangeNodes.add(mixNode); 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); fakeMixPayment.setType(Payment.Type.FAKE_MIX);
txPayments.add(fakeMixPayment); txPayments.add(fakeMixPayment);
} }
@ -1100,6 +1100,9 @@ public class Wallet extends Persistable implements Comparable<Wallet> {
if(payment instanceof SilentPayment silentPayment) { if(payment instanceof SilentPayment silentPayment) {
TransactionOutput output = transaction.addOutput(payment.getAmount(), new Script(new byte[0])); TransactionOutput output = transaction.addOutput(payment.getAmount(), new Script(new byte[0]));
outputs.add(new WalletTransaction.SilentPaymentOutput(output, silentPayment)); 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 { } else {
TransactionOutput output = transaction.addOutput(payment.getAmount(), payment.getAddress()); TransactionOutput output = transaction.addOutput(payment.getAmount(), payment.getAddress());
outputs.add(new WalletTransaction.PaymentOutput(output, payment)); outputs.add(new WalletTransaction.PaymentOutput(output, payment));

View file

@ -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;
}
}

View file

@ -25,8 +25,6 @@ public class WalletTransaction {
private final Map<Sha256Hash, BlockTransaction> inputTransactions; private final Map<Sha256Hash, BlockTransaction> inputTransactions;
private final List<Output> outputs; private final List<Output> outputs;
private Map<Wallet, Map<Address, WalletNode>> addressNodeMap = new HashMap<>();
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, List<Map<BlockTransactionHashIndex, WalletNode>> selectedUtxoSets, List<Payment> payments, List<Output> outputs, long fee) { public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, List<Map<BlockTransactionHashIndex, WalletNode>> selectedUtxoSets, List<Payment> payments, List<Output> outputs, long fee) {
this(wallet, transaction, utxoSelectors, selectedUtxoSets, payments, outputs, Collections.emptyMap(), 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; return !utxoSelectors.isEmpty() && utxoSelectors.get(0) instanceof StonewallUtxoSelector;
} }
public boolean isConsolidationSend(Payment payment) {
return isWalletSend(getWallet(), payment);
}
public boolean isPremixSend(Payment payment) { public boolean isPremixSend(Payment payment) {
return isWalletSend(StandardAccount.WHIRLPOOL_PREMIX, payment); return isWalletSend(StandardAccount.WHIRLPOOL_PREMIX, payment);
} }
@ -167,7 +161,7 @@ public class WalletTransaction {
return false; return false;
} }
return getAddressNodeMap(wallet).get(payment.getAddress()) != null; return wallet.getWalletAddresses().get(payment.getAddress()) != null;
} }
private String getOutputLabel(Payment payment) { private String getOutputLabel(Payment payment) {
@ -212,35 +206,12 @@ public class WalletTransaction {
.anyMatch(p -> payment.getAddress() != null && payment.getAddress().equals(p.getAddress())); .anyMatch(p -> payment.getAddress() != null && payment.getAddress().equals(p.getAddress()));
} }
public void updateAddressNodeMap(Map<Wallet, Map<Address, WalletNode>> addressNodeMap, Wallet wallet) { public List<Payment> getExternalPayments() {
this.addressNodeMap = addressNodeMap; return payments.stream().filter(payment -> !(payment instanceof WalletNodePayment)).collect(Collectors.toList());
getAddressNodeMap(wallet);
} }
public Map<Address, WalletNode> getAddressNodeMap() { public List<WalletNodePayment> getWalletNodePayments() {
return getAddressNodeMap(getWallet()); return payments.stream().filter(payment -> payment instanceof WalletNodePayment).map(payment -> (WalletNodePayment)payment).collect(Collectors.toList());
}
public Map<Address, WalletNode> getAddressNodeMap(Wallet wallet) {
Map<Address, WalletNode> walletAddresses = null;
Map<Address, WalletNode> 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 static class Output { 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 WalletNode walletNode;
private final Long value; private final Long value;
public ChangeOutput(TransactionOutput transactionOutput, WalletNode walletNode, Long value) { public WalletNodeOutput(TransactionOutput transactionOutput, WalletNode walletNode, Long value) {
super(transactionOutput); super(transactionOutput);
this.walletNode = walletNode; this.walletNode = walletNode;
this.value = value; this.value = value;
@ -319,4 +290,23 @@ public class WalletTransaction {
return value; 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);
}
}
} }