DefaultCoinSelector: require network for constructor

This is so that the special handling for regtest network can work in future.
This commit is contained in:
Andreas Schildbach 2023-04-01 22:48:04 +02:00
parent 472af481cc
commit c60c612107
3 changed files with 32 additions and 15 deletions

View File

@ -19,6 +19,7 @@ package org.bitcoinj.wallet;
import com.google.common.annotations.VisibleForTesting;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.Coin;
import org.bitcoinj.base.Network;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput;
@ -34,7 +35,14 @@ import java.util.List;
* "spending" more priority than would be required to get the transaction we are creating confirmed.
*/
public class DefaultCoinSelector implements CoinSelector {
private final Network network;
protected DefaultCoinSelector() {
this.network = null;
}
private DefaultCoinSelector(Network network) {
this.network = network;
}
@Override
@ -45,7 +53,6 @@ public class DefaultCoinSelector implements CoinSelector {
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<>(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.
// TODO: Take in network parameters when instantiated, and then test against the current network. Or just have a boolean parameter for "give me everything"
if (!target.equals(BitcoinNetwork.MAX_MONEY)) {
sortOutputs(sortedOutputs);
}
@ -87,12 +94,20 @@ public class DefaultCoinSelector implements CoinSelector {
/** Sub-classes can override this to just customize whether transactions are usable, but keep age sorting. */
protected boolean shouldSelect(Transaction tx) {
if (tx != null) {
return isSelectable(tx);
return isSelectable(tx, network);
}
return true;
}
public static boolean isSelectable(Transaction tx) {
/**
* Helper to determine if this selector would select a given transaction. Note that in a regtest network outgoing
* payments will likely not see propagation, so there is a special exception.
*
* @param tx transaction to determine if it would be selected
* @param network network the transaction is on
* @return true if it would be selected, false otherwise
*/
public static boolean isSelectable(Transaction tx, Network network) {
// Only pick chain-included transactions, or transactions that are ours and pending.
TransactionConfidence confidence = tx.getConfidence();
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
@ -101,10 +116,10 @@ public class DefaultCoinSelector implements CoinSelector {
type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
confidence.getSource().equals(TransactionConfidence.Source.SELF) &&
// In regtest mode we expect to have only one peer, so we won't see transactions propagate.
(confidence.numBroadcastPeers() > 0 || tx.getParams().network() == BitcoinNetwork.REGTEST);
(confidence.numBroadcastPeers() > 0 || network == BitcoinNetwork.REGTEST);
}
public static CoinSelector get() {
return new DefaultCoinSelector();
public static CoinSelector get(Network network) {
return new DefaultCoinSelector(network);
}
}

View File

@ -294,7 +294,7 @@ public class Wallet extends BaseTaggableObject
@Nullable
private volatile Instant vKeyRotationTime = null;
protected final CoinSelector coinSelector = DefaultCoinSelector.get();
protected final CoinSelector coinSelector;
// The wallet version. This is an int that can be used to track breaking changes in the wallet format.
// You can also use it to detect wallets that come from the future (ie they contain features you
@ -511,6 +511,7 @@ public class Wallet extends BaseTaggableObject
*/
public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) {
this.params = Objects.requireNonNull(params);
this.coinSelector = DefaultCoinSelector.get(params.network());
this.keyChainGroup = Objects.requireNonNull(keyChainGroup);
watchedScripts = new HashSet<>();
unspent = new HashMap<>();

View File

@ -16,6 +16,7 @@
package org.bitcoinj.wallet;
import org.bitcoinj.base.BitcoinNetwork;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.core.AbstractBlockChain;
import org.bitcoinj.core.Block;
@ -65,20 +66,20 @@ public class DefaultCoinSelectorTest extends TestWithWallet {
Transaction t;
t = new Transaction(TESTNET);
t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING);
assertFalse(DefaultCoinSelector.isSelectable(t));
assertFalse(DefaultCoinSelector.isSelectable(t, BitcoinNetwork.TESTNET));
t.getConfidence().setSource(TransactionConfidence.Source.SELF);
assertFalse(DefaultCoinSelector.isSelectable(t));
assertFalse(DefaultCoinSelector.isSelectable(t, BitcoinNetwork.TESTNET));
t.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("1.2.3.4"), TESTNET.getPort()));
assertTrue(DefaultCoinSelector.isSelectable(t));
assertTrue(DefaultCoinSelector.isSelectable(t, BitcoinNetwork.TESTNET));
t.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByName("5.6.7.8"), TESTNET.getPort()));
assertTrue(DefaultCoinSelector.isSelectable(t));
assertTrue(DefaultCoinSelector.isSelectable(t, BitcoinNetwork.TESTNET));
t = new Transaction(TESTNET);
t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.BUILDING);
assertTrue(DefaultCoinSelector.isSelectable(t));
assertTrue(DefaultCoinSelector.isSelectable(t, BitcoinNetwork.TESTNET));
t = new Transaction(REGTEST);
t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.PENDING);
t.getConfidence().setSource(TransactionConfidence.Source.SELF);
assertTrue(DefaultCoinSelector.isSelectable(t));
assertTrue(DefaultCoinSelector.isSelectable(t, BitcoinNetwork.REGTEST));
}
@Test
@ -88,7 +89,7 @@ public class DefaultCoinSelectorTest extends TestWithWallet {
Transaction t2 = Objects.requireNonNull(sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN));
// Check we selected just the oldest one.
CoinSelector selector = DefaultCoinSelector.get();
CoinSelector selector = wallet.getCoinSelector();
CoinSelection selection = selector.select(COIN, wallet.calculateAllSpendCandidates());
assertTrue(selection.outputs().contains(t1.getOutputs().get(0)));
assertEquals(COIN, selection.totalValue());
@ -136,7 +137,7 @@ public class DefaultCoinSelectorTest extends TestWithWallet {
);
t.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.BUILDING);
CoinSelector selector = DefaultCoinSelector.get();
CoinSelector selector = DefaultCoinSelector.get(BitcoinNetwork.TESTNET);
CoinSelection selection = selector.select(COIN.multiply(2), outputs);
assertTrue(selection.outputs().size() == 4);