From 27fae3484cdb21b0d24face833b966fce5926be5 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Wed, 2 Dec 2015 09:37:18 -0500 Subject: [PATCH] Use fee deltas for determining mempool acceptance --- qa/rpc-tests/prioritise_transaction.py | 40 ++++++++++++++++++++++++++ src/main.cpp | 18 +++++++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py index f376ceee5..d9492f27a 100755 --- a/qa/rpc-tests/prioritise_transaction.py +++ b/qa/rpc-tests/prioritise_transaction.py @@ -143,5 +143,45 @@ class PrioritiseTransactionTest(BitcoinTestFramework): if (x != high_fee_tx): assert(x not in mempool) + # Create a free, low priority transaction. Should be rejected. + utxo_list = self.nodes[0].listunspent() + assert(len(utxo_list) > 0) + utxo = utxo_list[0] + + inputs = [] + outputs = {} + inputs.append({"txid" : utxo["txid"], "vout" : utxo["vout"]}) + outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee + raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) + tx_hex = self.nodes[0].signrawtransaction(raw_tx)["hex"] + txid = self.nodes[0].sendrawtransaction(tx_hex) + + # A tx that spends an in-mempool tx has 0 priority, so we can use it to + # test the effect of using prioritise transaction for mempool acceptance + inputs = [] + inputs.append({"txid": txid, "vout": 0}) + outputs = {} + outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee + raw_tx2 = self.nodes[0].createrawtransaction(inputs, outputs) + tx2_hex = self.nodes[0].signrawtransaction(raw_tx2)["hex"] + tx2_id = self.nodes[0].decoderawtransaction(tx2_hex)["txid"] + + try: + self.nodes[0].sendrawtransaction(tx2_hex) + except JSONRPCException as exp: + assert_equal(exp.error['code'], -26) # insufficient fee + assert(tx2_id not in self.nodes[0].getrawmempool()) + else: + assert(False) + + # This is a less than 1000-byte transaction, so just set the fee + # to be the minimum for a 1000 byte transaction and check that it is + # accepted. + self.nodes[0].prioritisetransaction(tx2_id, 0, int(self.relayfee*COIN)) + + print "Assert that prioritised free transaction is accepted to mempool" + assert_equal(self.nodes[0].sendrawtransaction(tx2_hex), tx2_id) + assert(tx2_id in self.nodes[0].getrawmempool()) + if __name__ == '__main__': PrioritiseTransactionTest().main() diff --git a/src/main.cpp b/src/main.cpp index 23df8ca68..12642f319 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -968,6 +968,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; + // nModifiedFees includes any fee deltas from PrioritiseTransaction + CAmount nModifiedFees = nFees; + double nPriorityDummy = 0; + pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees); + CAmount inChainInputValue; double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); @@ -987,14 +992,17 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Don't accept it if it can't get into a block CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); + + // txMinFee takes into account priority/fee deltas, so compare using + // nFees rather than nModifiedFees if (fLimitFree && nFees < txMinFee) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("%d < %d", nFees, txMinFee)); CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { + if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); - } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { + } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { // Require that free transactions have sufficient priority to be mined in the next block. return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } @@ -1002,7 +1010,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) + if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { static CCriticalSection csFreeLimiter; static double dFreeCount; @@ -1061,10 +1069,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C uint64_t nConflictingCount = 0; CTxMemPool::setEntries allConflicting; - CAmount nModifiedFees = nFees; - double nPriorityDummy = 0; - pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees); - // If we don't hold the lock allConflicting might be incomplete; the // subsequent RemoveStaged() and addUnchecked() calls don't guarantee // mempool consistency for us.