From cdf312f0d19a6c91d28f9b8886ec968aeb61a11e Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 22 Nov 2014 15:08:19 +0100 Subject: [PATCH] Limit blocks by BDB locks required. This is a temporary measure to make the rebased client "compatible" with the old BDB-based one. Based on https://github.com/bitcoin/bitcoin/commit/8c222dca4f961ad13ec64d690134a40d09b20813. --- TODO | 3 ++- src/main.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ src/main.h | 3 +++ src/miner.cpp | 4 ++++ src/rpcmining.cpp | 4 ++-- 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index 13d4d40a5e..5b37c6bd0b 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,8 @@ * soft fork in namecoind: + + prevent "name_new stealing" in memory pool + + enforce nametx-version also for name *outputs* - limit DB locks / unique tx - disallow greedy names - - enforce nametx-version also for name *outputs* - disallow auxpow parent blocks with auxpow - update fee logic? diff --git a/src/main.cpp b/src/main.cpp index 30de00e80e..efe4bb219c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2485,6 +2485,43 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } +/* Temporary check that blocks are compatible with BDB's 10,000 lock limit. + This is based on Bitcoin's commit 8c222dca4f961ad13ec64d690134a40d09b20813. + Each "object" touched in the DB may cause two locks (one read and one + write lock). Objects are transaction IDs and names. Thus, count the + total number of transaction IDs (tx themselves plus all distinct inputs). + In addition, each Namecoin transaction could touch at most one name, + so add them as well. */ +bool CheckDbLockLimit(const CBlock& block, const CTransaction* extraTx) +{ + std::vector vtx = block.vtx; + if (extraTx) + vtx.push_back(*extraTx); + + set setTxIds; + unsigned nNames = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + setTxIds.insert(tx.GetHash()); + if (tx.IsNamecoin()) + ++nNames; + + BOOST_FOREACH(const CTxIn& txIn, tx.vin) + setTxIds.insert(txIn.prevout.hash); + } + + const unsigned nTotalIds = setTxIds.size() + nNames; + + if (nTotalIds > 4500) + return error("%s : %u locks estimated, that is too much for BDB", + __func__, nTotalIds); + + if (fDebug) + LogPrintf ("%s : need %u locks\n", __func__, nTotalIds); + + return true; +} + bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) { // Check proof of work matches claimed amount @@ -2534,6 +2571,12 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return state.DoS(100, error("CheckBlock() : size limits failed"), REJECT_INVALID, "bad-blk-length"); + // Enforce the temporary DB lock limit. + // TODO: Remove with a hardfork in the future. + if (!CheckDbLockLimit(block)) + return state.DoS(100, error("%s : DB lock limit failed", __func__), + REJECT_INVALID, "bad-db-locks"); + // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), diff --git a/src/main.h b/src/main.h index 34d0cc118d..6d10252a12 100644 --- a/src/main.h +++ b/src/main.h @@ -388,6 +388,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex /** Apply the effects of this block (with given index) on the UTXO set represented by coins */ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, std::set& expiredNames, bool fJustCheck = false); +// TODO: Remove when this check is no longer necessary. +bool CheckDbLockLimit(const CBlock& block, const CTransaction* extraTx = NULL); + /** Context-independent validity checks */ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); diff --git a/src/miner.cpp b/src/miner.cpp index fd27d71c61..e42e1100e3 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -243,6 +243,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (nBlockSize + nTxSize >= nBlockMaxSize) continue; + // Check the DB lock limit won't be exceeded. + if (!CheckDbLockLimit(*pblock, &tx)) + continue; + // Legacy limits on sigOps: unsigned int nTxSigOps = GetLegacySigOpCount(tx); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 56757d0cb6..9729b88476 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -454,10 +454,10 @@ Value getblocktemplate(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); if (vNodes.empty()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!"); + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Namecoin is not connected!"); if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Namecoin is downloading blocks..."); static unsigned int nTransactionsUpdatedLast;