aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.cpp10
-rw-r--r--src/miner.cpp14
-rw-r--r--src/rpcclient.cpp2
-rw-r--r--src/rpcmining.cpp14
-rw-r--r--src/rpcserver.cpp1
-rw-r--r--src/rpcserver.h1
-rw-r--r--src/script.h2
-rw-r--r--src/test/DoS_tests.cpp87
-rw-r--r--src/txmempool.cpp29
-rw-r--r--src/txmempool.h6
-rw-r--r--src/util.cpp37
11 files changed, 101 insertions, 102 deletions
diff --git a/src/main.cpp b/src/main.cpp
index ea47601081..e06c519ee2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -789,6 +789,16 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, enum GetMinFee_mode mode)
{
+ {
+ LOCK(mempool.cs);
+ uint256 hash = tx.GetHash();
+ double dPriorityDelta = 0;
+ int64_t nFeeDelta = 0;
+ mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
+ if (dPriorityDelta > 0 || nFeeDelta > 0)
+ return 0;
+ }
+
// Base fee is either minTxFee or minRelayTxFee
CFeeRate baseFeeRate = (mode == GMF_RELAY) ? tx.minRelayTxFee : tx.minTxFee;
diff --git a/src/miner.cpp b/src/miner.cpp
index 19b4694357..69e53756e0 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -3,6 +3,8 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <inttypes.h>
+
#include "miner.h"
#include "core.h"
@@ -186,6 +188,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
dPriority = tx.ComputePriority(dPriority, nTxSize);
+ uint256 hash = tx.GetHash();
+ mempool.ApplyDeltas(hash, dPriority, nTotalIn);
+
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
if (porphan)
@@ -227,10 +232,14 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue;
// Skip free transactions if we're past the minimum block size:
- if (fSortedByFee && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
+ const uint256& hash = tx.GetHash();
+ double dPriorityDelta = 0;
+ int64_t nFeeDelta = 0;
+ mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
+ if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < CTransaction::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
continue;
- // Prioritize by fee once past the priority size or we run out of high-priority
+ // Prioritise by fee once past the priority size or we run out of high-priority
// transactions:
if (!fSortedByFee &&
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
@@ -257,7 +266,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue;
CTxUndo txundo;
- const uint256& hash = tx.GetHash();
UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1);
// Added
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index 3a06e33016..30881b9b1a 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -72,6 +72,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "listtransactions" && n > 2) ConvertTo<int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<int64_t>(params[1]);
+ if (strMethod == "prioritisetransaction" && n > 1) ConvertTo<double>(params[1]);
+ if (strMethod == "prioritisetransaction" && n > 2) ConvertTo<int64_t>(params[2]);
if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
if (strMethod == "listsinceblock" && n > 1) ConvertTo<int64_t>(params[1]);
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 0072557f87..98caf704ee 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -247,6 +247,20 @@ Value getmininginfo(const Array& params, bool fHelp)
}
+Value prioritisetransaction(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 3)
+ throw runtime_error(
+ "prioritisetransaction <txid> <priority delta> <fee delta>\n"
+ "Accepts the transaction into mined blocks at a higher (or lower) priority");
+
+ uint256 hash;
+ hash.SetHex(params[0].get_str());
+ mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), params[2].get_int64());
+ return true;
+}
+
+
Value getblocktemplate(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index c5d09cf577..6552de8c49 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -254,6 +254,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getblocktemplate", &getblocktemplate, true, false, false },
{ "getmininginfo", &getmininginfo, true, false, false },
{ "getnetworkhashps", &getnetworkhashps, true, false, false },
+ { "prioritisetransaction", &prioritisetransaction, true, false, false },
{ "submitblock", &submitblock, false, true, false },
/* Raw transactions */
diff --git a/src/rpcserver.h b/src/rpcserver.h
index 5271542385..1966a65b50 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -130,6 +130,7 @@ extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHe
extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value prioritisetransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value estimatefee(const json_spirit::Array& params, bool fHelp);
diff --git a/src/script.h b/src/script.h
index ea988f0e40..7ab471f6e6 100644
--- a/src/script.h
+++ b/src/script.h
@@ -20,7 +20,7 @@
class CCoins;
class CKeyStore;
class CTransaction;
-class CMutableTransaction;
+struct CMutableTransaction;
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index d512053051..5e17555e7a 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -231,91 +231,4 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
}
-BOOST_AUTO_TEST_CASE(DoS_checkSig)
-{
- // Test signature caching code (see key.cpp Verify() methods)
-
- CKey key;
- key.MakeNewKey(true);
- CBasicKeyStore keystore;
- keystore.AddKey(key);
- unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
-
- // 100 orphan transactions:
- static const int NPREV=100;
- CMutableTransaction orphans[NPREV];
- for (int i = 0; i < NPREV; i++)
- {
- CMutableTransaction& tx = orphans[i];
- tx.vin.resize(1);
- tx.vin[0].prevout.n = 0;
- tx.vin[0].prevout.hash = GetRandHash();
- tx.vin[0].scriptSig << OP_1;
- tx.vout.resize(1);
- tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
-
- AddOrphanTx(tx);
- }
-
- // Create a transaction that depends on orphans:
- CMutableTransaction tx;
- tx.vout.resize(1);
- tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
- tx.vin.resize(NPREV);
- for (unsigned int j = 0; j < tx.vin.size(); j++)
- {
- tx.vin[j].prevout.n = 0;
- tx.vin[j].prevout.hash = orphans[j].GetHash();
- }
- // Creating signatures primes the cache:
- boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time();
- for (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(SignSignature(keystore, orphans[j], tx, j));
- boost::posix_time::ptime mst2 = boost::posix_time::microsec_clock::local_time();
- boost::posix_time::time_duration msdiff = mst2 - mst1;
- long nOneValidate = msdiff.total_milliseconds();
- if (fDebug) printf("DoS_Checksig sign: %ld\n", nOneValidate);
-
- // ... now validating repeatedly should be quick:
- // 2.8GHz machine, -g build: Sign takes ~760ms,
- // uncached Verify takes ~250ms, cached Verify takes ~50ms
- // (for 100 single-signature inputs)
- mst1 = boost::posix_time::microsec_clock::local_time();
- for (unsigned int i = 0; i < 5; i++)
- for (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL));
- mst2 = boost::posix_time::microsec_clock::local_time();
- msdiff = mst2 - mst1;
- long nManyValidate = msdiff.total_milliseconds();
- if (fDebug) printf("DoS_Checksig five: %ld\n", nManyValidate);
-
- BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed");
-
- // Empty a signature, validation should fail:
- CScript save = tx.vin[0].scriptSig;
- tx.vin[0].scriptSig = CScript();
- BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL));
- tx.vin[0].scriptSig = save;
-
- // Swap signatures, validation should fail:
- std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
- BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL));
- BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, SIGHASH_ALL));
- std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
-
- // Exercise -maxsigcachesize code:
- mapArgs["-maxsigcachesize"] = "10";
- // Generate a new, different signature for vin[0] to trigger cache clear:
- CScript oldSig = tx.vin[0].scriptSig;
- BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0));
- BOOST_CHECK(tx.vin[0].scriptSig != oldSig);
- for (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL));
- mapArgs.erase("-maxsigcachesize");
-
- LimitOrphanTxSize(0);
-}
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 4bf01d4848..52f82ef040 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -447,6 +447,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i
std::list<CTransaction> dummy;
remove(tx, dummy, false);
removeConflicts(tx, conflicts);
+ ClearPrioritisation(tx.GetHash());
}
}
@@ -564,6 +565,34 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
return true;
}
+void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, int64_t nFeeDelta)
+{
+ {
+ LOCK(cs);
+ std::pair<double, int64_t> &deltas = mapDeltas[hash];
+ deltas.first += dPriorityDelta;
+ deltas.second += nFeeDelta;
+ }
+ LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash.c_str(), dPriorityDelta, nFeeDelta);
+}
+
+void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta)
+{
+ LOCK(cs);
+ std::map<uint256, std::pair<double, int64_t> >::iterator pos = mapDeltas.find(hash);
+ if (pos == mapDeltas.end())
+ return;
+ const std::pair<double, int64_t> &deltas = pos->second;
+ dPriorityDelta += deltas.first;
+ nFeeDelta += deltas.second;
+}
+
+void CTxMemPool::ClearPrioritisation(const uint256 hash)
+{
+ LOCK(cs);
+ mapDeltas.erase(hash);
+}
+
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
diff --git a/src/txmempool.h b/src/txmempool.h
index b2915aa842..f7dbb126a0 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -71,6 +71,7 @@ public:
mutable CCriticalSection cs;
std::map<uint256, CTxMemPoolEntry> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;
+ std::map<uint256, std::pair<double, int64_t> > mapDeltas;
CTxMemPool();
~CTxMemPool();
@@ -95,6 +96,11 @@ public:
unsigned int GetTransactionsUpdated() const;
void AddTransactionsUpdated(unsigned int n);
+ /** Affect CreateNewBlock prioritisation of transactions */
+ void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, int64_t nFeeDelta);
+ void ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta);
+ void ClearPrioritisation(const uint256 hash);
+
unsigned long size()
{
LOCK(cs);
diff --git a/src/util.cpp b/src/util.cpp
index 7a0e2cc800..9e4b2b787e 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -167,16 +167,31 @@ void RandAddSeedPerfmon()
#ifdef WIN32
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
- unsigned char pdata[250000];
- memset(pdata, 0, sizeof(pdata));
- unsigned long nSize = sizeof(pdata);
- long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
+ std::vector <unsigned char> vData(250000,0);
+ long ret = 0;
+ unsigned long nSize = 0;
+ const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
+ while (true)
+ {
+ nSize = vData.size();
+ ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize);
+ if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
+ break;
+ vData.resize(std::max((vData.size()*3)/2, nMaxSize)); // Grow size of buffer exponentially
+ }
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS)
{
- RAND_add(pdata, nSize, nSize/100.0);
- OPENSSL_cleanse(pdata, nSize);
- LogPrint("rand", "RandAddSeed() %lu bytes\n", nSize);
+ RAND_add(begin_ptr(vData), nSize, nSize/100.0);
+ OPENSSL_cleanse(begin_ptr(vData), nSize);
+ LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
+ } else {
+ static bool warned = false; // Warn only once
+ if (!warned)
+ {
+ LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
+ warned = true;
+ }
}
#endif
}
@@ -1140,15 +1155,15 @@ void ShrinkDebugFile()
if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000)
{
// Restart the file with some of the end
- char pch[200000];
- fseek(file, -sizeof(pch), SEEK_END);
- int nBytes = fread(pch, 1, sizeof(pch), file);
+ std::vector <char> vch(200000,0);
+ fseek(file, -vch.size(), SEEK_END);
+ int nBytes = fread(begin_ptr(vch), 1, vch.size(), file);
fclose(file);
file = fopen(pathLog.string().c_str(), "w");
if (file)
{
- fwrite(pch, 1, nBytes, file);
+ fwrite(begin_ptr(vch), 1, nBytes, file);
fclose(file);
}
}