From 9ce89c7da18223882772b150acb3de07691eeb98 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Thu, 22 Jan 2026 01:31:47 +0100 Subject: [PATCH 1/5] Transaction: lower the default minimum transaction fee rate As of Bitcoin Core 29.1 (released Sep 4, 2025), transaction with fee rates as low as 100 sats per vkB are now relayed. --- core/src/main/java/org/bitcoinj/core/Transaction.java | 2 +- core/src/test/java/org/bitcoinj/wallet/WalletTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index bb202313416..ccac91503ec 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -142,7 +142,7 @@ private static int sortableBlockHeight(Transaction tx) { /** * If feePerKb is lower than this, Bitcoin Core will treat it as if there were no fee. */ - public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(1_000); // 0.01 mBTC + public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(100); // per vkB /** * Minimum feerate for defining dust, in sats per kB. diff --git a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java index f403ef97ef9..c4c77fb2af0 100644 --- a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java @@ -2658,8 +2658,8 @@ public void lowerThanDefaultFee() throws InsufficientMoneyException { emptyReq.ensureMinRequiredFee = true; emptyReq.emptyWallet = true; emptyReq.allowUnconfirmed(); - wallet.completeTx(emptyReq); - assertEquals(Coin.valueOf(342), emptyReq.tx.getFee()); // converted to fee rate this is close to min rate + wallet.completeTx(emptyReq); // resulting vsize ~339 + assertEquals(Coin.valueOf(171), emptyReq.tx.getFee()); wallet.commitTx(emptyReq.tx); } From 24b458bea607a51bd01e7fcb097f23fc70b1c59b Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Fri, 23 Jan 2026 09:46:21 +0100 Subject: [PATCH 2/5] ParseByteCacheTest, TransactionTest, SPVBlockStoreTest, WalletProtobufSerializerTest, VersionTallyTest, FilteredBlockAndPartialMerkleTreeTest, PeerTest: in tests which don't assert on calculated fee set fee rate to zero This removes dependencies on the `Transaction.DEFAULT_TX_FEE` constant. --- .../java/org/bitcoinj/core/ParseByteCacheTest.java | 3 ++- .../test/java/org/bitcoinj/core/TransactionTest.java | 2 +- .../java/org/bitcoinj/store/SPVBlockStoreTest.java | 8 ++++---- .../bitcoinj/store/WalletProtobufSerializerTest.java | 4 ++-- .../java/org/bitcoinj/utils/VersionTallyTest.java | 4 ++-- .../core/FilteredBlockAndPartialMerkleTreeTest.java | 2 +- .../src/test/java/org/bitcoinj/core/PeerTest.java | 12 ++++++------ 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/org/bitcoinj/core/ParseByteCacheTest.java b/core/src/test/java/org/bitcoinj/core/ParseByteCacheTest.java index d808f63a2e2..16c94739d96 100644 --- a/core/src/test/java/org/bitcoinj/core/ParseByteCacheTest.java +++ b/core/src/test/java/org/bitcoinj/core/ParseByteCacheTest.java @@ -18,6 +18,7 @@ package org.bitcoinj.core; import org.bitcoinj.base.BitcoinNetwork; +import org.bitcoinj.base.Coin; import org.bitcoinj.base.ScriptType; import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.base.internal.ByteUtils; @@ -71,7 +72,7 @@ private void resetBlockStore() { @Before public void setUp() throws Exception { TimeUtils.setMockClock(); // Use mock clock - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Wallet wallet = Wallet.createDeterministic(BitcoinNetwork.TESTNET, ScriptType.P2PKH); wallet.freshReceiveKey(); diff --git a/core/src/test/java/org/bitcoinj/core/TransactionTest.java b/core/src/test/java/org/bitcoinj/core/TransactionTest.java index c6cfa979323..53dc16eca8e 100644 --- a/core/src/test/java/org/bitcoinj/core/TransactionTest.java +++ b/core/src/test/java/org/bitcoinj/core/TransactionTest.java @@ -622,7 +622,7 @@ public void optInFullRBF() { */ @Test public void testHashForSignatureThreadSafety() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Block genesis = TESTNET.getGenesisBlock(); Block block1 = TestBlocks.createNextBlock(genesis, ECKey.random().toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET), genesis.transaction(0).getOutput(0).getOutPointFor()); diff --git a/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java b/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java index fb973db9b5e..ae89662f880 100644 --- a/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java +++ b/core/src/test/java/org/bitcoinj/store/SPVBlockStoreTest.java @@ -18,6 +18,7 @@ package org.bitcoinj.store; import org.bitcoinj.base.BitcoinNetwork; +import org.bitcoinj.base.Coin; import org.bitcoinj.base.Difficulty; import org.bitcoinj.base.ScriptType; import org.bitcoinj.base.Address; @@ -31,7 +32,6 @@ import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.base.Sha256Hash; import org.bitcoinj.core.StoredBlock; -import org.bitcoinj.core.Transaction; import org.bitcoinj.params.TestNet3Params; import org.junit.Before; import org.junit.BeforeClass; @@ -70,7 +70,7 @@ public void setup() throws Exception { @Test public void basics() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); SPVBlockStore store = new SPVBlockStore(TESTNET, blockStoreFile); Address to = ECKey.random().toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET); @@ -125,7 +125,7 @@ public void twoStores_sequentially_butMismatchingCapacity() throws Exception { @Test public void twoStores_sequentially_grow() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Address to = ECKey.random().toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET); SPVBlockStore store = new SPVBlockStore(TESTNET, blockStoreFile, 10, true); final StoredBlock block0 = store.getChainHead(); @@ -178,7 +178,7 @@ public void performanceTest() throws BlockStoreException { @Test public void clear() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); SPVBlockStore store = new SPVBlockStore(TESTNET, blockStoreFile); // Build a new block. diff --git a/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java b/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java index 9613c1855b0..e542f83e600 100644 --- a/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java +++ b/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java @@ -262,7 +262,7 @@ public void testSequenceNumber() throws Exception { @Test public void testAppearedAtChainHeightDepthAndWorkDone() throws Exception { // Test the TransactionConfidence appearedAtChainHeight, depth and workDone field are stored. - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); BlockChain chain = new BlockChain(BitcoinNetwork.TESTNET, myWallet, new MemoryBlockStore(TESTNET.getGenesisBlock())); final ArrayList txns = new ArrayList<>(2); @@ -377,7 +377,7 @@ public void roundtripVersionTwoTransaction() throws Exception { @Test public void coinbaseTxns() throws Exception { // Covers issue 420 where the outpoint index of a coinbase tx input was being mis-serialized. - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Block b = TestBlocks.createNextBlockWithCoinbase(TESTNET.getGenesisBlock(), Block.BLOCK_VERSION_GENESIS, myKey.getPubKey(), FIFTY_COINS, Block.BLOCK_HEIGHT_GENESIS); Transaction coinbase = b.transaction(0); assertTrue(coinbase.isCoinBase()); diff --git a/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java b/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java index 16ba9a93d28..d3f4c68fba8 100644 --- a/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java +++ b/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java @@ -17,12 +17,12 @@ package org.bitcoinj.utils; import org.bitcoinj.base.BitcoinNetwork; +import org.bitcoinj.base.Coin; import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.core.BlockChain; import org.bitcoinj.core.Context; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.StoredBlock; -import org.bitcoinj.core.Transaction; import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; @@ -107,7 +107,7 @@ public void testVersionCounts() { @Test public void testInitialize() throws BlockStoreException { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); final BlockStore blockStore = new MemoryBlockStore(TESTNET.getGenesisBlock()); final BlockChain chain = new BlockChain(BitcoinNetwork.TESTNET, blockStore); diff --git a/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java b/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java index d5d39c973c2..60878c7b4f6 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java @@ -111,7 +111,7 @@ public void deserializeFilteredBlock() { @Test public void createFilteredBlock() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); ECKey key1 = ECKey.random(); ECKey key2 = ECKey.random(); Transaction tx1 = FakeTxBuilder.createFakeTx(Coin.COIN, key1); diff --git a/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java b/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java index 03eab0d4450..f7397b5fd7b 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java @@ -126,7 +126,7 @@ public void shutdown() throws Exception { @Test public void chainDownloadEnd2End() throws Exception { // A full end-to-end test of the chain download process, with a new block being solved in the middle. - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block; blockChain.add(b1); Block b2 = makeTestBlock(b1); @@ -294,7 +294,7 @@ public void invDownloadTxMultiPeer() throws Exception { // Check that inventory message containing blocks we want is processed correctly. @Test public void newBlock() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block; blockChain.add(b1); final Block b2 = makeTestBlock(b1); @@ -354,7 +354,7 @@ public synchronized void onBlocksDownloaded(Peer p, Block block, @Nullable Filte // Check that it starts downloading the block chain correctly on request. @Test public void startBlockChainDownload() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block; blockChain.add(b1); Block b2 = makeTestBlock(b1); @@ -380,7 +380,7 @@ public void startBlockChainDownload() throws Exception { @Test public void getBlock() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); connect(); Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block; @@ -403,7 +403,7 @@ public void getBlock() throws Exception { @Test public void getLargeBlock() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); connect(); Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block; @@ -429,7 +429,7 @@ public void getLargeBlock() throws Exception { @Test public void fastCatchup() throws Exception { - Context.propagate(new Context(100, Transaction.DEFAULT_TX_FEE, false, true)); + Context.propagate(new Context(100, Coin.ZERO, false, true)); connect(); TimeUtils.setMockClock(); // Check that blocks before the fast catchup point are retrieved using getheaders, and after using getblocks. From 0335452fe04fecaf367b2b7de5854dc372206369 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Fri, 23 Jan 2026 10:01:46 +0100 Subject: [PATCH 3/5] WalletTest: use dedicated fee rate for testing fee calculations This removes dependencies on the `Transaction.DEFAULT_TX_FEE` constant. --- .../java/org/bitcoinj/wallet/WalletTest.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java index c4c77fb2af0..9bfab47a68d 100644 --- a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java @@ -141,6 +141,8 @@ public class WalletTest extends TestWithWallet { private final Address OTHER_ADDRESS = ECKey.random().toAddress(ScriptType.P2PKH, TESTNET); private final Address OTHER_SEGWIT_ADDRESS = ECKey.random().toAddress(ScriptType.P2WPKH, TESTNET); + private static final Coin TEST_TX_FEE_RATE = Coin.valueOf(100_000); // per vkB + @Before @Override public void setUp() throws Exception { @@ -2292,7 +2294,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { for (int i = 0; i < 29; i++) request15.tx.addOutput(CENT, OTHER_ADDRESS); assertTrue(request15.tx.messageSize() > 1000); - request15.feePerKb = Transaction.DEFAULT_TX_FEE; + request15.feePerKb = TEST_TX_FEE_RATE; request15.ensureMinRequiredFee = true; wallet.completeTx(request15); assertEquals(Coin.valueOf(121300), request15.tx.getFee()); @@ -2324,7 +2326,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { for (int i = 0; i < 22; i++) request17.tx.addOutput(CENT, OTHER_ADDRESS); request17.tx.addOutput(new TransactionOutput(request17.tx, CENT, new byte[15])); - request17.feePerKb = Transaction.DEFAULT_TX_FEE; + request17.feePerKb = TEST_TX_FEE_RATE; request17.ensureMinRequiredFee = true; wallet.completeTx(request17); assertEquals(Coin.valueOf(99900), request17.tx.getFee()); @@ -2351,7 +2353,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { for (int i = 0; i < 22; i++) request18.tx.addOutput(CENT, OTHER_ADDRESS); request18.tx.addOutput(new TransactionOutput(request18.tx, CENT, new byte[17])); - request18.feePerKb = Transaction.DEFAULT_TX_FEE; + request18.feePerKb = TEST_TX_FEE_RATE; request18.ensureMinRequiredFee = true; wallet.completeTx(request18); assertEquals(Coin.valueOf(100100), request18.tx.getFee()); @@ -2385,7 +2387,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { // Now reset request19 and give it a fee per kb request19.tx.clearInputs(); request19 = SendRequest.forTx(request19.tx); - request19.feePerKb = Transaction.DEFAULT_TX_FEE; + request19.feePerKb = TEST_TX_FEE_RATE; request19.shuffleOutputs = false; wallet.completeTx(request19); assertEquals(Coin.valueOf(374200), request19.tx.getFee()); @@ -2406,7 +2408,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { // Now reset request19 and give it a fee per kb request20.tx.clearInputs(); request20 = SendRequest.forTx(request20.tx); - request20.feePerKb = Transaction.DEFAULT_TX_FEE; + request20.feePerKb = TEST_TX_FEE_RATE; wallet.completeTx(request20); // 4kb tx. assertEquals(Coin.valueOf(374200), request20.tx.getFee()); @@ -2444,7 +2446,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { // Now reset request25 and give it a fee per kb request25.tx.clearInputs(); request25 = SendRequest.forTx(request25.tx); - request25.feePerKb = Transaction.DEFAULT_TX_FEE; + request25.feePerKb = TEST_TX_FEE_RATE; request25.shuffleOutputs = false; wallet.completeTx(request25); assertEquals(Coin.valueOf(279000), request25.tx.getFee()); @@ -2489,7 +2491,7 @@ public void recipientPaysFees() throws Exception { // Simplest recipientPaysFees use case Coin valueToSend = CENT.divide(2); SendRequest request = SendRequest.to(OTHER_ADDRESS, valueToSend); - request.feePerKb = Transaction.DEFAULT_TX_FEE; + request.feePerKb = TEST_TX_FEE_RATE; request.ensureMinRequiredFee = true; request.recipientsPayFees = true; request.shuffleOutputs = false; @@ -2507,7 +2509,7 @@ public void recipientPaysFees() throws Exception { // Fee is split between the 2 outputs SendRequest request2 = SendRequest.to(OTHER_ADDRESS, valueToSend); request2.tx.addOutput(valueToSend, OTHER_ADDRESS); - request2.feePerKb = Transaction.DEFAULT_TX_FEE; + request2.feePerKb = TEST_TX_FEE_RATE; request2.ensureMinRequiredFee = true; request2.recipientsPayFees = true; request2.shuffleOutputs = false; @@ -2527,7 +2529,7 @@ public void recipientPaysFees() throws Exception { SendRequest request3 = SendRequest.to(OTHER_ADDRESS, valueToSend); request3.tx.addOutput(valueToSend, OTHER_ADDRESS); request3.tx.addOutput(valueToSend, OTHER_ADDRESS); - request3.feePerKb = Transaction.DEFAULT_TX_FEE; + request3.feePerKb = TEST_TX_FEE_RATE; request3.ensureMinRequiredFee = true; request3.recipientsPayFees = true; request3.shuffleOutputs = false; @@ -2548,11 +2550,11 @@ public void recipientPaysFees() throws Exception { // Output when subtracted fee is dust // Hardcoded tx length because actual length may vary depending on actual signature length - Coin fee4 = Transaction.DEFAULT_TX_FEE.multiply(227).divide(1000); + Coin fee4 = TEST_TX_FEE_RATE.multiply(227).divide(1000); Coin dustThreshold = new TransactionOutput(null, Coin.COIN, OTHER_ADDRESS).getMinNonDustValue(); valueToSend = fee4.add(dustThreshold).subtract(SATOSHI); SendRequest request4 = SendRequest.to(OTHER_ADDRESS, valueToSend); - request4.feePerKb = Transaction.DEFAULT_TX_FEE; + request4.feePerKb = TEST_TX_FEE_RATE; request4.ensureMinRequiredFee = true; request4.recipientsPayFees = true; request4.shuffleOutputs = false; @@ -2564,11 +2566,11 @@ public void recipientPaysFees() throws Exception { // Change is dust, so it is incremented to min non dust value. First output value is reduced to compensate. // Hardcoded tx length because actual length may vary depending on actual signature length - Coin fee5 = Transaction.DEFAULT_TX_FEE.multiply(261).divide(1000); + Coin fee5 = TEST_TX_FEE_RATE.multiply(261).divide(1000); valueToSend = COIN.divide(2).subtract(Coin.MICROCOIN); SendRequest request5 = SendRequest.to(OTHER_ADDRESS, valueToSend); request5.tx.addOutput(valueToSend, OTHER_ADDRESS); - request5.feePerKb = Transaction.DEFAULT_TX_FEE; + request5.feePerKb = TEST_TX_FEE_RATE; request5.ensureMinRequiredFee = true; request5.recipientsPayFees = true; request5.shuffleOutputs = false; @@ -2588,12 +2590,12 @@ public void recipientPaysFees() throws Exception { // Change is dust, so it is incremented to min non dust value. First output value is about to be reduced to // compensate, but after subtracting some satoshis, first output is dust. // Hardcoded tx length because actual length may vary depending on actual signature length - Coin fee6 = Transaction.DEFAULT_TX_FEE.multiply(261).divide(1000); + Coin fee6 = TEST_TX_FEE_RATE.multiply(261).divide(1000); Coin valueToSend1 = fee6.divide(2).add(dustThreshold).add(Coin.MICROCOIN); Coin valueToSend2 = COIN.subtract(valueToSend1).subtract(Coin.MICROCOIN.multiply(2)); SendRequest request6 = SendRequest.to(OTHER_ADDRESS, valueToSend1); request6.tx.addOutput(valueToSend2, OTHER_ADDRESS); - request6.feePerKb = Transaction.DEFAULT_TX_FEE; + request6.feePerKb = TEST_TX_FEE_RATE; request6.ensureMinRequiredFee = true; request6.recipientsPayFees = true; request6.shuffleOutputs = false; @@ -2613,7 +2615,7 @@ public void transactionGetFeeTest() throws Exception { // Create a transaction SendRequest request = SendRequest.to(OTHER_ADDRESS, CENT); - request.feePerKb = Transaction.DEFAULT_TX_FEE; + request.feePerKb = TEST_TX_FEE_RATE; wallet.completeTx(request); assertEquals(Coin.valueOf(22700), request.tx.getFee()); } @@ -2630,7 +2632,7 @@ public void witnessTransactionGetFeeTest() throws Exception { // Create a transaction SendRequest request = SendRequest.to(OTHER_SEGWIT_ADDRESS, CENT); - request.feePerKb = Transaction.DEFAULT_TX_FEE; + request.feePerKb = TEST_TX_FEE_RATE; mySegwitWallet.completeTx(request); // Fee test, absolute and per virtual kilobyte @@ -2639,8 +2641,8 @@ public void witnessTransactionGetFeeTest() throws Exception { Coin feePerVkb = fee.multiply(1000).divide(vsize); assertEquals(Coin.valueOf(14100), fee); // due to shorter than expected signature encoding, in rare cases we overpay a little - Coin overpaidFee = Transaction.DEFAULT_TX_FEE.add(valueOf(714)); - assertTrue(feePerVkb.toString(),feePerVkb.equals(Transaction.DEFAULT_TX_FEE) || feePerVkb.equals(overpaidFee)); + Coin overpaidFee = TEST_TX_FEE_RATE.add(valueOf(714)); + assertTrue(feePerVkb.toString(),feePerVkb.equals(TEST_TX_FEE_RATE) || feePerVkb.equals(overpaidFee)); } @Test From 59a82fa3fd6526424da04f37712a8584e4bde1a1 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Fri, 23 Jan 2026 23:16:54 +0100 Subject: [PATCH 4/5] Transaction: rename constant `DEFAULT_TX_FEE_RATE` from `DEFAULT_TX_FEE` Keep old constant as deprecated. Also renames two tests for consistency. --- core/src/main/java/org/bitcoinj/core/Context.java | 2 +- core/src/main/java/org/bitcoinj/core/Transaction.java | 8 ++++++-- core/src/main/java/org/bitcoinj/wallet/Wallet.java | 2 +- core/src/test/java/org/bitcoinj/wallet/WalletTest.java | 8 ++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Context.java b/core/src/main/java/org/bitcoinj/core/Context.java index d3a8822fe8f..439df216114 100644 --- a/core/src/main/java/org/bitcoinj/core/Context.java +++ b/core/src/main/java/org/bitcoinj/core/Context.java @@ -58,7 +58,7 @@ public class Context { * expected to do this yourself. */ public Context() { - this(DEFAULT_EVENT_HORIZON, Transaction.DEFAULT_TX_FEE, true, false); + this(DEFAULT_EVENT_HORIZON, Transaction.DEFAULT_TX_FEE_RATE, true, false); } /** diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index ccac91503ec..94f3a2513f7 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -150,10 +150,14 @@ private static int sortableBlockHeight(Transaction tx) { public static final Coin DUST_RELAY_TX_FEE_RATE = Coin.valueOf(3_000); // per kB /** - * If using this feePerKb, transactions will get confirmed within the next couple of blocks. + * If using this fee rate, transactions will get confirmed within the next couple of blocks. * This should be adjusted from time to time. Last adjustment: February 2017. */ - public static final Coin DEFAULT_TX_FEE = Coin.valueOf(100_000); // 1 mBTC + public static final Coin DEFAULT_TX_FEE_RATE = Coin.valueOf(100_000); // per vkB + + /** @deprecated use {@link #DEFAULT_TX_FEE_RATE} */ + @Deprecated + public static final Coin DEFAULT_TX_FEE = DEFAULT_TX_FEE_RATE; /** * The scale factor for Witness data in Segregated Witness transactions. diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index a10820a7be8..d5fcfe66946 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -5653,7 +5653,7 @@ private Transaction rekeyOneBatch(Instant time, @Nullable AesKey aesKey, List Date: Fri, 23 Jan 2026 23:57:34 +0100 Subject: [PATCH 5/5] Transaction: rename constant `REFERENCE_DEFAULT_MIN_TX_FEE_RATE` from `REFERENCE_DEFAULT_MIN_TX_FEE` Keep old constant as deprecated. --- core/src/main/java/org/bitcoinj/core/Transaction.java | 8 ++++++-- core/src/main/java/org/bitcoinj/wallet/SendRequest.java | 2 +- core/src/main/java/org/bitcoinj/wallet/Wallet.java | 6 +++--- core/src/test/java/org/bitcoinj/wallet/WalletTest.java | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index 94f3a2513f7..b81ad35731b 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -140,9 +140,13 @@ private static int sortableBlockHeight(Transaction tx) { public static final int MAX_STANDARD_TX_SIZE = 100_000; /** - * If feePerKb is lower than this, Bitcoin Core will treat it as if there were no fee. + * If fee rate is lower than this, Bitcoin Core will treat it as if there were no fee. */ - public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(100); // per vkB + public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE_RATE = Coin.valueOf(100); // per vkB + + /** @deprecated use {@link #REFERENCE_DEFAULT_MIN_TX_FEE_RATE} */ + @Deprecated + public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = REFERENCE_DEFAULT_MIN_TX_FEE_RATE; /** * Minimum feerate for defining dust, in sats per kB. diff --git a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java index bc92791dd1f..71d25d5d0b4 100644 --- a/core/src/main/java/org/bitcoinj/wallet/SendRequest.java +++ b/core/src/main/java/org/bitcoinj/wallet/SendRequest.java @@ -93,7 +93,7 @@ public void setFeePerVkb(Coin feePerVkb) { * *

Note that this does not enforce certain fee rules that only apply to transactions which are larger than * 26,000 bytes. If you get a transaction which is that large, you should set a feePerKb of at least - * {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}.

+ * {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE_RATE}.

*/ public boolean ensureMinRequiredFee = Context.get().isEnsureMinRequiredFee(); diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index d5fcfe66946..ff7eaa120f1 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -4552,7 +4552,7 @@ public void signTransaction(SendRequest req) throws BadWalletEncryptionKeyExcept /** * Reduce the value of the first output of a transaction to pay the given feePerKb as appropriate for its size. - * If ensureMinRequiredFee is true, feePerKb is set to at least {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}. + * If ensureMinRequiredFee is true, feePerKb is set to at least {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE_RATE}. * @return true if output is not dust */ private boolean adjustOutputDownwardsForFee(Transaction tx, CoinSelection coinSelection, Coin feePerKb, @@ -5345,8 +5345,8 @@ private void addSuppliedInputs(Transaction tx, List originalIn } private Coin estimateFees(Transaction tx, CoinSelection coinSelection, Coin requestedFeePerKb, boolean ensureMinRequiredFee) { - Coin feePerKb = (ensureMinRequiredFee && requestedFeePerKb.isLessThan(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE)) - ? Transaction.REFERENCE_DEFAULT_MIN_TX_FEE + Coin feePerKb = (ensureMinRequiredFee && requestedFeePerKb.isLessThan(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE_RATE)) + ? Transaction.REFERENCE_DEFAULT_MIN_TX_FEE_RATE : requestedFeePerKb; int vSize = tx.getVsize() + estimateVirtualBytesForSigning(coinSelection.outputs()); return feePerKb.multiply(vSize).divide(1000); diff --git a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java index 0c2b4689568..5316ebb0d38 100644 --- a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java @@ -2314,7 +2314,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { wallet.completeTx(request16); // Just the reference fee should be added if feePerKb == 0 // Hardcoded tx length because actual length may vary depending on actual signature length - assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(1213).divide(1000), request16.tx.getFee()); + assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE_RATE.multiply(1213).divide(1000), request16.tx.getFee()); Transaction spend16 = request16.tx; assertEquals(31, spend16.getOutputs().size()); // We optimize for priority, so the output selected should be the largest one @@ -2428,7 +2428,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { // If we send without a feePerKb, we should still require REFERENCE_DEFAULT_MIN_TX_FEE because we have an output < 0.01 wallet.completeTx(request21); // Hardcoded tx length because actual length may vary depending on actual signature length - assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(3776).divide(1000), request21.tx.getFee()); + assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE_RATE.multiply(3776).divide(1000), request21.tx.getFee()); assertEquals(2, request21.tx.getInputs().size()); assertEquals(COIN, request21.tx.getInput(0).getValue()); assertEquals(CENT, request21.tx.getInput(1).getValue()); @@ -2468,7 +2468,7 @@ public void feeSolverAndCoinSelectionTests2() throws Exception { for (int i = 0; i < 98; i++) request26.tx.addOutput(CENT, OTHER_ADDRESS); // Hardcoded tx length because actual length may vary depending on actual signature length - Coin fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(3560).divide(1000); + Coin fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE_RATE.multiply(3560).divide(1000); Coin dustThresholdMinusOne = new TransactionOutput(null, Coin.COIN, OTHER_ADDRESS).getMinNonDustValue().subtract(SATOSHI); request26.tx.addOutput(CENT.subtract(fee.add(dustThresholdMinusOne)), OTHER_ADDRESS);