diff options
-rw-r--r-- | src/main.cpp | 10 | ||||
-rw-r--r-- | src/miner.cpp | 14 | ||||
-rw-r--r-- | src/rpcclient.cpp | 2 | ||||
-rw-r--r-- | src/rpcmining.cpp | 14 | ||||
-rw-r--r-- | src/rpcserver.cpp | 1 | ||||
-rw-r--r-- | src/rpcserver.h | 1 | ||||
-rw-r--r-- | src/script.h | 2 | ||||
-rw-r--r-- | src/test/DoS_tests.cpp | 87 | ||||
-rw-r--r-- | src/txmempool.cpp | 29 | ||||
-rw-r--r-- | src/txmempool.h | 6 | ||||
-rw-r--r-- | src/util.cpp | 37 |
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); } } |