From b2f5f5ffebfc0ce9f0d992f3ac3b59965661491b Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Thu, 21 Jul 2022 12:29:21 +0200 Subject: [PATCH] spend change from notification transactions only if necessary --- .../drongo/bip47/PaymentCode.java | 4 ++++ .../drongo/wallet/OutputGroup.java | 16 +++++++++---- .../sparrowwallet/drongo/wallet/Wallet.java | 23 +++++++++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/sparrowwallet/drongo/bip47/PaymentCode.java b/src/main/java/com/sparrowwallet/drongo/bip47/PaymentCode.java index c2a2966..ecdf87e 100644 --- a/src/main/java/com/sparrowwallet/drongo/bip47/PaymentCode.java +++ b/src/main/java/com/sparrowwallet/drongo/bip47/PaymentCode.java @@ -174,6 +174,10 @@ public class PaymentCode { if(scriptChunks.get(1).getData() != null && scriptChunks.get(1).getData().length != 80) { return null; } + byte[] data = scriptChunks.get(1).getData(); + if(data[0] != 0x01 || (data[2] != 0x02 && data[2] != 0x03)) { + return null; + } return scriptChunks; } diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/OutputGroup.java b/src/main/java/com/sparrowwallet/drongo/wallet/OutputGroup.java index b66e928..59b703c 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/OutputGroup.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/OutputGroup.java @@ -20,6 +20,7 @@ public class OutputGroup { private long longTermFee = 0; private int depth = Integer.MAX_VALUE; private boolean allInputsFromWallet = true; + private boolean spendLast; public OutputGroup(ScriptType scriptType, int walletBlockHeight, long inputWeightUnits, double feeRate, double longTermFeeRate) { this.scriptType = scriptType; @@ -29,7 +30,7 @@ public class OutputGroup { this.longTermFeeRate = longTermFeeRate; } - public void add(BlockTransactionHashIndex utxo, boolean allInputsFromWallet) { + public void add(BlockTransactionHashIndex utxo, boolean allInputsFromWallet, boolean spendLast) { utxos.add(utxo); value += utxo.getValue(); effectiveValue += utxo.getValue() - (long)(inputWeightUnits * feeRate / WITNESS_SCALE_FACTOR); @@ -37,6 +38,7 @@ public class OutputGroup { longTermFee += (long)(inputWeightUnits * longTermFeeRate / WITNESS_SCALE_FACTOR); depth = utxo.getHeight() <= 0 ? 0 : Math.min(depth, walletBlockHeight - utxo.getHeight() + 1); this.allInputsFromWallet &= allInputsFromWallet; + this.spendLast |= spendLast; } public void remove(BlockTransactionHashIndex utxo) { @@ -80,21 +82,27 @@ public class OutputGroup { return allInputsFromWallet; } + public boolean isSpendLast() { + return spendLast; + } + public static class Filter { private final int minWalletConfirmations; private final int minExternalConfirmations; + private final boolean includeSpendLast; - public Filter(int minWalletConfirmations, int minExternalConfirmations) { + public Filter(int minWalletConfirmations, int minExternalConfirmations, boolean includeSpendLast) { this.minWalletConfirmations = minWalletConfirmations; this.minExternalConfirmations = minExternalConfirmations; + this.includeSpendLast = includeSpendLast; } public boolean isEligible(OutputGroup outputGroup) { if(outputGroup.isAllInputsFromWallet()) { - return outputGroup.getDepth() >= minWalletConfirmations; + return outputGroup.getDepth() >= minWalletConfirmations && (includeSpendLast || !outputGroup.isSpendLast()); } - return outputGroup.getDepth() >= minExternalConfirmations; + return outputGroup.getDepth() >= minExternalConfirmations && (includeSpendLast || !outputGroup.isSpendLast()); } } } diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index d69755f..76eff7e 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -1163,10 +1163,13 @@ public class Wallet extends Persistable implements Comparable { List utxoPool = getGroupedUtxos(utxoFilters, feeRate, longTermFeeRate, groupByAddress, includeSpentMempoolOutputs); List filters = new ArrayList<>(); - filters.add(new OutputGroup.Filter(1, 6)); - filters.add(new OutputGroup.Filter(1, 1)); + filters.add(new OutputGroup.Filter(1, 6, false)); + filters.add(new OutputGroup.Filter(1, 1, false)); if(includeMempoolOutputs) { - filters.add(new OutputGroup.Filter(0, 0)); + filters.add(new OutputGroup.Filter(0, 0, false)); + filters.add(new OutputGroup.Filter(0, 0, true)); + } else { + filters.add(new OutputGroup.Filter(1, 1, true)); } if(sendMax) { @@ -1237,7 +1240,7 @@ public class Wallet extends Persistable implements Comparable { outputGroups.add(outputGroup); } - outputGroup.add(utxo, allInputsFromWallet(walletTransactions, walletTxos, utxo.getHash())); + outputGroup.add(utxo, allInputsFromWallet(walletTransactions, walletTxos, utxo.getHash()), isNotificationChange(walletTransactions, utxo.getHash())); } } } @@ -1280,6 +1283,18 @@ public class Wallet extends Persistable implements Comparable { return true; } + private boolean isNotificationChange(Map walletTransactions, Sha256Hash txId) { + BlockTransaction utxoBlkTx = walletTransactions.get(txId); + try { + PaymentCode.getOpReturnData(utxoBlkTx.getTransaction()); + return true; + } catch(IllegalArgumentException e) { + //ignore, not a notification tx + } + + return false; + } + /** * Determines the maximum total amount this wallet can send for the number and type of addresses at the given fee rate *