diff --git a/qa/rpc-tests/getrawmempool_priority.py b/qa/rpc-tests/getrawmempool_priority.py new file mode 100755 index 0000000..1adf61d --- /dev/null +++ b/qa/rpc-tests/getrawmempool_priority.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python2 +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.blocktools import create_block, create_coinbase +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +from binascii import a2b_hex, b2a_hex + +def find_unspent(node, txid, amount): + for utxo in node.listunspent(0): + if utxo['txid'] != txid: continue + if float(utxo['amount']) != amount: continue + return {'txid': utxo['txid'], 'vout': utxo['vout']} + +def solve_template_hex(tmpl, txlist): + block = create_block(tmpl=tmpl, txlist=txlist) + block.solve() + b = block.serialize() + x = b2a_hex(b).decode('ascii') + return x + +def get_modified_size(node, txdata): + decoded = node.decoderawtransaction(txdata) + size = len(txdata) // 2 + for inp in decoded['vin']: + offset = 41 + min(len(inp['scriptSig']['hex']) // 2, 110) + if offset <= size: + size -= offset + return size + +def assert_approximate(a, b): + assert_equal(int(a), int(b)) + +class PriorityTest(BitcoinTestFramework): + + def __init__(self): + self.num_nodes = 2 + + def setup_nodes(self): + nodes = [] + nodes.append(start_node(0, self.options.tmpdir, ['-blockmaxsize=0'])) + nodes.append(start_node(1, self.options.tmpdir, ['-blockminsize=1000000', '-blockmaxsize=1000000'])) + return nodes + + def setup_network(self, split=False): + self.nodes = self.setup_nodes() + connect_nodes_bi(self.nodes,0,1) + self.is_network_split = False + self.sync_all() + + def run_test(self): + node = self.nodes[0] + miner = self.nodes[1] + node.generate(50) + miner.generate(101) + self.sync_all() + + fee = 0.01 + amt = 11 + + txid_a = node.sendtoaddress(node.getnewaddress(), amt) + txdata_b = node.createrawtransaction([find_unspent(node, txid_a, amt)], {node.getnewaddress(): amt - fee}) + txdata_b = node.signrawtransaction(txdata_b)['hex'] + txmodsize_b = get_modified_size(node, txdata_b) + txid_b = node.sendrawtransaction(txdata_b) + + txid_b_mempoolentry = node.getrawmempool(True)[txid_b] + assert_approximate(txid_b_mempoolentry['startingpriority'], txid_b_mempoolentry['currentpriority']) + assert_approximate(txid_b_mempoolentry['currentpriority'], 0) + + # Mine only the sendtoaddress transaction + tmpl = node.getblocktemplate() + rawblock = solve_template_hex(tmpl, [create_coinbase(tmpl['height']), a2b_hex(node.getrawtransaction(txid_a))]) + assert_equal(node.submitblock(rawblock), None) + + txid_b_mempoolentry = node.getrawmempool(True)[txid_b] + assert_approximate(txid_b_mempoolentry['startingpriority'], 0) + assert_approximate(txid_b_mempoolentry['currentpriority'], amt * 1e8 / txmodsize_b) + + node.generate(2) + + txid_b_mempoolentry = node.getrawmempool(True)[txid_b] + assert_approximate(txid_b_mempoolentry['startingpriority'], 0) + assert_approximate(txid_b_mempoolentry['currentpriority'], amt * 1e8 * 3 / txmodsize_b) + + self.sync_all() + miner.generate(4) + self.sync_all() + + txdata_c = node.createrawtransaction([find_unspent(node, txid_b, amt - fee)], {node.getnewaddress(): amt - (fee * 2)}) + txdata_c = node.signrawtransaction(txdata_c)['hex'] + txmodsize_c = get_modified_size(node, txdata_c) + txid_c = node.sendrawtransaction(txdata_c) + txid_c_mempoolentry = node.getrawmempool(True)[txid_c] + assert_approximate(txid_c_mempoolentry['startingpriority'], txid_c_mempoolentry['currentpriority']) + assert_approximate(txid_c_mempoolentry['currentpriority'], (amt - fee) * 1e8 * 4 / txmodsize_c) + + node.generate(1) + + txid_c_mempoolentry = node.getrawmempool(True)[txid_c] + assert_approximate(txid_c_mempoolentry['startingpriority'], (amt - fee) * 1e8 * 4 / txmodsize_c) + assert_approximate(txid_c_mempoolentry['currentpriority'], (amt - fee) * 1e8 * 5 / txmodsize_c) + + node.generate(2) + + txid_c_mempoolentry = node.getrawmempool(True)[txid_c] + assert_approximate(txid_c_mempoolentry['startingpriority'], (amt - fee) * 1e8 * 4 / txmodsize_c) + assert_approximate(txid_c_mempoolentry['currentpriority'], (amt - fee) * 1e8 * 7 / txmodsize_c) + +if __name__ == '__main__': + PriorityTest().main() diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index 59aa8c1..b3aa8af 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -4,20 +4,41 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # +from binascii import a2b_hex, b2a_hex +import cStringIO from mininode import * from script import CScript, CScriptOp, OP_TRUE, OP_CHECKSIG +import struct # Create a block (with regtest difficulty) -def create_block(hashprev, coinbase, nTime=None): +def create_block(hashprev=None, coinbase=None, nTime=None, tmpl=None, txlist=None): block = CBlock() - if nTime is None: + if tmpl: + block.nVersion = tmpl.get('version', block.nVersion) + if not nTime is None: + block.nTime = nTime + elif tmpl and not tmpl.get('curtime') is None: + block.nTime = tmpl['curtime'] + else: import time block.nTime = int(time.time()+600) + if not hashprev is None: + block.hashPrevBlock = hashprev else: - block.nTime = nTime - block.hashPrevBlock = hashprev - block.nBits = 0x207fffff # Will break after a difficulty adjustment... - block.vtx.append(coinbase) + block.hashPrevBlock = int ("0x" + tmpl['previousblockhash'] + "L", 0) + if tmpl and not tmpl.get('bits') is None: + block.nBits = struct.unpack('>I', a2b_hex(tmpl['bits']))[0] + else: + block.nBits = 0x207fffff # Will break after a difficulty adjustment... + if not coinbase is None: + block.vtx.append(coinbase) + if txlist: + for tx in txlist: + if not hasattr(tx, 'calc_sha256'): + txo = CTransaction() + txo.deserialize(cStringIO.StringIO(tx)) + tx = txo + block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() block.calc_sha256() return block diff --git a/src/main.cpp b/src/main.cpp index 37dfaaa..f02a53e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -948,7 +948,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees); CAmount inChainInputValue; - double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); + // Priority is based on height+1 since it is used for mining the *next* block + double dPriority = view.GetPriority(tx, chainActive.Height() + 1, inChainInputValue); // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 3cd2b76..8d9c66b 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -194,7 +194,7 @@ UniValue mempoolToJSON(bool fVerbose = false) info.push_back(Pair("time", e.GetTime())); info.push_back(Pair("height", (int)e.GetHeight())); info.push_back(Pair("startingpriority", e.GetStartingPriority())); - info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height() + 1))); info.push_back(Pair("descendantcount", e.GetCountWithDescendants())); info.push_back(Pair("descendantsize", e.GetSizeWithDescendants())); info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants())); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index ed44647..aed5fb4 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -39,7 +39,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, feeDelta = 0; - cachedHeight = entryHeight; + cachedHeight = entryHeight + 1; cachedPriority = entryPriority; } @@ -328,8 +328,9 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t void CTxMemPoolEntry::UpdateCachedPriority(unsigned int currentHeight, CAmount valueInCurrentBlock) { + ++currentHeight; int heightDiff = currentHeight - cachedHeight; - double deltaPriority = ((double)heightDiff*inChainInputValue)/nModSize; + double deltaPriority = (((double)heightDiff*inChainInputValue) + valueInCurrentBlock)/nModSize; cachedPriority += deltaPriority; cachedHeight = currentHeight; inChainInputValue += valueInCurrentBlock; @@ -648,11 +649,12 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins, unsigned int nBlockHeight) unsigned int i = 0; checkTotal += it->GetTxSize(); CAmount dummyValue; - double freshPriority = view.GetPriority(it->GetTx(), nBlockHeight, dummyValue); - double cachePriority = it->GetPriority(nBlockHeight); + double freshPriority = view.GetPriority(it->GetTx(), nBlockHeight + 1, dummyValue); + double cachePriority = it->GetPriority(nBlockHeight + 1); double priDiff = cachePriority > freshPriority ? cachePriority - freshPriority : freshPriority - cachePriority; // Verify that the difference between the on the fly calculation and a fresh calculation - // is small enough to be a result of double imprecision. + // is small enough to be a result of double imprecision. nBlockHeight + 1 is the + // earliest reliable value to use. assert(priDiff < .0001 * freshPriority + 1); innerUsage += it->DynamicMemoryUsage(); const CTransaction& tx = it->GetTx();