diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_MINER_H #define BITCOIN_MINER_H +#include "policy/policy.h" #include "primitives/block.h" #include "txmempool.h" @@ -134,7 +135,7 @@ // Configuration parameters for the block size uint64_t nMaxGeneratedBlockSize; - CFeeRate blockMinFeeRate; + CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); // Information on the current status of the block uint64_t nBlockSize; @@ -161,6 +162,7 @@ CreateNewBlock(const CScript &scriptPubKeyIn); uint64_t GetMaxGeneratedBlockSize() const { return nMaxGeneratedBlockSize; } + void SetBlockMinTxFee(const Amount blockmintxfee); private: // utility functions diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -80,6 +80,10 @@ return nNewTime - nOldTime; } +void BlockAssembler::SetBlockMinTxFee(const Amount blockmintxfee) { + blockMinFeeRate = CFeeRate(blockmintxfee); +} + static uint64_t ComputeMaxGeneratedBlockSize(const Config &config, const CBlockIndex *pindexPrev) { // Block resource limits @@ -106,9 +110,7 @@ if (IsArgSet("-blockmintxfee")) { Amount n(0); ParseMoney(GetArg("-blockmintxfee", ""), n); - blockMinFeeRate = CFeeRate(n); - } else { - blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + SetBlockMinTxFee(n); } LOCK(cs_main); diff --git a/src/policy/policy.h b/src/policy/policy.h --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -22,7 +22,7 @@ static const uint64_t DEFAULT_BLOCK_PRIORITY_PERCENTAGE = 5; /** Default for -blockmintxfee, which sets the minimum feerate for a transaction * in blocks created by mining code **/ -static const Amount DEFAULT_BLOCK_MIN_TX_FEE(1000); +static const Amount DEFAULT_BLOCK_MIN_TX_FEE(0); /** The maximum size for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** Maximum number of signature check operations in an IsStandard() P2SH script diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1113,7 +1113,7 @@ CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const uint256 &txid = tx->GetId(); - bool fLimitFree = false; + bool fLimitFree = true; Amount nMaxRawTxFee = maxTxFee; if (request.params.size() > 1 && request.params[1].get_bool()) { nMaxRawTxFee = Amount(0); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "miner.h" - #include "chainparams.h" #include "coins.h" #include "config.h" @@ -86,12 +85,40 @@ GlobalConfig config; + // test 0 fee transaction is included in a block with + // default configuration. + blockMinFeeRate = CFeeRate(Amount(0)); + + // Test that a medium fee transaction will be selected after a higher fee + // rate package with a low fee rate parent. + CMutableTransaction tx; + tx.vin.resize(1); + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vin[0].prevout.hash = txFirst[0]->GetId(); + tx.vin[0].prevout.n = 0; + tx.vout.resize(1); + tx.vout[0].nValue = Amount(5000000000LL); + // This tx has a low fee: 1000 satoshis. + // Save this txid for later use. + uint256 zeroFeeTx = tx.GetId(); + mempool.addUnchecked( + zeroFeeTx, + entry.Fee(Amount(0)).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + + std::unique_ptr pblocktemplate = + BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); + BOOST_CHECK(pblocktemplate->block.vtx[1]->GetId() == zeroFeeTx); + // these 3 tests assume blockprioritypercentage is 0. config.SetBlockPriorityPercentage(0); + // the following tests assume that minimum fee rate is 1000 satoshi per kB. + BlockAssembler ba(config, chainparams); + ba.SetBlockMinTxFee(Amount(1000)); + blockMinFeeRate = CFeeRate(Amount(1000)); + // Test that a medium fee transaction will be selected after a higher fee // rate package with a low fee rate parent. - CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].prevout.hash = txFirst[0]->GetId(); @@ -125,8 +152,7 @@ .SpendsCoinbase(false) .FromTx(tx)); - std::unique_ptr pblocktemplate = - BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = ba.CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetId() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetId() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetId() == hashMediumFeeTx); @@ -147,8 +173,7 @@ tx.vout[0].nValue = Amount(5000000000LL - 1000 - 50000) - feeToUse; uint256 hashLowFeeTx = tx.GetId(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = ba.CreateNewBlock(scriptPubKey); // Verify that the free tx and the low fee tx didn't get selected. for (size_t i = 0; i < pblocktemplate->block.vtx.size(); ++i) { BOOST_CHECK(pblocktemplate->block.vtx[i]->GetId() != hashFreeTx); @@ -164,8 +189,7 @@ hashLowFeeTx = tx.GetId(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse + Amount(2)).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = ba.CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetId() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetId() == hashLowFeeTx); @@ -189,8 +213,7 @@ uint256 hashLowFeeTx2 = tx.GetId(); mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = ba.CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. for (size_t i = 0; i < pblocktemplate->block.vtx.size(); ++i) { @@ -204,8 +227,7 @@ // 10k satoshi fee. tx.vout[0].nValue = Amount(100000000 - 10000); mempool.addUnchecked(tx.GetId(), entry.Fee(Amount(10000)).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = ba.CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetId() == hashLowFeeTx2); } diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -56,7 +56,7 @@ /** Default for DEFAULT_WHITELISTFORCERELAY. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ -static const Amount DEFAULT_MIN_RELAY_TX_FEE(1000); +static const Amount DEFAULT_MIN_RELAY_TX_FEE(0); //! -maxtxfee default static const Amount DEFAULT_TRANSACTION_MAXFEE(COIN / 10); //! Discourage users to set fees higher than this amount (in satoshis) per kB @@ -139,9 +139,12 @@ /** Additional block download timeout per parallel downloading peer (i.e. 5 min) */ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000; - -static const unsigned int DEFAULT_LIMITFREERELAY = 0; -static const bool DEFAULT_RELAYPRIORITY = true; +/** Continuously rate-limit free/priority transactions to + * limiterfreerelay*1000 bytes per minute. */ +static const unsigned int DEFAULT_LIMITFREERELAY = 20; +// Do *not* Require that free transactions have sufficient priority +// to be relayed (hence mined in the next block). +static const bool DEFAULT_RELAYPRIORITY = false; static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; /** Maximum age of our tip in seconds for us to be considered current for fee * estimation */ diff --git a/test/functional/.gitignore b/test/functional/.gitignore --- a/test/functional/.gitignore +++ b/test/functional/.gitignore @@ -1,2 +1,3 @@ *.pyc cache +config.ini diff --git a/test/functional/relay_zero_fee_transaction.py b/test/functional/relay_zero_fee_transaction.py new file mode 100755 --- /dev/null +++ b/test/functional/relay_zero_fee_transaction.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test HighPriorityTransaction code +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from datetime import datetime + + +class RelayZeroFeeTransactionTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.is_network_split = False + self.num_nodes = 2 + # limitfreerelay = X means that we can't relay more than X * 1000 bytes of 0 fee txns per minute + # but for some reasons involving a 10 minutes exponentially decay window the limit is X * 10 * 1000 + # see validation.cpp#L917 + self.extra_args = [ + ["-limitfreerelay=1", "-minrelaytxfee=0.0001"], + ["-limitfreerelay=1", "-minrelaytxfee=0.0001"] + ] + + def setup_network(self): + self.setup_nodes() + connect_nodes(self.nodes[0], 1) + self.sync_all() + + def create_transactions(self, node, utxos, num, fee): + addr = node.getnewaddress() + txids = [] + for _ in range(num): + t = utxos.pop() + inputs = [{"txid": t["txid"], "vout": t["vout"]}] + outputs = {} + change = t['amount'] - fee + outputs[addr] = satoshi_round(change) + rawtx = node.createrawtransaction(inputs, outputs) + signresult = node.signrawtransaction( + rawtx, None, None, "NONE|FORKID") + txid = node.sendrawtransaction(signresult["hex"], True) + txids.append(txid) + return txids + + def generate_zerofee_transactions(self, node, count, fee): + # be sure to make this utxo aged enough + # and with enough fees + txns = create_confirmed_utxos( + Decimal('0.00015'), node, count) + txids = [] + + # Create txns_count number of txns with 0 fee + range_size = [0, count] + start_range = range_size[0] + end_range = range_size[1] + txids = self.create_transactions( + node, txns[start_range:end_range], end_range - start_range, fee) + return txids + + def run_test(self): + # self.relayfee0 = self.nodes[0].getnetworkinfo()['relayfee'] + # self.relayfee1 = self.nodes[1].getnetworkinfo()['relayfee'] + # self.log.info("minRelayTxFee node0 set to: %s" % str(self.relayfee0)) + # self.log.info("minRelayTxFee node1 set to: %s" % str(self.relayfee1)) + # self.log.info("Fee set to: %d" % self.relayfee0) + + # first test: verify that 0 fee transactions generated locally are rate + # rate limited. So we are going to generate 200 simple txns (~35K) in a very + # short period. We should get rate limited at around 10K + try: + txids = self.generate_zerofee_transactions(self.nodes[0], 200, 0) + except JSONRPCException as e: + # mempool should be around 10K (using 2% tolerance) + self.log.info("Error message is: %s" % e.error['message']) + self.log.info("mem pool size is: %d" % + self.nodes[0].getmempoolinfo()['bytes']) + assert(self.nodes[0].getmempoolinfo()['bytes'] <= 10200) + + # 2nd test: verify that only 10K 0 fee txns are realyed from + # node0 to node1 if minrelayfee is set to 0.0001 and limiterfreerelay is set to 1 + # (the mempool size is about 35K) + stop_nodes(self.nodes) + os.remove(self.options.tmpdir + "/node0/regtest/mempool.dat") + os.remove(self.options.tmpdir + "/node1/regtest/mempool.dat") + self.extra_args = [ + ["-limitfreerelay=10", "-minrelaytxfee=0.0001"], + ["-limitfreerelay=1", "-minrelaytxfee=0.0001"] + ] + self.nodes = start_nodes( + self.num_nodes, self.options.tmpdir, self.extra_args) + connect_nodes_bi(self.nodes, 0, 1) + + txids = self.generate_zerofee_transactions(self.nodes[0], 200, 0) + mempool_size_pre = self.nodes[0].getmempoolinfo()['bytes'] + self.log.info("Current mempool size n bytes is: %d" % + mempool_size_pre) + + try: + # wait for 1 minute for the sync to happen + # node1 mempool should be lower than 10K + sync_mempools(self.nodes, wait=1, timeout=60) + except AssertionError: + self.log.debug("memp 0 %d, memp 1 %d" % + (self.nodes[0].getmempoolinfo()['bytes'], self.nodes[1].getmempoolinfo()['bytes'])) + self.log.info( + "Assert that only 10K worth of zero fee transactions will be relayed") + assert(self.nodes[1].getmempoolinfo()['bytes'] <= 10200) + +if __name__ == '__main__': + RelayZeroFeeTransactionTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -48,6 +48,7 @@ 'wallet-accounts.py', 'wallet-dump.py', 'listtransactions.py', + 'relay_zero_fee_transaction.py', # vv Tests less than 60s vv 'sendheaders.py', 'zapwallettxes.py',