mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54:15 +00:00
Wallet: Split DefaultCoinSelector out into a top level class in the wallet package.
This commit is contained in:
parent
c366c5fa44
commit
2b4595c4f0
@ -25,10 +25,7 @@ import com.google.bitcoin.store.UnreadableWalletException;
|
||||
import com.google.bitcoin.store.WalletProtobufSerializer;
|
||||
import com.google.bitcoin.utils.ListenerRegistration;
|
||||
import com.google.bitcoin.utils.Threading;
|
||||
import com.google.bitcoin.wallet.CoinSelection;
|
||||
import com.google.bitcoin.wallet.CoinSelector;
|
||||
import com.google.bitcoin.wallet.KeyTimeCoinSelector;
|
||||
import com.google.bitcoin.wallet.WalletFiles;
|
||||
import com.google.bitcoin.wallet.*;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
@ -156,77 +153,6 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
|
||||
private volatile long vKeyRotationTimestamp;
|
||||
private volatile boolean vKeyRotationEnabled;
|
||||
|
||||
/**
|
||||
* This class implements a {@link CoinSelector} which attempts to get the highest priority possible. This means that
|
||||
* the transaction is the most likely to get confirmed
|
||||
* Note that this means we may end up "spending" more priority than would be required to get the transaction we are
|
||||
* creating confirmed.
|
||||
*/
|
||||
public static class DefaultCoinSelector implements CoinSelector {
|
||||
public CoinSelection select(BigInteger biTarget, LinkedList<TransactionOutput> candidates) {
|
||||
long target = biTarget.longValue();
|
||||
HashSet<TransactionOutput> selected = new HashSet<TransactionOutput>();
|
||||
// Sort the inputs by age*value so we get the highest "coindays" spent.
|
||||
// TODO: Consider changing the wallets internal format to track just outputs and keep them ordered.
|
||||
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates);
|
||||
// When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
|
||||
// them in order to improve performance.
|
||||
if (!biTarget.equals(NetworkParameters.MAX_MONEY)) {
|
||||
Collections.sort(sortedOutputs, new Comparator<TransactionOutput>() {
|
||||
public int compare(TransactionOutput a, TransactionOutput b) {
|
||||
int depth1 = 0;
|
||||
int depth2 = 0;
|
||||
TransactionConfidence conf1 = a.parentTransaction.getConfidence();
|
||||
TransactionConfidence conf2 = b.parentTransaction.getConfidence();
|
||||
if (conf1.getConfidenceType() == ConfidenceType.BUILDING) depth1 = conf1.getDepthInBlocks();
|
||||
if (conf2.getConfidenceType() == ConfidenceType.BUILDING) depth2 = conf2.getDepthInBlocks();
|
||||
BigInteger aValue = a.getValue();
|
||||
BigInteger bValue = b.getValue();
|
||||
BigInteger aCoinDepth = aValue.multiply(BigInteger.valueOf(depth1));
|
||||
BigInteger bCoinDepth = bValue.multiply(BigInteger.valueOf(depth2));
|
||||
int c1 = bCoinDepth.compareTo(aCoinDepth);
|
||||
if (c1 != 0) return c1;
|
||||
// The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size.
|
||||
int c2 = bValue.compareTo(aValue);
|
||||
if (c2 != 0) return c2;
|
||||
// They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering.
|
||||
BigInteger aHash = a.parentTransaction.getHash().toBigInteger();
|
||||
BigInteger bHash = b.parentTransaction.getHash().toBigInteger();
|
||||
return aHash.compareTo(bHash);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Now iterate over the sorted outputs until we have got as close to the target as possible or a little
|
||||
// bit over (excessive value will be change).
|
||||
long total = 0;
|
||||
for (TransactionOutput output : sortedOutputs) {
|
||||
if (total >= target) break;
|
||||
// Only pick chain-included transactions, or transactions that are ours and pending.
|
||||
if (!shouldSelect(output.parentTransaction)) continue;
|
||||
selected.add(output);
|
||||
total += output.getValue().longValue();
|
||||
}
|
||||
// Total may be lower than target here, if the given candidates were insufficient to create to requested
|
||||
// transaction.
|
||||
return new CoinSelection(BigInteger.valueOf(total), selected);
|
||||
}
|
||||
|
||||
/** Sub-classes can override this to just customize whether transactions are usable, but keep age sorting. */
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
return isSelectable(tx);
|
||||
}
|
||||
|
||||
public static boolean isSelectable(Transaction tx) {
|
||||
// Only pick chain-included transactions, or transactions that are ours and pending.
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
ConfidenceType type = confidence.getConfidenceType();
|
||||
if (type.equals(ConfidenceType.BUILDING)) return true;
|
||||
return type.equals(ConfidenceType.PENDING) &&
|
||||
confidence.getSource().equals(TransactionConfidence.Source.SELF) &&
|
||||
confidence.numBroadcastPeers() > 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This coin selector will select any transaction at all, regardless of where it came from or whether it was
|
||||
* confirmed yet. However immature coinbases will not be included (would be a protocol violation).
|
||||
|
@ -0,0 +1,81 @@
|
||||
package com.google.bitcoin.wallet;
|
||||
|
||||
import com.google.bitcoin.core.NetworkParameters;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
import com.google.bitcoin.core.TransactionOutput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class implements a {@link com.google.bitcoin.wallet.CoinSelector} which attempts to get the highest priority
|
||||
* possible. This means that the transaction is the most likely to get confirmed. Note that this means we may end up
|
||||
* "spending" more priority than would be required to get the transaction we are creating confirmed.
|
||||
*/
|
||||
public class DefaultCoinSelector implements CoinSelector {
|
||||
public CoinSelection select(BigInteger biTarget, LinkedList<TransactionOutput> candidates) {
|
||||
long target = biTarget.longValue();
|
||||
HashSet<TransactionOutput> selected = new HashSet<TransactionOutput>();
|
||||
// Sort the inputs by age*value so we get the highest "coindays" spent.
|
||||
// TODO: Consider changing the wallets internal format to track just outputs and keep them ordered.
|
||||
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates);
|
||||
// When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
|
||||
// them in order to improve performance.
|
||||
if (!biTarget.equals(NetworkParameters.MAX_MONEY)) {
|
||||
Collections.sort(sortedOutputs, new Comparator<TransactionOutput>() {
|
||||
public int compare(TransactionOutput a, TransactionOutput b) {
|
||||
int depth1 = 0;
|
||||
int depth2 = 0;
|
||||
TransactionConfidence conf1 = a.getParentTransaction().getConfidence();
|
||||
TransactionConfidence conf2 = b.getParentTransaction().getConfidence();
|
||||
if (conf1.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
depth1 = conf1.getDepthInBlocks();
|
||||
if (conf2.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
depth2 = conf2.getDepthInBlocks();
|
||||
BigInteger aValue = a.getValue();
|
||||
BigInteger bValue = b.getValue();
|
||||
BigInteger aCoinDepth = aValue.multiply(BigInteger.valueOf(depth1));
|
||||
BigInteger bCoinDepth = bValue.multiply(BigInteger.valueOf(depth2));
|
||||
int c1 = bCoinDepth.compareTo(aCoinDepth);
|
||||
if (c1 != 0) return c1;
|
||||
// The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size.
|
||||
int c2 = bValue.compareTo(aValue);
|
||||
if (c2 != 0) return c2;
|
||||
// They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering.
|
||||
BigInteger aHash = a.getParentTransaction().getHash().toBigInteger();
|
||||
BigInteger bHash = b.getParentTransaction().getHash().toBigInteger();
|
||||
return aHash.compareTo(bHash);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Now iterate over the sorted outputs until we have got as close to the target as possible or a little
|
||||
// bit over (excessive value will be change).
|
||||
long total = 0;
|
||||
for (TransactionOutput output : sortedOutputs) {
|
||||
if (total >= target) break;
|
||||
// Only pick chain-included transactions, or transactions that are ours and pending.
|
||||
if (!shouldSelect(output.getParentTransaction())) continue;
|
||||
selected.add(output);
|
||||
total += output.getValue().longValue();
|
||||
}
|
||||
// Total may be lower than target here, if the given candidates were insufficient to create to requested
|
||||
// transaction.
|
||||
return new CoinSelection(BigInteger.valueOf(total), selected);
|
||||
}
|
||||
|
||||
/** Sub-classes can override this to just customize whether transactions are usable, but keep age sorting. */
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
return isSelectable(tx);
|
||||
}
|
||||
|
||||
public static boolean isSelectable(Transaction tx) {
|
||||
// Only pick chain-included transactions, or transactions that are ours and pending.
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
||||
if (type.equals(TransactionConfidence.ConfidenceType.BUILDING)) return true;
|
||||
return type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
|
||||
confidence.getSource().equals(TransactionConfidence.Source.SELF) &&
|
||||
confidence.numBroadcastPeers() > 1;
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.script.Script;
|
||||
import com.google.bitcoin.script.ScriptBuilder;
|
||||
import com.google.bitcoin.utils.TestWithWallet;
|
||||
import com.google.bitcoin.wallet.DefaultCoinSelector;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
@ -193,7 +194,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
// Create a block with the payment transaction in it and give it to both wallets
|
||||
chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), new Transaction(params, closeTx.bitcoinSerialize())));
|
||||
|
||||
assertEquals(size.multiply(BigInteger.valueOf(5)), serverWallet.getBalance(new Wallet.DefaultCoinSelector() {
|
||||
assertEquals(size.multiply(BigInteger.valueOf(5)), serverWallet.getBalance(new DefaultCoinSelector() {
|
||||
@Override
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
@ -203,7 +204,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
}));
|
||||
assertEquals(0, serverWallet.getPendingTransactions().size());
|
||||
|
||||
assertEquals(Utils.COIN.subtract(size.multiply(BigInteger.valueOf(5))), wallet.getBalance(new Wallet.DefaultCoinSelector() {
|
||||
assertEquals(Utils.COIN.subtract(size.multiply(BigInteger.valueOf(5))), wallet.getBalance(new DefaultCoinSelector() {
|
||||
@Override
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
|
Loading…
x
Reference in New Issue
Block a user