mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +00:00
Consider age of outputs when creating spends.
This commit is contained in:
parent
757334da80
commit
e99e4bd63c
@ -221,9 +221,30 @@ public class Wallet implements Serializable, BlockChainListener {
|
||||
long target = biTarget.longValue();
|
||||
long total = 0;
|
||||
LinkedList<TransactionOutput> selected = Lists.newLinkedList();
|
||||
// Super dumb algorithm: just iterate through candidates and keep adding them in whatever order until we
|
||||
// have enough.
|
||||
for (TransactionOutput output : candidates) {
|
||||
// Sort the inputs by age so we use oldest first.
|
||||
// TODO: Consider changing the wallets internal format to track just outputs and keep them ordered.
|
||||
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates);
|
||||
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();
|
||||
if (depth1 < depth2)
|
||||
return 1;
|
||||
else if (depth1 > depth2)
|
||||
return -1;
|
||||
// Their depths are equal (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).
|
||||
for (TransactionOutput output : sortedOutputs) {
|
||||
if (total >= target) break;
|
||||
// Only pick chain-included transactions, or transactions that are ours and pending.
|
||||
TransactionConfidence confidence = output.parentTransaction.getConfidence();
|
||||
|
@ -866,6 +866,23 @@ public class WalletTest {
|
||||
assertFalse(o2.isAvailableForSpending());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ageMattersDuringSelection() throws Exception {
|
||||
// Test that we prefer older coins to newer coins when building spends. This reduces required fees and improves
|
||||
// time to confirmation as the transaction will appear less spammy.
|
||||
final int ITERATIONS = 10;
|
||||
Transaction[] txns = new Transaction[ITERATIONS];
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
txns[i] = sendMoneyToWallet(Utils.toNanoCoins(1, 0), AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
}
|
||||
// Check that we spend transactions in order of reception.
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
Transaction spend = wallet.createSend(new ECKey().toAddress(params), Utils.toNanoCoins(1, 0));
|
||||
assertEquals("Failed on iteration " + i, spend.getInput(0).getOutpoint().getHash(), txns[i].getHash());
|
||||
wallet.commitTx(spend);
|
||||
}
|
||||
}
|
||||
|
||||
// There is a test for spending a coinbase transaction as it matures in BlockChainTest#coinbaseTransactionAvailability
|
||||
|
||||
// Support for offline spending is tested in PeerGroupTest
|
||||
|
Loading…
Reference in New Issue
Block a user