diff options
-rw-r--r-- | contrib/bitcoin-qt.pro | 2 | ||||
-rw-r--r-- | doc/release-notes.md | 9 | ||||
-rw-r--r-- | src/Makefile.qt.include | 2 | ||||
-rw-r--r-- | src/chainparams.cpp | 2 | ||||
-rw-r--r-- | src/core_memusage.h | 2 | ||||
-rw-r--r-- | src/main.cpp | 76 | ||||
-rw-r--r-- | src/miner.cpp | 2 | ||||
-rw-r--r-- | src/primitives/block.cpp | 39 | ||||
-rw-r--r-- | src/primitives/block.h | 10 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 17 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 2 | ||||
-rw-r--r-- | src/qt/forms/debugwindow.ui (renamed from src/qt/forms/rpcconsole.ui) | 0 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 2 | ||||
-rw-r--r-- | src/test/miner_tests.cpp | 2 | ||||
-rw-r--r-- | src/test/pmt_tests.cpp | 2 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 15 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 15 | ||||
-rw-r--r-- | src/wallet/wallet.h | 7 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 6 |
19 files changed, 92 insertions, 120 deletions
diff --git a/contrib/bitcoin-qt.pro b/contrib/bitcoin-qt.pro index 3a72d10f47..b8133bf789 100644 --- a/contrib/bitcoin-qt.pro +++ b/contrib/bitcoin-qt.pro @@ -11,7 +11,7 @@ FORMS += \ ../src/qt/forms/overviewpage.ui \ ../src/qt/forms/receivecoinsdialog.ui \ ../src/qt/forms/receiverequestdialog.ui \ - ../src/qt/forms/rpcconsole.ui \ + ../src/qt/forms/debugwindow.ui \ ../src/qt/forms/sendcoinsdialog.ui \ ../src/qt/forms/sendcoinsentry.ui \ ../src/qt/forms/signverifymessagedialog.ui \ diff --git a/doc/release-notes.md b/doc/release-notes.md index a06075334a..bef9af60ef 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -105,6 +105,15 @@ In this version, it is only enforced for peers that send protocol versions removed. It is recommended to update SPV clients to check for the `NODE_BLOOM` service bit for nodes that report versions newer than 70011. +Merkle branches removed from wallet +----------------------------------- + +Previously, every wallet transaction stored a Merkle branch to prove its +presence in blocks. This wasn't being used for more than an expensive +sanity check. Since 0.12, these are no longer stored. When loading a +0.12 wallet into an older version, it will automatically rescan to avoid +failed checks. + 0.12.0 Change log ================= diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 480bd9dc8a..3330ed2890 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -87,7 +87,7 @@ QT_FORMS_UI = \ qt/forms/overviewpage.ui \ qt/forms/receivecoinsdialog.ui \ qt/forms/receiverequestdialog.ui \ - qt/forms/rpcconsole.ui \ + qt/forms/debugwindow.ui \ qt/forms/sendcoinsdialog.ui \ qt/forms/sendcoinsentry.ui \ qt/forms/signverifymessagedialog.ui \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9c843f6e7e..15b86cdda6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -31,7 +31,7 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi genesis.nVersion = nVersion; genesis.vtx.push_back(txNew); genesis.hashPrevBlock.SetNull(); - genesis.hashMerkleRoot = genesis.BuildMerkleTree(); + genesis.hashMerkleRoot = genesis.ComputeMerkleRoot(); return genesis; } diff --git a/src/core_memusage.h b/src/core_memusage.h index 711135bb44..a05f59ee0c 100644 --- a/src/core_memusage.h +++ b/src/core_memusage.h @@ -48,7 +48,7 @@ static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) { } static inline size_t RecursiveDynamicUsage(const CBlock& block) { - size_t mem = memusage::DynamicUsage(block.vtx) + memusage::DynamicUsage(block.vMerkleTree); + size_t mem = memusage::DynamicUsage(block.vtx); for (std::vector<CTransaction>::const_iterator it = block.vtx.begin(); it != block.vtx.end(); it++) { mem += RecursiveDynamicUsage(*it); } diff --git a/src/main.cpp b/src/main.cpp index e27b8444b4..1df4c9fa36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -965,47 +965,45 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; + + LOCK(cs_main); + + if (mempool.lookup(hash, txOut)) { - LOCK(cs_main); - { - if (mempool.lookup(hash, txOut)) - { - return true; - } - } + return true; + } - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) - return error("%s: txid mismatch", __func__); - return true; + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s: txid mismatch", __func__); + return true; } + } - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - int nHeight = -1; - { - CCoinsViewCache &view = *pcoinsTip; - const CCoins* coins = view.AccessCoins(hash); - if (coins) - nHeight = coins->nHeight; - } - if (nHeight > 0) - pindexSlow = chainActive[nHeight]; + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache &view = *pcoinsTip; + const CCoins* coins = view.AccessCoins(hash); + if (coins) + nHeight = coins->nHeight; } + if (nHeight > 0) + pindexSlow = chainActive[nHeight]; } if (pindexSlow) { @@ -2585,6 +2583,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo { // These are checks that are independent of context. + if (block.fChecked) + return true; + // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. if (!CheckBlockHeader(block, state, fCheckPOW)) @@ -2593,7 +2594,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; - uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); + uint256 hashMerkleRoot2 = block.ComputeMerkleRoot(&mutated); if (block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); @@ -2640,6 +2641,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); + if (fCheckPOW && fCheckMerkleRoot) + block.fChecked = true; + return true; } diff --git a/src/miner.cpp b/src/miner.cpp index b2a356e52d..42c8bb970b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -368,7 +368,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned assert(txCoinbase.vin[0].scriptSig.size() <= 100); pblock->vtx[0] = txCoinbase; - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->hashMerkleRoot = pblock->ComputeMerkleRoot(); } ////////////////////////////////////////////////////////////////////////////// diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 5b9c13d870..7a58074d24 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -15,7 +15,7 @@ uint256 CBlockHeader::GetHash() const return SerializeHash(*this); } -uint256 CBlock::BuildMerkleTree(bool* fMutated) const +uint256 CBlock::ComputeMerkleRoot(bool* fMutated) const { /* WARNING! If you're reading this because you're learning about crypto and/or designing a new system that will use merkle trees, keep in mind @@ -52,7 +52,7 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const known ways of changing the transactions without affecting the merkle root. */ - vMerkleTree.clear(); + std::vector<uint256> vMerkleTree; vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. for (std::vector<CTransaction>::const_iterator it(vtx.begin()); it != vtx.end(); ++it) vMerkleTree.push_back(it->GetHash()); @@ -78,37 +78,6 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); } -std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const -{ - if (vMerkleTree.empty()) - BuildMerkleTree(); - std::vector<uint256> vMerkleBranch; - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - int i = std::min(nIndex^1, nSize-1); - vMerkleBranch.push_back(vMerkleTree[j+i]); - nIndex >>= 1; - j += nSize; - } - return vMerkleBranch; -} - -uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex) -{ - if (nIndex == -1) - return uint256(); - for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it) - { - if (nIndex & 1) - hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash)); - else - hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it)); - nIndex >>= 1; - } - return hash; -} - std::string CBlock::ToString() const { std::stringstream s; @@ -123,9 +92,5 @@ std::string CBlock::ToString() const { s << " " << vtx[i].ToString() << "\n"; } - s << " vMerkleTree: "; - for (unsigned int i = 0; i < vMerkleTree.size(); i++) - s << " " << vMerkleTree[i].ToString(); - s << "\n"; return s.str(); } diff --git a/src/primitives/block.h b/src/primitives/block.h index 59f46deb1c..86106098f5 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -78,7 +78,7 @@ public: std::vector<CTransaction> vtx; // memory only - mutable std::vector<uint256> vMerkleTree; + mutable bool fChecked; CBlock() { @@ -103,7 +103,7 @@ public: { CBlockHeader::SetNull(); vtx.clear(); - vMerkleTree.clear(); + fChecked = false; } CBlockHeader GetBlockHeader() const @@ -118,14 +118,12 @@ public: return block; } - // Build the in-memory merkle tree for this block and return the merkle root. + // Build the merkle tree for this block and return the merkle root. // If non-NULL, *mutated is set to whether mutation was detected in the merkle // tree (a duplication of transactions in the block leading to an identical // merkle root). - uint256 BuildMerkleTree(bool* mutated = NULL) const; + uint256 ComputeMerkleRoot(bool* mutated = NULL) const; - std::vector<uint256> GetMerkleBranch(int nIndex) const; - static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex); std::string ToString() const; }; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index db9e558764..d930d15953 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -215,11 +215,6 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n statusBar()->addWidget(progressBar); statusBar()->addPermanentWidget(frameBlocks); - connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); - - // prevents an open debug window from becoming stuck/unusable on client shutdown - connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide())); - // Install event filter to be able to catch status tip events (QEvent::StatusTip) this->installEventFilter(this); @@ -352,6 +347,10 @@ void BitcoinGUI::createActions() connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden())); connect(showHelpMessageAction, SIGNAL(triggered()), this, SLOT(showHelpMessageClicked())); + connect(openRPCConsoleAction, SIGNAL(triggered()), this, SLOT(showDebugWindow())); + // prevents an open debug window from becoming stuck/unusable on client shutdown + connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide())); + #ifdef ENABLE_WALLET if(walletFrame) { @@ -590,6 +589,14 @@ void BitcoinGUI::aboutClicked() dlg.exec(); } +void BitcoinGUI::showDebugWindow() +{ + rpcConsole->showNormal(); + rpcConsole->show(); + rpcConsole->raise(); + rpcConsole->activateWindow(); +} + void BitcoinGUI::showHelpMessageClicked() { helpMessageDialog->show(); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index f1b7a502ba..717f2bd12d 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -196,6 +196,8 @@ private Q_SLOTS: void optionsClicked(); /** Show about dialog */ void aboutClicked(); + /** Show debug window */ + void showDebugWindow(); /** Show help message dialog */ void showHelpMessageClicked(); #ifndef Q_OS_MAC diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/debugwindow.ui index 4117da57f5..4117da57f5 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/debugwindow.ui diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 9603a26c6e..72a3023c9a 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rpcconsole.h" -#include "ui_rpcconsole.h" +#include "ui_debugwindow.h" #include "bantablemodel.h" #include "clientmodel.h" diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ad79a558c2..91a3a5738e 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->vtx[0] = CTransaction(txCoinbase); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->hashMerkleRoot = pblock->ComputeMerkleRoot(); pblock->nNonce = blockinfo[i].nonce; CValidationState state; BOOST_CHECK(ProcessNewBlock(state, NULL, pblock, true, NULL)); diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index f6d06d6805..d9f3c3e467 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1) } // calculate actual merkle root and height - uint256 merkleRoot1 = block.BuildMerkleTree(); + uint256 merkleRoot1 = block.ComputeMerkleRoot(); std::vector<uint256> vTxid(nTx, uint256()); for (unsigned int j=0; j<nTx; j++) vTxid[j] = block.vtx[j].GetHash(); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 8d557979c0..7e22faac37 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -97,8 +97,6 @@ UniValue importprivkey(const UniValue& params, bool fHelp) + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing keys is disabled in pruned mode"); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -114,6 +112,9 @@ UniValue importprivkey(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strSecret); @@ -201,8 +202,6 @@ UniValue importaddress(const UniValue& params, bool fHelp) + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing addresses is disabled in pruned mode"); string strLabel = ""; if (params.size() > 1) @@ -213,6 +212,9 @@ UniValue importaddress(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + // Whether to import a p2sh version, too bool fP2SH = false; if (params.size() > 3) @@ -264,8 +266,6 @@ UniValue importpubkey(const UniValue& params, bool fHelp) + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing public keys is disabled in pruned mode"); string strLabel = ""; if (params.size() > 1) @@ -276,6 +276,9 @@ UniValue importpubkey(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + if (!IsHex(params[0].get_str())) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); std::vector<unsigned char> data(ParseHex(params[0].get_str())); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c3b1172201..39eff3af30 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -702,9 +702,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } - if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) { - wtx.vMerkleBranch = wtxIn.vMerkleBranch; wtx.nIndex = wtxIn.nIndex; fUpdated = true; } @@ -2812,15 +2811,11 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) break; if (nIndex == (int)block.vtx.size()) { - vMerkleBranch.clear(); nIndex = -1; LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); return 0; } - // Fill in merkle branch - vMerkleBranch = block.GetMerkleBranch(nIndex); - // Is the tx in a block that's in the main chain BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) @@ -2846,14 +2841,6 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const if (!pindex || !chainActive.Contains(pindex)) return 0; - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) - { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; - } - pindexRet = pindex; return chainActive.Height() - pindex->nHeight + 1; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index bd30b67b09..34e98cfb81 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -151,13 +151,8 @@ private: public: uint256 hashBlock; - std::vector<uint256> vMerkleBranch; int nIndex; - // memory only - mutable bool fMerkleVerified; - - CMerkleTx() { Init(); @@ -172,13 +167,13 @@ public: { hashBlock = uint256(); nIndex = -1; - fMerkleVerified = false; } ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + std::vector<uint256> vMerkleBranch; // For compatibility with older versions. READWRITE(*(CTransaction*)this); nVersion = this->nVersion; READWRITE(hashBlock); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index c1eb184581..0624e442d1 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -131,12 +131,14 @@ bool CWalletDB::EraseWatchOnly(const CScript &dest) bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); + Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan + return Write(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::ReadBestBlock(CBlockLocator& locator) { - return Read(std::string("bestblock"), locator); + if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; + return Read(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) |