mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-26 01:56:44 +00:00
fix high stonewall fee issue, add support for generalized transaction diagram
This commit is contained in:
parent
eb49c97133
commit
99440eda7f
4 changed files with 42 additions and 6 deletions
|
@ -165,6 +165,14 @@ public class Script {
|
||||||
throw new ProtocolException("Script not a standard form that contains a single hash");
|
throw new ProtocolException("Script not a standard form that contains a single hash");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Address getToAddress() {
|
||||||
|
try {
|
||||||
|
return getToAddresses()[0];
|
||||||
|
} catch(Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the destination address from this script, if it's in the required form.
|
* Gets the destination address from this script, if it's in the required form.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,13 +7,18 @@ public class Payment {
|
||||||
private String label;
|
private String label;
|
||||||
private long amount;
|
private long amount;
|
||||||
private boolean sendMax;
|
private boolean sendMax;
|
||||||
private Type type = Type.DEFAULT;
|
private Type type;
|
||||||
|
|
||||||
public Payment(Address address, String label, long amount, boolean sendMax) {
|
public Payment(Address address, String label, long amount, boolean sendMax) {
|
||||||
|
this(address, label, amount, sendMax, Type.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Payment(Address address, String label, long amount, boolean sendMax, Type type) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.sendMax = sendMax;
|
this.sendMax = sendMax;
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Address getAddress() {
|
public Address getAddress() {
|
||||||
|
|
|
@ -518,6 +518,10 @@ public class Wallet extends Persistable implements Comparable<Wallet> {
|
||||||
return getWalletTxos().keySet().stream().anyMatch(ref -> ref.getHash().equals(txInput.getOutpoint().getHash()) && ref.getIndex() == txInput.getOutpoint().getIndex());
|
return getWalletTxos().keySet().stream().anyMatch(ref -> ref.getHash().equals(txInput.getOutpoint().getHash()) && ref.getIndex() == txInput.getOutpoint().getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isWalletTxo(TransactionOutput txOutput) {
|
||||||
|
return getWalletTxos().keySet().stream().anyMatch(ref -> ref.getHash().equals(txOutput.getHash()) && ref.getIndex() == txOutput.getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isWalletTxo(BlockTransactionHashIndex txo) {
|
public boolean isWalletTxo(BlockTransactionHashIndex txo) {
|
||||||
return getWalletTxos().containsKey(txo);
|
return getWalletTxos().containsKey(txo);
|
||||||
}
|
}
|
||||||
|
@ -771,7 +775,7 @@ public class Wallet extends Persistable implements Comparable<Wallet> {
|
||||||
List<Long> setChangeAmts = getSetChangeAmounts(selectedUtxoSets, totalPaymentAmount, noChangeFeeRequiredAmt);
|
List<Long> setChangeAmts = getSetChangeAmounts(selectedUtxoSets, totalPaymentAmount, noChangeFeeRequiredAmt);
|
||||||
double noChangeFeeRate = (fee == null ? feeRate : noChangeFeeRequiredAmt / transaction.getVirtualSize());
|
double noChangeFeeRate = (fee == null ? feeRate : noChangeFeeRequiredAmt / transaction.getVirtualSize());
|
||||||
long costOfChangeAmt = getCostOfChange(noChangeFeeRate, longTermFeeRate);
|
long costOfChangeAmt = getCostOfChange(noChangeFeeRate, longTermFeeRate);
|
||||||
if(setChangeAmts.stream().allMatch(amt -> amt > costOfChangeAmt)) {
|
if(setChangeAmts.stream().allMatch(amt -> amt > costOfChangeAmt) || (numSets > 1 && differenceAmt / transaction.getVirtualSize() > noChangeFeeRate * 2)) {
|
||||||
//Change output is required, determine new fee once change output has been added
|
//Change output is required, determine new fee once change output has been added
|
||||||
WalletNode changeNode = getFreshNode(KeyPurpose.CHANGE);
|
WalletNode changeNode = getFreshNode(KeyPurpose.CHANGE);
|
||||||
while(txExcludedChangeNodes.contains(changeNode)) {
|
while(txExcludedChangeNodes.contains(changeNode)) {
|
||||||
|
@ -796,7 +800,7 @@ public class Wallet extends Persistable implements Comparable<Wallet> {
|
||||||
|
|
||||||
if(setChangeAmts.stream().anyMatch(amt -> amt < costOfChangeAmt)) {
|
if(setChangeAmts.stream().anyMatch(amt -> amt < costOfChangeAmt)) {
|
||||||
//The new fee has meant that one of the change outputs is now dust. We pay too high a fee without change, but change is dust when added.
|
//The new fee has meant that one of the change outputs is now dust. We pay too high a fee without change, but change is dust when added.
|
||||||
if(numSets > 1) {
|
if(numSets > 1 && differenceAmt / transaction.getVirtualSize() < noChangeFeeRate * 2) {
|
||||||
//Maximize privacy. Pay a higher fee to keep multiple output sets.
|
//Maximize privacy. Pay a higher fee to keep multiple output sets.
|
||||||
return new WalletTransaction(this, transaction, utxoSelectors, selectedUtxos, txPayments, differenceAmt);
|
return new WalletTransaction(this, transaction, utxoSelectors, selectedUtxos, txPayments, differenceAmt);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.sparrowwallet.drongo.wallet;
|
package com.sparrowwallet.drongo.wallet;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.address.Address;
|
import com.sparrowwallet.drongo.address.Address;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
|
|
||||||
|
@ -21,12 +22,17 @@ public class WalletTransaction {
|
||||||
private final List<Payment> payments;
|
private final List<Payment> payments;
|
||||||
private final Map<WalletNode, Long> changeMap;
|
private final Map<WalletNode, Long> changeMap;
|
||||||
private final long fee;
|
private final long fee;
|
||||||
|
private final Map<Sha256Hash, BlockTransaction> inputTransactions;
|
||||||
|
|
||||||
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, long fee) {
|
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, long fee) {
|
||||||
this(wallet, transaction, utxoSelectors, selectedUtxos, payments, Collections.emptyMap(), fee);
|
this(wallet, transaction, utxoSelectors, selectedUtxos, payments, Collections.emptyMap(), fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, Map<WalletNode, Long> changeMap, long fee) {
|
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, Map<WalletNode, Long> changeMap, long fee) {
|
||||||
|
this(wallet, transaction, utxoSelectors, selectedUtxos, payments, changeMap, fee, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, Map<WalletNode, Long> changeMap, long fee, Map<Sha256Hash, BlockTransaction> inputTransactions) {
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
this.transaction = transaction;
|
this.transaction = transaction;
|
||||||
this.utxoSelectors = utxoSelectors;
|
this.utxoSelectors = utxoSelectors;
|
||||||
|
@ -34,6 +40,7 @@ public class WalletTransaction {
|
||||||
this.payments = payments;
|
this.payments = payments;
|
||||||
this.changeMap = changeMap;
|
this.changeMap = changeMap;
|
||||||
this.fee = fee;
|
this.fee = fee;
|
||||||
|
this.inputTransactions = inputTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PSBT createPSBT() {
|
public PSBT createPSBT() {
|
||||||
|
@ -80,12 +87,16 @@ public class WalletTransaction {
|
||||||
return selectedUtxos.keySet().stream().mapToLong(BlockTransactionHashIndex::getValue).sum();
|
return selectedUtxos.keySet().stream().mapToLong(BlockTransactionHashIndex::getValue).sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<Sha256Hash, BlockTransaction> getInputTransactions() {
|
||||||
|
return inputTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fee percentage matches the Coldcard implementation of total fee as a percentage of total value out
|
* Fee percentage matches the Coldcard implementation of total fee as a percentage of total value out
|
||||||
* @return the fee percentage
|
* @return the fee percentage
|
||||||
*/
|
*/
|
||||||
public double getFeePercentage() {
|
public double getFeePercentage() {
|
||||||
return (double)getFee() / (getTotal() - getFee());
|
return getFee() == 0 ? 0 : (double)getFee() / (getTotal() - getFee());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCoinControlUsed() {
|
public boolean isCoinControlUsed() {
|
||||||
|
@ -97,11 +108,19 @@ public class WalletTransaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPremixSend(Payment payment) {
|
public boolean isPremixSend(Payment payment) {
|
||||||
return isWalletSend(getWallet().getChildWallet(StandardAccount.WHIRLPOOL_PREMIX), payment);
|
return isWalletSend(StandardAccount.WHIRLPOOL_PREMIX, payment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBadbankSend(Payment payment) {
|
public boolean isBadbankSend(Payment payment) {
|
||||||
return isWalletSend(getWallet().getChildWallet(StandardAccount.WHIRLPOOL_BADBANK), payment);
|
return isWalletSend(StandardAccount.WHIRLPOOL_BADBANK, payment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWalletSend(StandardAccount childAccount, Payment payment) {
|
||||||
|
if(getWallet() != null) {
|
||||||
|
return isWalletSend(getWallet().getChildWallet(childAccount), payment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWalletSend(Wallet wallet, Payment payment) {
|
public boolean isWalletSend(Wallet wallet, Payment payment) {
|
||||||
|
|
Loading…
Reference in a new issue