mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +00:00
Change getBalance to use the default coin selector instead of its own logic for calculating the available and estimated balances.
This commit is contained in:
parent
29727113a5
commit
757334da80
@ -256,7 +256,7 @@ public class Wallet implements Serializable, BlockChainListener {
|
|||||||
* see loadFromFile.
|
* see loadFromFile.
|
||||||
*/
|
*/
|
||||||
public Wallet(NetworkParameters params) {
|
public Wallet(NetworkParameters params) {
|
||||||
this.params = params;
|
this.params = checkNotNull(params);
|
||||||
keychain = new ArrayList<ECKey>();
|
keychain = new ArrayList<ECKey>();
|
||||||
unspent = new HashMap<Sha256Hash, Transaction>();
|
unspent = new HashMap<Sha256Hash, Transaction>();
|
||||||
spent = new HashMap<Sha256Hash, Transaction>();
|
spent = new HashMap<Sha256Hash, Transaction>();
|
||||||
@ -1681,16 +1681,7 @@ public class Wallet implements Serializable, BlockChainListener {
|
|||||||
// change - it could be pre-calculated and held in RAM, and this is probably an optimization worth doing.
|
// change - it could be pre-calculated and held in RAM, and this is probably an optimization worth doing.
|
||||||
// Note that output.isMine(this) needs to test the keychain which is currently an array, so it's
|
// Note that output.isMine(this) needs to test the keychain which is currently an array, so it's
|
||||||
// O(candidate outputs ^ keychain.size())! There's lots of low hanging fruit here.
|
// O(candidate outputs ^ keychain.size())! There's lots of low hanging fruit here.
|
||||||
LinkedList<TransactionOutput> candidates = Lists.newLinkedList();
|
LinkedList<TransactionOutput> candidates = calculateSpendCandidates(true);
|
||||||
for (Transaction tx : Iterables.concat(unspent.values(), pending.values())) {
|
|
||||||
// Do not try and spend coinbases that were mined too recently, the protocol forbids it.
|
|
||||||
if (!tx.isMature()) continue;
|
|
||||||
for (TransactionOutput output : tx.getOutputs()) {
|
|
||||||
if (!output.isAvailableForSpending()) continue;
|
|
||||||
if (!output.isMine(this)) continue;
|
|
||||||
candidates.add(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Of the coins we could spend, pick some that we actually will spend.
|
// Of the coins we could spend, pick some that we actually will spend.
|
||||||
CoinSelection selection = coinSelector.select(value, candidates);
|
CoinSelection selection = coinSelector.select(value, candidates);
|
||||||
// Can we afford this?
|
// Can we afford this?
|
||||||
@ -1733,6 +1724,20 @@ public class Wallet implements Serializable, BlockChainListener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LinkedList<TransactionOutput> calculateSpendCandidates(boolean excludeImmatureCoinbases) {
|
||||||
|
LinkedList<TransactionOutput> candidates = Lists.newLinkedList();
|
||||||
|
for (Transaction tx : Iterables.concat(unspent.values(), pending.values())) {
|
||||||
|
// Do not try and spend coinbases that were mined too recently, the protocol forbids it.
|
||||||
|
if (excludeImmatureCoinbases && !tx.isMature()) continue;
|
||||||
|
for (TransactionOutput output : tx.getOutputs()) {
|
||||||
|
if (!output.isAvailableForSpending()) continue;
|
||||||
|
if (!output.isMine(this)) continue;
|
||||||
|
candidates.add(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized Address getChangeAddress() {
|
synchronized Address getChangeAddress() {
|
||||||
// For now let's just pick the first key in our keychain. In future we might want to do something else to
|
// For now let's just pick the first key in our keychain. In future we might want to do something else to
|
||||||
// give the user better privacy here, eg in incognito mode.
|
// give the user better privacy here, eg in incognito mode.
|
||||||
@ -1823,35 +1828,32 @@ public class Wallet implements Serializable, BlockChainListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It's possible to calculate a wallets balance from multiple points of view. This enum selects which
|
* <p>It's possible to calculate a wallets balance from multiple points of view. This enum selects which
|
||||||
* getBalance() should use.<p>
|
* getBalance() should use.</p>
|
||||||
* <p/>
|
*
|
||||||
* Consider a real-world example: you buy a snack costing $5 but you only have a $10 bill. At the start you have
|
* <p>Consider a real-world example: you buy a snack costing $5 but you only have a $10 bill. At the start you have
|
||||||
* $10 viewed from every possible angle. After you order the snack you hand over your $10 bill. From the
|
* $10 viewed from every possible angle. After you order the snack you hand over your $10 bill. From the
|
||||||
* perspective of your wallet you have zero dollars (AVAILABLE). But you know in a few seconds the shopkeeper
|
* perspective of your wallet you have zero dollars (AVAILABLE). But you know in a few seconds the shopkeeper
|
||||||
* will give you back $5 change so most people in practice would say they have $5 (ESTIMATED).<p>
|
* will give you back $5 change so most people in practice would say they have $5 (ESTIMATED).</p>
|
||||||
*/
|
*/
|
||||||
public enum BalanceType {
|
public enum BalanceType {
|
||||||
/**
|
/**
|
||||||
* Balance calculated assuming all pending transactions are in fact included into the best chain by miners.
|
* Balance calculated assuming all pending transactions are in fact included into the best chain by miners.
|
||||||
* This is the right balance to show in user interfaces.
|
* This includes the value of immature coinbase transactions.
|
||||||
*/
|
*/
|
||||||
ESTIMATED,
|
ESTIMATED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Balance that can be safely used to create new spends. This is all confirmed unspent outputs minus the ones
|
* Balance that can be safely used to create new spends. This is whatever the default coin selector would
|
||||||
* spent by pending transactions, but not including the outputs of those pending transactions.
|
* make available, which by default means transaction outputs with at least 1 confirmation and pending
|
||||||
|
* transactions created by our own wallet which have been propagated across the network.
|
||||||
*/
|
*/
|
||||||
AVAILABLE
|
AVAILABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AVAILABLE balance of this wallet. See {@link BalanceType#AVAILABLE} for details on what this
|
* Returns the AVAILABLE balance of this wallet. See {@link BalanceType#AVAILABLE} for details on what this
|
||||||
* means.<p>
|
* means.
|
||||||
* <p/>
|
|
||||||
* Note: the estimated balance is usually the one you want to show to the end user - however attempting to
|
|
||||||
* actually spend these coins may result in temporary failure. This method returns how much you can safely
|
|
||||||
* provide to {@link Wallet#createSend(Address, java.math.BigInteger)}.
|
|
||||||
*/
|
*/
|
||||||
public synchronized BigInteger getBalance() {
|
public synchronized BigInteger getBalance() {
|
||||||
return getBalance(BalanceType.AVAILABLE);
|
return getBalance(BalanceType.AVAILABLE);
|
||||||
@ -1861,31 +1863,27 @@ public class Wallet implements Serializable, BlockChainListener {
|
|||||||
* Returns the balance of this wallet as calculated by the provided balanceType.
|
* Returns the balance of this wallet as calculated by the provided balanceType.
|
||||||
*/
|
*/
|
||||||
public synchronized BigInteger getBalance(BalanceType balanceType) {
|
public synchronized BigInteger getBalance(BalanceType balanceType) {
|
||||||
BigInteger available = BigInteger.ZERO;
|
if (balanceType == BalanceType.AVAILABLE) {
|
||||||
for (Transaction tx : unspent.values()) {
|
return getBalance(coinSelector);
|
||||||
// For an 'available to spend' balance exclude coinbase transactions that have not yet matured.
|
} else if (balanceType == BalanceType.ESTIMATED) {
|
||||||
if (balanceType == BalanceType.AVAILABLE && !tx.isMature()) {
|
LinkedList<TransactionOutput> all = calculateSpendCandidates(false);
|
||||||
continue;
|
BigInteger value = BigInteger.ZERO;
|
||||||
}
|
for (TransactionOutput out : all) value = value.add(out.getValue());
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Unknown balance type"); // Unreachable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (TransactionOutput output : tx.getOutputs()) {
|
/**
|
||||||
if (!output.isMine(this)) continue;
|
* Returns the balance that would be considered spendable by the given coin selector. Just asks it to select
|
||||||
if (!output.isAvailableForSpending()) continue;
|
* as many coins as possible and returns the total.
|
||||||
available = available.add(output.getValue());
|
*/
|
||||||
}
|
public synchronized BigInteger getBalance(CoinSelector selector) {
|
||||||
}
|
checkNotNull(selector);
|
||||||
if (balanceType == BalanceType.AVAILABLE)
|
LinkedList<TransactionOutput> candidates = calculateSpendCandidates(true);
|
||||||
return available;
|
CoinSelection selection = selector.select(params.MAX_MONEY, candidates);
|
||||||
checkState(balanceType == BalanceType.ESTIMATED);
|
return selection.valueGathered;
|
||||||
// Now add back all the pending outputs to assume the transaction goes through.
|
|
||||||
BigInteger estimated = available;
|
|
||||||
for (Transaction tx : pending.values()) {
|
|
||||||
for (TransactionOutput output : tx.getOutputs()) {
|
|
||||||
if (!output.isMine(this)) continue;
|
|
||||||
estimated = estimated.add(output.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return estimated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -2408,6 +2406,11 @@ public class Wallet implements Serializable, BlockChainListener {
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the {@link CoinSelector} object which controls which outputs can be spent by this wallet. */
|
||||||
|
public synchronized CoinSelector getCoinSelector() {
|
||||||
|
return coinSelector;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A coin selector is responsible for choosing which outputs to spend when creating transactions. The default
|
* A coin selector is responsible for choosing which outputs to spend when creating transactions. The default
|
||||||
* selector implements a policy of spending transactions that appeared in the best chain and pending transactions
|
* selector implements a policy of spending transactions that appeared in the best chain and pending transactions
|
||||||
|
@ -139,7 +139,7 @@ public class WalletTest {
|
|||||||
assertEquals(1, txns.size());
|
assertEquals(1, txns.size());
|
||||||
|
|
||||||
// Now check that we can spend the unconfirmed change.
|
// Now check that we can spend the unconfirmed change.
|
||||||
assertEquals(v3, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
assertEquals(v3, wallet.getBalance());
|
||||||
Transaction t3 = wallet.createSend(new ECKey().toAddress(params), v3);
|
Transaction t3 = wallet.createSend(new ECKey().toAddress(params), v3);
|
||||||
assertNotNull(t3);
|
assertNotNull(t3);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user