diff options
Diffstat (limited to 'src')
27 files changed, 467 insertions, 279 deletions
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index f595f16eab..88d8da6ed5 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -20,6 +20,15 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; + + // Even if tx.nLockTime isn't satisfied by nBlockHeight/nBlockTime, a + // transaction is still considered final if all inputs' nSequence == + // SEQUENCE_FINAL (0xffffffff), in which case nLockTime is ignored. + // + // Because of this behavior OP_CHECKLOCKTIMEVERIFY/CheckLockTime() will + // also check that the spending input's nSequence != SEQUENCE_FINAL, + // ensuring that an unsatisfied nLockTime value will actually cause + // IsFinalTx() to return false here: for (const auto& txin : tx.vin) { if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL)) return false; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index b3984a43bb..8741ad9b86 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -136,7 +136,7 @@ static struct evhttp* eventHTTP = nullptr; //! List of subnets to allow RPC connections from static std::vector<CSubNet> rpc_allow_subnets; //! Work queue for handling longer requests off the event loop thread -static WorkQueue<HTTPClosure>* workQueue = nullptr; +static std::unique_ptr<WorkQueue<HTTPClosure>> g_work_queue{nullptr}; //! Handlers for (sub)paths static std::vector<HTTPPathHandler> pathHandlers; //! Bound listening sockets @@ -256,10 +256,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg) // Dispatch to worker thread if (i != iend) { std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler)); - assert(workQueue); - if (workQueue->Enqueue(item.get())) + assert(g_work_queue); + if (g_work_queue->Enqueue(item.get())) { item.release(); /* if true, queue took ownership */ - else { + } else { LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n"); item->req->WriteReply(HTTP_SERVICE_UNAVAILABLE, "Work queue depth exceeded"); } @@ -392,7 +392,7 @@ bool InitHTTPServer() int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); - workQueue = new WorkQueue<HTTPClosure>(workQueueDepth); + g_work_queue = std::make_unique<WorkQueue<HTTPClosure>>(workQueueDepth); // transfer ownership to eventBase/HTTP via .release() eventBase = base_ctr.release(); eventHTTP = http_ctr.release(); @@ -424,7 +424,7 @@ void StartHTTPServer() g_thread_http = std::thread(ThreadHTTP, eventBase); for (int i = 0; i < rpcThreads; i++) { - g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i); + g_thread_http_workers.emplace_back(HTTPWorkQueueRun, g_work_queue.get(), i); } } @@ -435,16 +435,17 @@ void InterruptHTTPServer() // Reject requests on current connections evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr); } - if (workQueue) - workQueue->Interrupt(); + if (g_work_queue) { + g_work_queue->Interrupt(); + } } void StopHTTPServer() { LogPrint(BCLog::HTTP, "Stopping HTTP server\n"); - if (workQueue) { + if (g_work_queue) { LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n"); - for (auto& thread: g_thread_http_workers) { + for (auto& thread : g_thread_http_workers) { thread.join(); } g_thread_http_workers.clear(); @@ -467,10 +468,7 @@ void StopHTTPServer() event_base_free(eventBase); eventBase = nullptr; } - if (workQueue) { - delete workQueue; - workQueue = nullptr; - } + g_work_queue.reset(); LogPrint(BCLog::HTTP, "Stopped HTTP server\n"); } diff --git a/src/init.cpp b/src/init.cpp index da0447ca79..a88adbaa21 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1372,7 +1372,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // block file from disk. // Note that it also sets fReindex based on the disk flag! // From here on out fReindex and fReset mean something different! - if (!chainman.LoadBlockIndex(chainparams)) { + if (!chainman.LoadBlockIndex()) { if (ShutdownRequested()) break; strLoadError = _("Error loading block database"); break; @@ -1396,7 +1396,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // If we're not mid-reindex (based on disk + args), add a genesis block on disk // (otherwise we use the one already on disk). // This is called again in ThreadImport after the reindex completes. - if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock(chainparams)) { + if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) { strLoadError = _("Error initializing block database"); break; } @@ -1427,7 +1427,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate - if (!chainstate->ReplayBlocks(chainparams)) { + if (!chainstate->ReplayBlocks()) { strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate."); failed_chainstate_init = true; break; @@ -1439,7 +1439,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (!is_coinsview_empty(chainstate)) { // LoadChainTip initializes the chain based on CoinsTip()'s best block - if (!chainstate->LoadChainTip(chainparams)) { + if (!chainstate->LoadChainTip()) { strLoadError = _("Error initializing block database"); failed_chainstate_init = true; break; // out of the per-chainstate loop @@ -1461,7 +1461,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) LOCK(cs_main); auto chainstates{chainman.GetAll()}; if (std::any_of(chainstates.begin(), chainstates.end(), - [&chainparams](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(chainparams); })) { + [](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) { strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."), chainparams.GetConsensus().SegwitHeight); break; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 2175b76d16..ab9c41ca2b 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1709,7 +1709,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& } // release cs_main before calling ActivateBestChain if (need_activate_chain) { BlockValidationState state; - if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) { + if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } @@ -2935,7 +2935,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, a_recent_block = most_recent_block; } BlockValidationState state; - if (!m_chainman.ActiveChainstate().ActivateBestChain(state, m_chainparams, a_recent_block)) { + if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 013d61282b..0083b74b33 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -493,7 +493,6 @@ struct CImportingNow { void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args) { - const CChainParams& chainparams = Params(); ScheduleBatchPriority(); { @@ -512,7 +511,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile break; // This error is logged in OpenBlockFile } LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); - chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file, &pos); + chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; @@ -523,7 +522,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile fReindex = false; LogPrintf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): - chainman.ActiveChainstate().LoadGenesisBlock(chainparams); + chainman.ActiveChainstate().LoadGenesisBlock(); } // -loadblock= @@ -531,7 +530,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile FILE* file = fsbridge::fopen(path, "rb"); if (file) { LogPrintf("Importing blocks file %s...\n", path.string()); - chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file); + chainman.ActiveChainstate().LoadExternalBlockFile(file); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; @@ -548,7 +547,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile // the relevant pointers before the ABC call. for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) { BlockValidationState state; - if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) { + if (!chainstate->ActivateBestChain(state, nullptr)) { LogPrintf("Failed to connect best block (%s)\n", state.ToString()); StartShutdown(); return; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 3e1a0e0fa9..e883337fb5 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -69,7 +69,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe ->findChild<QCheckBox*>("optInRBF") ->setCheckState(rbf ? Qt::Checked : Qt::Unchecked); uint256 txid; - boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) { + boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](const uint256& hash, ChangeType status) { if (status == CT_NEW) txid = hash; })); ConfirmSend(); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f996aa9c76..02f8cec54c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1706,7 +1706,7 @@ static RPCHelpMan preciousblock() } BlockValidationState state; - chainman.ActiveChainstate().PreciousBlock(state, Params(), pblockindex); + chainman.ActiveChainstate().PreciousBlock(state, pblockindex); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); @@ -1743,10 +1743,10 @@ static RPCHelpMan invalidateblock() throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } - chainman.ActiveChainstate().InvalidateBlock(state, Params(), pblockindex); + chainman.ActiveChainstate().InvalidateBlock(state, pblockindex); if (state.IsValid()) { - chainman.ActiveChainstate().ActivateBestChain(state, Params()); + chainman.ActiveChainstate().ActivateBestChain(state); } if (!state.IsValid()) { @@ -1787,7 +1787,7 @@ static RPCHelpMan reconsiderblock() } BlockValidationState state; - chainman.ActiveChainstate().ActivateBestChain(state, Params()); + chainman.ActiveChainstate().ActivateBestChain(state); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString()); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index fdbd2d7fc7..682b55742a 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -166,7 +166,7 @@ public: * write_cache is the cache to write keys to (if not nullptr) * Caches are not exclusive but this is not tested. Currently we use them exclusively */ - virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) = 0; + virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const = 0; /** Whether this represent multiple public keys at different positions. */ virtual bool IsRange() const = 0; @@ -181,7 +181,7 @@ public: virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0; /** Get the descriptor string form with the xpub at the last hardened derivation */ - virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const = 0; + virtual bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr) const = 0; /** Derive a private key, if private data is available in arg. */ virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0; @@ -199,7 +199,7 @@ class OriginPubkeyProvider final : public PubkeyProvider public: OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : PubkeyProvider(exp_index), m_origin(std::move(info)), m_provider(std::move(provider)) {} - bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override + bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override { if (!m_provider->GetPubKey(pos, arg, key, info, read_cache, write_cache)) return false; std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint); @@ -216,10 +216,10 @@ public: ret = "[" + OriginString() + "]" + std::move(sub); return true; } - bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override + bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override { std::string sub; - if (!m_provider->ToNormalizedString(arg, sub, priv)) return false; + if (!m_provider->ToNormalizedString(arg, sub, cache)) return false; // If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider // In that case, we need to strip out the leading square bracket and fingerprint from the substring, // and append that to our own origin string. @@ -245,7 +245,7 @@ class ConstPubkeyProvider final : public PubkeyProvider public: ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {} - bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override + bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override { key = m_pubkey; info.path.clear(); @@ -263,9 +263,8 @@ public: ret = EncodeSecret(key); return true; } - bool ToNormalizedString(const SigningProvider& arg, std::string& ret, bool priv) const override + bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override { - if (priv) return ToPrivateString(arg, ret); ret = ToString(); return true; } @@ -288,9 +287,6 @@ class BIP32PubkeyProvider final : public PubkeyProvider CExtPubKey m_root_extkey; KeyPath m_path; DeriveType m_derive; - // Cache of the parent of the final derived pubkeys. - // Primarily useful for situations when no read_cache is provided - CExtPubKey m_cached_xpub; bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const { @@ -305,11 +301,14 @@ class BIP32PubkeyProvider final : public PubkeyProvider } // Derives the last xprv - bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv) const + bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv, CExtKey& last_hardened) const { if (!GetExtKey(arg, xprv)) return false; for (auto entry : m_path) { xprv.Derive(xprv, entry); + if (entry >> 31) { + last_hardened = xprv; + } } return true; } @@ -327,7 +326,7 @@ public: BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive) {} bool IsRange() const override { return m_derive != DeriveType::NO; } size_t GetSize() const override { return 33; } - bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key_out, KeyOriginInfo& final_info_out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override + bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key_out, KeyOriginInfo& final_info_out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override { // Info of parent of the to be derived pubkey KeyOriginInfo parent_info; @@ -343,6 +342,7 @@ public: // Derive keys or fetch them from cache CExtPubKey final_extkey = m_root_extkey; CExtPubKey parent_extkey = m_root_extkey; + CExtPubKey last_hardened_extkey; bool der = true; if (read_cache) { if (!read_cache->GetCachedDerivedExtPubKey(m_expr_index, pos, final_extkey)) { @@ -352,16 +352,17 @@ public: final_extkey = parent_extkey; if (m_derive == DeriveType::UNHARDENED) der = parent_extkey.Derive(final_extkey, pos); } - } else if (m_cached_xpub.pubkey.IsValid() && m_derive != DeriveType::HARDENED) { - parent_extkey = final_extkey = m_cached_xpub; - if (m_derive == DeriveType::UNHARDENED) der = parent_extkey.Derive(final_extkey, pos); } else if (IsHardened()) { CExtKey xprv; - if (!GetDerivedExtKey(arg, xprv)) return false; + CExtKey lh_xprv; + if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return false; parent_extkey = xprv.Neuter(); if (m_derive == DeriveType::UNHARDENED) der = xprv.Derive(xprv, pos); if (m_derive == DeriveType::HARDENED) der = xprv.Derive(xprv, pos | 0x80000000UL); final_extkey = xprv.Neuter(); + if (lh_xprv.key.IsValid()) { + last_hardened_extkey = lh_xprv.Neuter(); + } } else { for (auto entry : m_path) { der = parent_extkey.Derive(parent_extkey, entry); @@ -376,15 +377,14 @@ public: final_info_out = final_info_out_tmp; key_out = final_extkey.pubkey; - // We rely on the consumer to check that m_derive isn't HARDENED as above - // But we can't have already cached something in case we read something from the cache - // and parent_extkey isn't actually the parent. - if (!m_cached_xpub.pubkey.IsValid()) m_cached_xpub = parent_extkey; - if (write_cache) { // Only cache parent if there is any unhardened derivation if (m_derive != DeriveType::HARDENED) { write_cache->CacheParentExtPubKey(m_expr_index, parent_extkey); + // Cache last hardened xpub if we have it + if (last_hardened_extkey.pubkey.IsValid()) { + write_cache->CacheLastHardenedExtPubKey(m_expr_index, last_hardened_extkey); + } } else if (final_info_out.path.size() > 0) { write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey); } @@ -412,11 +412,10 @@ public: } return true; } - bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override + bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override { // For hardened derivation type, just return the typical string, nothing to normalize if (m_derive == DeriveType::HARDENED) { - if (priv) return ToPrivateString(arg, out); out = ToString(); return true; } @@ -429,33 +428,42 @@ public: } // Either no derivation or all unhardened derivation if (i == -1) { - if (priv) return ToPrivateString(arg, out); out = ToString(); return true; } - // Derive the xpub at the last hardened step - CExtKey xprv; - if (!GetExtKey(arg, xprv)) return false; + // Get the path to the last hardened stup KeyOriginInfo origin; int k = 0; for (; k <= i; ++k) { - // Derive - xprv.Derive(xprv, m_path.at(k)); // Add to the path origin.path.push_back(m_path.at(k)); - // First derivation element, get the fingerprint for origin - if (k == 0) { - std::copy(xprv.vchFingerprint, xprv.vchFingerprint + 4, origin.fingerprint); - } } // Build the remaining path KeyPath end_path; for (; k < (int)m_path.size(); ++k) { end_path.push_back(m_path.at(k)); } + // Get the fingerprint + CKeyID id = m_root_extkey.pubkey.GetID(); + std::copy(id.begin(), id.begin() + 4, origin.fingerprint); + + CExtPubKey xpub; + CExtKey lh_xprv; + // If we have the cache, just get the parent xpub + if (cache != nullptr) { + cache->GetCachedLastHardenedExtPubKey(m_expr_index, xpub); + } + if (!xpub.pubkey.IsValid()) { + // Cache miss, or nor cache, or need privkey + CExtKey xprv; + if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return false; + xpub = lh_xprv.Neuter(); + } + assert(xpub.pubkey.IsValid()); + // Build the string std::string origin_str = HexStr(origin.fingerprint) + FormatHDKeypath(origin.path); - out = "[" + origin_str + "]" + (priv ? EncodeExtKey(xprv) : EncodeExtPubKey(xprv.Neuter())) + FormatHDKeypath(end_path); + out = "[" + origin_str + "]" + EncodeExtPubKey(xpub) + FormatHDKeypath(end_path); if (IsRange()) { out += "/*"; assert(m_derive == DeriveType::UNHARDENED); @@ -465,7 +473,8 @@ public: bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override { CExtKey extkey; - if (!GetDerivedExtKey(arg, extkey)) return false; + CExtKey dummy; + if (!GetDerivedExtKey(arg, extkey, dummy)) return false; if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos); if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL); key = extkey.key; @@ -508,6 +517,13 @@ public: DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {} DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::vector<std::unique_ptr<DescriptorImpl>> scripts, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(std::move(scripts)) {} + enum class StringType + { + PUBLIC, + PRIVATE, + NORMALIZED, + }; + bool IsSolvable() const override { for (const auto& arg : m_subdescriptor_args) { @@ -527,19 +543,19 @@ public: return false; } - virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const + virtual bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const { size_t pos = 0; for (const auto& scriptarg : m_subdescriptor_args) { if (pos++) ret += ","; std::string tmp; - if (!scriptarg->ToStringHelper(arg, tmp, priv, normalized)) return false; + if (!scriptarg->ToStringHelper(arg, tmp, type, cache)) return false; ret += std::move(tmp); } return true; } - bool ToStringHelper(const SigningProvider* arg, std::string& out, bool priv, bool normalized) const + bool ToStringHelper(const SigningProvider* arg, std::string& out, const StringType type, const DescriptorCache* cache = nullptr) const { std::string extra = ToStringExtra(); size_t pos = extra.size() > 0 ? 1 : 0; @@ -547,17 +563,21 @@ public: for (const auto& pubkey : m_pubkey_args) { if (pos++) ret += ","; std::string tmp; - if (normalized) { - if (!pubkey->ToNormalizedString(*arg, tmp, priv)) return false; - } else if (priv) { - if (!pubkey->ToPrivateString(*arg, tmp)) return false; - } else { - tmp = pubkey->ToString(); + switch (type) { + case StringType::NORMALIZED: + if (!pubkey->ToNormalizedString(*arg, tmp, cache)) return false; + break; + case StringType::PRIVATE: + if (!pubkey->ToPrivateString(*arg, tmp)) return false; + break; + case StringType::PUBLIC: + tmp = pubkey->ToString(); + break; } ret += std::move(tmp); } std::string subscript; - if (!ToStringSubScriptHelper(arg, subscript, priv, normalized)) return false; + if (!ToStringSubScriptHelper(arg, subscript, type, cache)) return false; if (pos && subscript.size()) ret += ','; out = std::move(ret) + std::move(subscript) + ")"; return true; @@ -566,20 +586,20 @@ public: std::string ToString() const final { std::string ret; - ToStringHelper(nullptr, ret, false, false); + ToStringHelper(nullptr, ret, StringType::PUBLIC); return AddChecksum(ret); } bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { - bool ret = ToStringHelper(&arg, out, true, false); + bool ret = ToStringHelper(&arg, out, StringType::PRIVATE); out = AddChecksum(out); return ret; } - bool ToNormalizedString(const SigningProvider& arg, std::string& out, bool priv) const override final + bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override final { - bool ret = ToStringHelper(&arg, out, priv, true); + bool ret = ToStringHelper(&arg, out, StringType::NORMALIZED, cache); out = AddChecksum(out); return ret; } @@ -833,7 +853,7 @@ protected: out.tr_spenddata[output].Merge(builder.GetSpendData()); return Vector(GetScriptForDestination(output)); } - bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override + bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, const StringType type, const DescriptorCache* cache = nullptr) const override { if (m_depths.empty()) return true; std::vector<bool> path; @@ -844,7 +864,7 @@ protected: path.push_back(false); } std::string tmp; - if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, priv, normalized)) return false; + if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, type, cache)) return false; ret += std::move(tmp); while (!path.empty() && path.back()) { if (path.size() > 1) ret += '}'; @@ -1411,6 +1431,11 @@ void DescriptorCache::CacheDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_i xpubs[der_index] = xpub; } +void DescriptorCache::CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub) +{ + m_last_hardened_xpubs[key_exp_pos] = xpub; +} + bool DescriptorCache::GetCachedParentExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const { const auto& it = m_parent_xpubs.find(key_exp_pos); @@ -1429,6 +1454,55 @@ bool DescriptorCache::GetCachedDerivedExtPubKey(uint32_t key_exp_pos, uint32_t d return true; } +bool DescriptorCache::GetCachedLastHardenedExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const +{ + const auto& it = m_last_hardened_xpubs.find(key_exp_pos); + if (it == m_last_hardened_xpubs.end()) return false; + xpub = it->second; + return true; +} + +DescriptorCache DescriptorCache::MergeAndDiff(const DescriptorCache& other) +{ + DescriptorCache diff; + for (const auto& parent_xpub_pair : other.GetCachedParentExtPubKeys()) { + CExtPubKey xpub; + if (GetCachedParentExtPubKey(parent_xpub_pair.first, xpub)) { + if (xpub != parent_xpub_pair.second) { + throw std::runtime_error(std::string(__func__) + ": New cached parent xpub does not match already cached parent xpub"); + } + continue; + } + CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second); + diff.CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second); + } + for (const auto& derived_xpub_map_pair : other.GetCachedDerivedExtPubKeys()) { + for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) { + CExtPubKey xpub; + if (GetCachedDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, xpub)) { + if (xpub != derived_xpub_pair.second) { + throw std::runtime_error(std::string(__func__) + ": New cached derived xpub does not match already cached derived xpub"); + } + continue; + } + CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second); + diff.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second); + } + } + for (const auto& lh_xpub_pair : other.GetCachedLastHardenedExtPubKeys()) { + CExtPubKey xpub; + if (GetCachedLastHardenedExtPubKey(lh_xpub_pair.first, xpub)) { + if (xpub != lh_xpub_pair.second) { + throw std::runtime_error(std::string(__func__) + ": New cached last hardened xpub does not match already cached last hardened xpub"); + } + continue; + } + CacheLastHardenedExtPubKey(lh_xpub_pair.first, lh_xpub_pair.second); + diff.CacheLastHardenedExtPubKey(lh_xpub_pair.first, lh_xpub_pair.second); + } + return diff; +} + const ExtPubKeyMap DescriptorCache::GetCachedParentExtPubKeys() const { return m_parent_xpubs; @@ -1438,3 +1512,8 @@ const std::unordered_map<uint32_t, ExtPubKeyMap> DescriptorCache::GetCachedDeriv { return m_derived_xpubs; } + +const ExtPubKeyMap DescriptorCache::GetCachedLastHardenedExtPubKeys() const +{ + return m_last_hardened_xpubs; +} diff --git a/src/script/descriptor.h b/src/script/descriptor.h index 332ae2f230..ecd7c4eea5 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -22,6 +22,8 @@ private: std::unordered_map<uint32_t, ExtPubKeyMap> m_derived_xpubs; /** Map key expression index -> parent xpub */ ExtPubKeyMap m_parent_xpubs; + /** Map key expression index -> last hardened xpub */ + ExtPubKeyMap m_last_hardened_xpubs; public: /** Cache a parent xpub @@ -50,11 +52,30 @@ public: * @param[in] xpub The CExtPubKey to get from cache */ bool GetCachedDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_index, CExtPubKey& xpub) const; + /** Cache a last hardened xpub + * + * @param[in] key_exp_pos Position of the key expression within the descriptor + * @param[in] xpub The CExtPubKey to cache + */ + void CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub); + /** Retrieve a cached last hardened xpub + * + * @param[in] key_exp_pos Position of the key expression within the descriptor + * @param[in] xpub The CExtPubKey to get from cache + */ + bool GetCachedLastHardenedExtPubKey(uint32_t key_exp_pos, CExtPubKey& xpub) const; /** Retrieve all cached parent xpubs */ const ExtPubKeyMap GetCachedParentExtPubKeys() const; /** Retrieve all cached derived xpubs */ const std::unordered_map<uint32_t, ExtPubKeyMap> GetCachedDerivedExtPubKeys() const; + /** Retrieve all cached last hardened xpubs */ + const ExtPubKeyMap GetCachedLastHardenedExtPubKeys() const; + + /** Combine another DescriptorCache into this one. + * Returns a cache containing the items from the other cache unknown to current cache + */ + DescriptorCache MergeAndDiff(const DescriptorCache& other); }; /** \brief Interface for parsed descriptor objects. @@ -94,7 +115,7 @@ struct Descriptor { virtual bool ToPrivateString(const SigningProvider& provider, std::string& out) const = 0; /** Convert the descriptor to a normalized string. Normalized descriptors have the xpub at the last hardened step. This fails if the provided provider does not have the private keys to derive that xpub. */ - virtual bool ToNormalizedString(const SigningProvider& provider, std::string& out, bool priv) const = 0; + virtual bool ToNormalizedString(const SigningProvider& provider, std::string& out, const DescriptorCache* cache = nullptr) const = 0; /** Expand a descriptor at a specified position. * diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 8cebbc5d10..ef48f89965 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1740,9 +1740,9 @@ bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLoc if (nLockTime > (int64_t)txTo->nLockTime) return false; - // Finally the nLockTime feature can be disabled and thus - // CHECKLOCKTIMEVERIFY bypassed if every txin has been - // finalized by setting nSequence to maxint. The + // Finally the nLockTime feature can be disabled in IsFinalTx() + // and thus CHECKLOCKTIMEVERIFY bypassed if every txin has + // been finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making // the opcode ineffective. // diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 36e2dac3ff..8553f80a17 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -124,14 +124,10 @@ void DoCheck(const std::string& prv, const std::string& pub, const std::string& // Check that private can produce the normalized descriptors std::string norm1; - BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1, false)); + BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1)); BOOST_CHECK(EqualDescriptor(norm1, norm_pub)); - BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1, false)); + BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1)); BOOST_CHECK(EqualDescriptor(norm1, norm_pub)); - BOOST_CHECK(parse_priv->ToNormalizedString(keys_priv, norm1, true)); - BOOST_CHECK(EqualDescriptor(norm1, norm_prv)); - BOOST_CHECK(parse_pub->ToNormalizedString(keys_priv, norm1, true)); - BOOST_CHECK(EqualDescriptor(norm1, norm_prv)); // Check whether IsRange on both returns the expected result BOOST_CHECK_EQUAL(parse_pub->IsRange(), (flags & RANGE) != 0); diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index a7770c90e8..bfa977520b 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -32,5 +32,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file) return; } FlatFilePos flat_file_pos; - g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr); + g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr); } diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp index 42a7c7798c..44779f7d7c 100644 --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor) auto* orig_tip = active.Tip(); for (int i = 0; i < 10; ++i) { BlockValidationState state; - m_node.chainman->ActiveChainstate().InvalidateBlock(state, Params(), active.Tip()); + m_node.chainman->ActiveChainstate().InvalidateBlock(state, active.Tip()); } BOOST_CHECK_EQUAL(active.Height(), orig_tip->nHeight - 10); coinbaseKey.MakeNewKey(true); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index f71d9148b6..748272bb1d 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -186,12 +186,12 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk()); m_node.chainman->ActiveChainstate().InitCoinsCache(1 << 23); assert(m_node.chainman->ActiveChainstate().CanFlushToDisk()); - if (!m_node.chainman->ActiveChainstate().LoadGenesisBlock(chainparams)) { + if (!m_node.chainman->ActiveChainstate().LoadGenesisBlock()) { throw std::runtime_error("LoadGenesisBlock failed."); } BlockValidationState state; - if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state, chainparams)) { + if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString())); } diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 0b912acb08..7c1db9d4b9 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -31,7 +31,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) CTxMemPool& mempool = *m_node.mempool; std::vector<CChainState*> chainstates; - const CChainParams& chainparams = Params(); BOOST_CHECK(!manager.SnapshotBlockhash().has_value()); @@ -76,9 +75,9 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23)); // Unlike c1, which doesn't have any blocks. Gets us different tip, height. - c2.LoadGenesisBlock(chainparams); + c2.LoadGenesisBlock(); BlockValidationState _; - BOOST_CHECK(c2.ActivateBestChain(_, chainparams, nullptr)); + BOOST_CHECK(c2.ActivateBestChain(_, nullptr)); BOOST_CHECK(manager.IsSnapshotActive()); BOOST_CHECK(!manager.IsSnapshotValidated()); @@ -138,7 +137,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) { LOCK(::cs_main); c1.InitCoinsCache(1 << 23); - BOOST_REQUIRE(c1.LoadGenesisBlock(Params())); + BOOST_REQUIRE(c1.LoadGenesisBlock()); c1.CoinsTip().SetBestBlock(InsecureRand256()); manager.MaybeRebalanceCaches(); } @@ -156,7 +155,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches) { LOCK(::cs_main); c2.InitCoinsCache(1 << 23); - BOOST_REQUIRE(c2.LoadGenesisBlock(Params())); + BOOST_REQUIRE(c2.LoadGenesisBlock()); c2.CoinsTip().SetBestBlock(InsecureRand256()); manager.MaybeRebalanceCaches(); } diff --git a/src/validation.cpp b/src/validation.cpp index b48e49a10b..df0ec3bd4f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1124,7 +1124,7 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp } // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits BlockValidationState state_dummy; - active_chainstate.FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC); + active_chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); return result; } @@ -1210,6 +1210,7 @@ void CoinsViews::InitCache() CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash) : m_mempool(mempool), + m_params(::Params()), m_blockman(blockman), m_from_snapshot_blockhash(from_snapshot_blockhash) {} @@ -1724,7 +1725,7 @@ static int64_t nBlocksTotal = 0; * Validity checks that depend on the UTXO set are also done; ConnectBlock() * can fail if those validity checks fail (among other reasons). */ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, - CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck) + CCoinsViewCache& view, bool fJustCheck) { AssertLockHeld(cs_main); assert(pindex); @@ -1744,7 +1745,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // is enforced in ContextualCheckBlockHeader(); we wouldn't want to // re-enforce that rule here (at least until we make it impossible for // GetAdjustedTime() to go backward). - if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) { + if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) { if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) { // We don't write down blocks to disk if they may have been // corrupted, so this should be impossible unless we're having hardware @@ -1762,7 +1763,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) - if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { + if (block.GetHash() == m_params.GetConsensus().hashGenesisBlock) { if (!fJustCheck) view.SetBestBlock(pindex->GetBlockHash()); return true; @@ -1794,7 +1795,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // artificially set the default assumed verified block further back. // The test against nMinimumChainWork prevents the skipping when denied access to any chain at // least as good as the expected chain. - fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, chainparams.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); + fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2); } } } @@ -1874,9 +1875,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // be reset before it reaches block 1,983,702 and starts doing unnecessary // BIP30 checking again. assert(pindex->pprev); - CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height); + CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(m_params.GetConsensus().BIP34Height); //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond. - fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash)); + fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == m_params.GetConsensus().BIP34Hash)); // TODO: Remove BIP30 checking from block height 1,983,702 on, once we have a // consensus change that ensures coinbases at those heights can not @@ -1894,12 +1895,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // Start enforcing BIP68 (sequence locks) int nLockTimeFlags = 0; - if (pindex->nHeight >= chainparams.GetConsensus().CSVHeight) { + if (pindex->nHeight >= m_params.GetConsensus().CSVHeight) { nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } // Get the script flags for this block - unsigned int flags = GetBlockScriptFlags(pindex, chainparams.GetConsensus()); + unsigned int flags = GetBlockScriptFlags(pindex, m_params.GetConsensus()); int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal); @@ -1989,7 +1990,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); - CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); + CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, m_params.GetConsensus()); if (block.vtx[0]->GetValueOut() > blockReward) { LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)\n", block.vtx[0]->GetValueOut(), blockReward); return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount"); @@ -2005,8 +2006,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, if (fJustCheck) return true; - if (!WriteUndoDataForBlock(blockundo, state, pindex, chainparams)) + if (!WriteUndoDataForBlock(blockundo, state, pindex, m_params)) { return false; + } if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) { pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); @@ -2059,7 +2061,6 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState( } bool CChainState::FlushStateToDisk( - const CChainParams& chainparams, BlockValidationState &state, FlushStateMode mode, int nManualPruneHeight) @@ -2096,7 +2097,7 @@ bool CChainState::FlushStateToDisk( } else { LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH); - m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload()); + m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload()); fCheckForPruning = false; } if (!setFilesToPrune.empty()) { @@ -2196,20 +2197,19 @@ bool CChainState::FlushStateToDisk( return true; } -void CChainState::ForceFlushStateToDisk() { +void CChainState::ForceFlushStateToDisk() +{ BlockValidationState state; - const CChainParams& chainparams = Params(); - if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { + if (!this->FlushStateToDisk(state, FlushStateMode::ALWAYS)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); } } -void CChainState::PruneAndFlush() { +void CChainState::PruneAndFlush() +{ BlockValidationState state; fCheckForPruning = true; - const CChainParams& chainparams = Params(); - - if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { + if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); } } @@ -2278,7 +2278,7 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C * disconnectpool (note that the caller is responsible for mempool consistency * in any case). */ -bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) +bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) { AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); @@ -2288,8 +2288,9 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& // Read block from disk. std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); CBlock& block = *pblock; - if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus())) + if (!ReadBlockFromDisk(block, pindexDelete, m_params.GetConsensus())) { return error("DisconnectTip(): Failed to read block"); + } // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); { @@ -2302,8 +2303,9 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& } LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED)) + if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) { return false; + } if (disconnectpool) { // Save transactions to re-add to mempool at end of reorg @@ -2320,7 +2322,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& m_chain.SetTip(pindexDelete->pprev); - UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *this); + UpdateTip(m_mempool, pindexDelete->pprev, m_params, *this); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: GetMainSignals().BlockDisconnected(pblock, pindexDelete); @@ -2379,7 +2381,7 @@ public: * * The block is added to connectTrace if connection succeeds. */ -bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) +bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) { AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); @@ -2390,8 +2392,9 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch std::shared_ptr<const CBlock> pthisBlock; if (!pblock) { std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); - if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus())) + if (!ReadBlockFromDisk(*pblockNew, pindexNew, m_params.GetConsensus())) { return AbortNode(state, "Failed to read block"); + } pthisBlock = pblockNew; } else { pthisBlock = pblock; @@ -2403,7 +2406,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); { CCoinsViewCache view(&CoinsTip()); - bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); + bool rv = ConnectBlock(blockConnecting, state, pindexNew, view); GetMainSignals().BlockChecked(blockConnecting, state); if (!rv) { if (state.IsInvalid()) @@ -2419,8 +2422,9 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED)) + if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) { return false; + } int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); // Remove conflicting transactions from the mempool.; @@ -2428,7 +2432,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch disconnectpool.removeForBlock(blockConnecting.vtx); // Update m_chain & related variables. m_chain.SetTip(pindexNew); - UpdateTip(m_mempool, pindexNew, chainparams, *this); + UpdateTip(m_mempool, pindexNew, m_params, *this); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal); @@ -2515,7 +2519,7 @@ void CChainState::PruneBlockIndexCandidates() { * * @returns true unless a system error occurred */ -bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) +bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) { AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); @@ -2527,7 +2531,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai bool fBlocksDisconnected = false; DisconnectedBlockTransactions disconnectpool; while (m_chain.Tip() && m_chain.Tip() != pindexFork) { - if (!DisconnectTip(state, chainparams, &disconnectpool)) { + if (!DisconnectTip(state, &disconnectpool)) { // This is likely a fatal error, but keep the mempool consistent, // just in case. Only remove from the mempool in this case. UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false); @@ -2560,7 +2564,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai // Connect new blocks. for (CBlockIndex* pindexConnect : reverse_iterate(vpindexToConnect)) { - if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { + if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { @@ -2637,7 +2641,8 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) { } } -bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { +bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock) +{ // Note that while we're often called here from ProcessNewBlock, this is // far from a guarantee. Things in the P2P/RPC will often end up calling // us in the middle of ProcessNewBlock - do not assume pblock is set @@ -2683,7 +2688,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar bool fInvalidFound = false; std::shared_ptr<const CBlock> nullBlockPtr; - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) { + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) { // A system error occurred return false; } @@ -2725,17 +2730,17 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar // that the best block hash is non-null. if (ShutdownRequested()) break; } while (pindexNewTip != pindexMostWork); - CheckBlockIndex(chainparams.GetConsensus()); + CheckBlockIndex(); // Write changes periodically to disk, after relay. - if (!FlushStateToDisk(chainparams, state, FlushStateMode::PERIODIC)) { + if (!FlushStateToDisk(state, FlushStateMode::PERIODIC)) { return false; } return true; } -bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) +bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex) { { LOCK(cs_main); @@ -2761,10 +2766,10 @@ bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams& } } - return ActivateBestChain(state, params, std::shared_ptr<const CBlock>()); + return ActivateBestChain(state, std::shared_ptr<const CBlock>()); } -bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) +bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex) { // Genesis block can't be invalidated assert(pindex); @@ -2822,7 +2827,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam // ActivateBestChain considers blocks already in m_chain // unconditionally valid already, so force disconnect away from it. DisconnectedBlockTransactions disconnectpool; - bool ret = DisconnectTip(state, chainparams, &disconnectpool); + bool ret = DisconnectTip(state, &disconnectpool); // DisconnectTip will add transactions to disconnectpool. // Adjust the mempool to be consistent with the new tip, adding // transactions back to the mempool if disconnecting was successful, @@ -2864,7 +2869,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam to_mark_failed = invalid_walk_tip; } - CheckBlockIndex(chainparams.GetConsensus()); + CheckBlockIndex(); { LOCK(cs_main); @@ -2975,7 +2980,7 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block) } /** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */ -void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) +void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos) { pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; @@ -2983,7 +2988,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; - if (IsWitnessEnabled(pindexNew->pprev, consensusParams)) { + if (IsWitnessEnabled(pindexNew->pprev, m_params.GetConsensus())) { pindexNew->nStatus |= BLOCK_OPT_WITNESS; } pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); @@ -3400,7 +3405,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast bool accepted = m_blockman.AcceptBlockHeader( header, state, chainparams, &pindex); - ActiveChainstate().CheckBlockIndex(chainparams.GetConsensus()); + ActiveChainstate().CheckBlockIndex(); if (!accepted) { return false; @@ -3419,7 +3424,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& } /** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ -bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) +bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) { const CBlock& block = *pblock; @@ -3429,8 +3434,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block CBlockIndex *pindexDummy = nullptr; CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; - bool accepted_header = m_blockman.AcceptBlockHeader(block, state, chainparams, &pindex); - CheckBlockIndex(chainparams.GetConsensus()); + bool accepted_header = m_blockman.AcceptBlockHeader(block, state, m_params, &pindex); + CheckBlockIndex(); if (!accepted_header) return false; @@ -3467,8 +3472,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block if (pindex->nChainWork < nMinimumChainWork) return true; } - if (!CheckBlock(block, state, chainparams.GetConsensus()) || - !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) { + if (!CheckBlock(block, state, m_params.GetConsensus()) || + !ContextualCheckBlock(block, state, m_params.GetConsensus(), pindex->pprev)) { if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3484,19 +3489,19 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block // Write block to history file if (fNewBlock) *fNewBlock = true; try { - FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp); + FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp); if (blockPos.IsNull()) { state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__)); return false; } - ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus()); + ReceivedBlockTransactions(block, pindex, blockPos); } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } - FlushStateToDisk(chainparams, state, FlushStateMode::NONE); + FlushStateToDisk(state, FlushStateMode::NONE); - CheckBlockIndex(chainparams.GetConsensus()); + CheckBlockIndex(); return true; } @@ -3522,7 +3527,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s bool ret = CheckBlock(*block, state, chainparams.GetConsensus()); if (ret) { // Store to disk - ret = ActiveChainstate().AcceptBlock(block, state, chainparams, &pindex, force_processing, nullptr, new_block); + ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block); } if (!ret) { GetMainSignals().BlockChecked(*block, state); @@ -3533,8 +3538,9 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s NotifyHeaderTip(ActiveChainstate()); BlockValidationState state; // Only used to report errors, not invalidity - ignore it - if (!ActiveChainstate().ActivateBestChain(state, chainparams, block)) + if (!ActiveChainstate().ActivateBestChain(state, block)) { return error("%s: ActivateBestChain failed (%s)", __func__, state.ToString()); + } return true; } @@ -3563,8 +3569,9 @@ bool TestBlockValidity(BlockValidationState& state, return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString()); if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString()); - if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) + if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, true)) { return false; + } assert(state.IsValid()); return true; @@ -3635,9 +3642,8 @@ void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nM void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight) { BlockValidationState state; - const CChainParams& chainparams = Params(); if (!active_chainstate.FlushStateToDisk( - chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { + state, FlushStateMode::NONE, nManualPruneHeight)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString()); } } @@ -3787,10 +3793,10 @@ void BlockManager::Unload() { m_block_index.clear(); } -bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams) +bool CChainState::LoadBlockIndexDB() { if (!m_blockman.LoadBlockIndex( - chainparams.GetConsensus(), *pblocktree, + m_params.GetConsensus(), *pblocktree, setBlockIndexCandidates)) { return false; } @@ -3850,7 +3856,7 @@ void CChainState::LoadMempool(const ArgsManager& args) m_mempool.SetIsLoaded(!ShutdownRequested()); } -bool CChainState::LoadChainTip(const CChainParams& chainparams) +bool CChainState::LoadChainTip() { AssertLockHeld(cs_main); const CCoinsViewCache& coins_cache = CoinsTip(); @@ -3871,10 +3877,10 @@ bool CChainState::LoadChainTip(const CChainParams& chainparams) tip = m_chain.Tip(); LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", - tip->GetBlockHash().ToString(), - m_chain.Height(), - FormatISO8601DateTime(tip->GetBlockTime()), - GuessVerificationProgress(chainparams.TxData(), tip)); + tip->GetBlockHash().ToString(), + m_chain.Height(), + FormatISO8601DateTime(tip->GetBlockTime()), + GuessVerificationProgress(m_params.TxData(), tip)); return true; } @@ -3985,8 +3991,9 @@ bool CVerifyDB::VerifyDB( CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!chainstate.ConnectBlock(block, state, pindex, coins, chainparams)) + if (!chainstate.ConnectBlock(block, state, pindex, coins)) { return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString()); + } if (ShutdownRequested()) return true; } } @@ -3998,11 +4005,11 @@ bool CVerifyDB::VerifyDB( } /** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */ -bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) +bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs) { // TODO: merge with ConnectBlock CBlock block; - if (!ReadBlockFromDisk(block, pindex, params.GetConsensus())) { + if (!ReadBlockFromDisk(block, pindex, m_params.GetConsensus())) { return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } @@ -4018,7 +4025,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i return true; } -bool CChainState::ReplayBlocks(const CChainParams& params) +bool CChainState::ReplayBlocks() { LOCK(cs_main); @@ -4054,7 +4061,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params) while (pindexOld != pindexFork) { if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. CBlock block; - if (!ReadBlockFromDisk(block, pindexOld, params.GetConsensus())) { + if (!ReadBlockFromDisk(block, pindexOld, m_params.GetConsensus())) { return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); } LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); @@ -4076,7 +4083,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params) const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight); LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); uiInterface.ShowProgress(_("Replaying blocks…").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false); - if (!RollforwardBlock(pindex, cache, params)) return false; + if (!RollforwardBlock(pindex, cache)) return false; } cache.SetBestBlock(pindexNew->GetBlockHash()); @@ -4085,13 +4092,13 @@ bool CChainState::ReplayBlocks(const CChainParams& params) return true; } -bool CChainState::NeedsRedownload(const CChainParams& params) const +bool CChainState::NeedsRedownload() const { AssertLockHeld(cs_main); - // At and above params.SegwitHeight, segwit consensus rules must be validated + // At and above m_params.SegwitHeight, segwit consensus rules must be validated CBlockIndex* block{m_chain.Tip()}; - const int segwit_height{params.GetConsensus().SegwitHeight}; + const int segwit_height{m_params.GetConsensus().SegwitHeight}; while (block != nullptr && block->nHeight >= segwit_height) { if (!(block->nStatus & BLOCK_OPT_WITNESS)) { @@ -4130,13 +4137,13 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman) fHavePruned = false; } -bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams) +bool ChainstateManager::LoadBlockIndex() { AssertLockHeld(cs_main); // Load block index from databases bool needs_init = fReindex; if (!fReindex) { - bool ret = ActiveChainstate().LoadBlockIndexDB(chainparams); + bool ret = ActiveChainstate().LoadBlockIndexDB(); if (!ret) return false; needs_init = m_blockman.m_block_index.empty(); } @@ -4153,7 +4160,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams) return true; } -bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) +bool CChainState::LoadGenesisBlock() { LOCK(cs_main); @@ -4161,16 +4168,16 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) // m_blockman.m_block_index. Note that we can't use m_chain here, since it is // set based on the coins db, not the block index db, which is the only // thing loaded at this point. - if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash())) + if (m_blockman.m_block_index.count(m_params.GenesisBlock().GetHash())) return true; try { - const CBlock& block = chainparams.GenesisBlock(); - FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr); + const CBlock& block = m_params.GenesisBlock(); + FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, m_params, nullptr); if (blockPos.IsNull()) return error("%s: writing genesis block to disk failed", __func__); CBlockIndex *pindex = m_blockman.AddToBlockIndex(block); - ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus()); + ReceivedBlockTransactions(block, pindex, blockPos); } catch (const std::runtime_error& e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } @@ -4178,7 +4185,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) return true; } -void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp) +void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp) { // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent; @@ -4199,11 +4206,12 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f try { // locate a header unsigned char buf[CMessageHeader::MESSAGE_START_SIZE]; - blkdat.FindByte(chainparams.MessageStart()[0]); + blkdat.FindByte(m_params.MessageStart()[0]); nRewind = blkdat.GetPos()+1; blkdat >> buf; - if (memcmp(buf, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) + if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) { continue; + } // read size blkdat >> nSize; if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE) @@ -4227,7 +4235,7 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f { LOCK(cs_main); // detect out of order blocks, and store them for later - if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) { + if (hash != m_params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) { LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), block.hashPrevBlock.ToString()); if (dbp) @@ -4239,21 +4247,21 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash); if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) { BlockValidationState state; - if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { + if (AcceptBlock(pblock, state, nullptr, true, dbp, nullptr)) { nLoaded++; } if (state.IsError()) { break; } - } else if (hash != chainparams.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) { - LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight); + } else if (hash != m_params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) { + LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight); } } // Activate the genesis block so normal node progress can continue - if (hash == chainparams.GetConsensus().hashGenesisBlock) { + if (hash == m_params.GetConsensus().hashGenesisBlock) { BlockValidationState state; - if (!ActivateBestChain(state, chainparams, nullptr)) { + if (!ActivateBestChain(state, nullptr)) { break; } } @@ -4270,14 +4278,12 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f while (range.first != range.second) { std::multimap<uint256, FlatFilePos>::iterator it = range.first; std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>(); - if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus())) - { + if (ReadBlockFromDisk(*pblockrecursive, it->second, m_params.GetConsensus())) { LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(), head.ToString()); LOCK(cs_main); BlockValidationState dummy; - if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) - { + if (AcceptBlock(pblockrecursive, dummy, nullptr, true, &it->second, nullptr)) { nLoaded++; queue.push_back(pblockrecursive->GetHash()); } @@ -4297,7 +4303,7 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); } -void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams) +void CChainState::CheckBlockIndex() { if (!fCheckBlockIndex) { return; @@ -4351,7 +4357,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams) // Begin: actual consistency checks. if (pindex->pprev == nullptr) { // Genesis block checks. - assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. + assert(pindex->GetBlockHash() == m_params.GetConsensus().hashGenesisBlock); // Genesis block's hash must match. assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block. } if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock) @@ -4507,16 +4513,14 @@ bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) this->ToString(), coinstip_size * (1.0 / 1024 / 1024)); BlockValidationState state; - const CChainParams& chainparams = Params(); - bool ret; if (coinstip_size > old_coinstip_size) { // Likely no need to flush if cache sizes have grown. - ret = FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED); + ret = FlushStateToDisk(state, FlushStateMode::IF_NEEDED); } else { // Otherwise, flush state to disk and deallocate the in-memory coins map. - ret = FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS); + ret = FlushStateToDisk(state, FlushStateMode::ALWAYS); CoinsTip().ReallocateCache(); } return ret; @@ -4816,7 +4820,7 @@ bool ChainstateManager::ActivateSnapshot( LOCK(::cs_main); assert(!m_snapshot_chainstate); m_snapshot_chainstate.swap(snapshot_chainstate); - const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip(::Params()); + const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip(); assert(chaintip_loaded); m_active_chainstate = m_snapshot_chainstate.get(); @@ -4879,6 +4883,14 @@ bool ChainstateManager::PopulateAndValidateSnapshot( coins_count - coins_left); return false; } + if (coin.nHeight > base_height || + outpoint.n >= std::numeric_limits<decltype(outpoint.n)>::max() // Avoid integer wrap-around in coinstats.cpp:ApplyHash + ) { + LogPrintf("[snapshot] bad snapshot data after deserializing %d coins\n", + coins_count - coins_left); + return false; + } + coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin)); --coins_left; diff --git a/src/validation.h b/src/validation.h index 5720ba8071..fc702b7183 100644 --- a/src/validation.h +++ b/src/validation.h @@ -595,6 +595,8 @@ protected: //! mempool that is kept in sync with the chain CTxMemPool& m_mempool; + const CChainParams& m_params; + //! Manages the UTXO set, which is a reflection of the contents of `m_chain`. std::unique_ptr<CoinsViews> m_coins_views; @@ -680,7 +682,7 @@ public: EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** Import blocks from an external file */ - void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr); + void LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp = nullptr); /** * Update the on-disk chain state. @@ -694,8 +696,7 @@ public: * @returns true unless a system error occurred */ bool FlushStateToDisk( - const CChainParams& chainparams, - BlockValidationState &state, + BlockValidationState& state, FlushStateMode mode, int nManualPruneHeight = 0); @@ -723,37 +724,36 @@ public: */ bool ActivateBestChain( BlockValidationState& state, - const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(cs_main); - bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Block (dis)connection on a given view: DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view); bool ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, - CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CCoinsViewCache& view, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Apply the effects of a block disconnection on the UTXO set. - bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + bool DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); // Manual block validity manipulation: /** Mark a block as precious and reorganize. * * May not be called in a validationinterface callback. */ - bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + bool PreciousBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); /** Mark a block as invalid. */ - bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + bool InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); /** Remove invalidity status from a block and its descendants. */ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Replay blocks that aren't fully applied to the database. */ - bool ReplayBlocks(const CChainParams& params); + bool ReplayBlocks(); /** Whether the chain state needs to be redownloaded due to lack of witness data */ - [[nodiscard]] bool NeedsRedownload(const CChainParams& params) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); + [[nodiscard]] bool NeedsRedownload() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ - bool LoadGenesisBlock(const CChainParams& chainparams); + bool LoadGenesisBlock(); void PruneBlockIndexCandidates(); @@ -767,13 +767,13 @@ public: * * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex. */ - void CheckBlockIndex(const Consensus::Params& consensusParams); + void CheckBlockIndex(); /** Load the persisted mempool from disk */ void LoadMempool(const ArgsManager& args); /** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */ - bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool LoadChainTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main); //! Dictates whether we need to flush the cache to disk or not. //! @@ -789,19 +789,19 @@ public: std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); private: - bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); - bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + bool ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); - void InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main); - void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main); void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool LoadBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(cs_main); friend ChainstateManager; }; @@ -1002,7 +1002,7 @@ public: bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main); //! Load the block tree and coins database from disk, initializing state if we're running with -reindex - bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main); //! Unload block index and chain data before shutdown. void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 5a832d020b..e33adf94c9 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -475,13 +475,13 @@ public: std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override { return MakeHandler(m_wallet->NotifyAddressBookChanged.connect( - [fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine, - const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); })); + [fn](const CTxDestination& address, const std::string& label, bool is_mine, + const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); })); } std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override { return MakeHandler(m_wallet->NotifyTransactionChanged.connect( - [fn](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); })); + [fn](const uint256& txid, ChangeType status) { fn(txid, status); })); } std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7de12c3a94..ea97b339cf 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1786,8 +1786,6 @@ RPCHelpMan listdescriptors() throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets"); } - EnsureWalletIsUnlocked(*wallet); - LOCK(wallet->cs_wallet); UniValue descriptors(UniValue::VARR); @@ -1801,7 +1799,7 @@ RPCHelpMan listdescriptors() LOCK(desc_spk_man->cs_desc_man); const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor(); std::string descriptor; - if (!desc_spk_man->GetDescriptorString(descriptor, false)) { + if (!desc_spk_man->GetDescriptorString(descriptor)) { throw JSONRPCError(RPC_WALLET_ERROR, "Can't get normalized descriptor string."); } spk.pushKV("desc", descriptor); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index bc5d771b6e..f1d5117415 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3872,7 +3872,7 @@ RPCHelpMan getaddressinfo() DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey)); if (desc_spk_man) { std::string desc_str; - if (desc_spk_man->GetDescriptorString(desc_str, false)) { + if (desc_spk_man->GetDescriptorString(desc_str)) { ret.pushKV("parent_desc", desc_str); } } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 8b397fa1f3..c20950e999 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1805,34 +1805,10 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size) } m_map_pubkeys[pubkey] = i; } - // Write the cache - for (const auto& parent_xpub_pair : temp_cache.GetCachedParentExtPubKeys()) { - CExtPubKey xpub; - if (m_wallet_descriptor.cache.GetCachedParentExtPubKey(parent_xpub_pair.first, xpub)) { - if (xpub != parent_xpub_pair.second) { - throw std::runtime_error(std::string(__func__) + ": New cached parent xpub does not match already cached parent xpub"); - } - continue; - } - if (!batch.WriteDescriptorParentCache(parent_xpub_pair.second, id, parent_xpub_pair.first)) { - throw std::runtime_error(std::string(__func__) + ": writing cache item failed"); - } - m_wallet_descriptor.cache.CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second); - } - for (const auto& derived_xpub_map_pair : temp_cache.GetCachedDerivedExtPubKeys()) { - for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) { - CExtPubKey xpub; - if (m_wallet_descriptor.cache.GetCachedDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, xpub)) { - if (xpub != derived_xpub_pair.second) { - throw std::runtime_error(std::string(__func__) + ": New cached derived xpub does not match already cached derived xpub"); - } - continue; - } - if (!batch.WriteDescriptorDerivedCache(derived_xpub_pair.second, id, derived_xpub_map_pair.first, derived_xpub_pair.first)) { - throw std::runtime_error(std::string(__func__) + ": writing cache item failed"); - } - m_wallet_descriptor.cache.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second); - } + // Merge and write the cache + DescriptorCache new_items = m_wallet_descriptor.cache.MergeAndDiff(temp_cache); + if (!batch.WriteDescriptorCacheItems(id, new_items)) { + throw std::runtime_error(std::string(__func__) + ": writing cache items failed"); } m_max_cached_index++; } @@ -2296,17 +2272,43 @@ const std::vector<CScript> DescriptorScriptPubKeyMan::GetScriptPubKeys() const return script_pub_keys; } -bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, bool priv) const +bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out) const { LOCK(cs_desc_man); - if (m_storage.IsLocked()) { - return false; + + FlatSigningProvider provider; + provider.keys = GetKeys(); + + return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, &m_wallet_descriptor.cache); +} + +void DescriptorScriptPubKeyMan::UpgradeDescriptorCache() +{ + LOCK(cs_desc_man); + if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) { + return; } + // Skip if we have the last hardened xpub cache + if (m_wallet_descriptor.cache.GetCachedLastHardenedExtPubKeys().size() > 0) { + return; + } + + // Expand the descriptor FlatSigningProvider provider; provider.keys = GetKeys(); + FlatSigningProvider out_keys; + std::vector<CScript> scripts_temp; + DescriptorCache temp_cache; + if (!m_wallet_descriptor.descriptor->Expand(0, provider, scripts_temp, out_keys, &temp_cache)){ + throw std::runtime_error("Unable to expand descriptor"); + } - return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, priv); + // Cache the last hardened xpubs + DescriptorCache diff = m_wallet_descriptor.cache.MergeAndDiff(temp_cache); + if (!WalletBatch(m_storage.GetDatabase()).WriteDescriptorCacheItems(GetID(), diff)) { + throw std::runtime_error(std::string(__func__) + ": writing cache items failed"); + } } void DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor) diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index f2d1d87d55..6ed4a7a541 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -632,7 +632,9 @@ public: const WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); const std::vector<CScript> GetScriptPubKeys() const; - bool GetDescriptorString(std::string& out, bool priv) const; + bool GetDescriptorString(std::string& out) const; + + void UpgradeDescriptorCache(); }; #endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 521708e69c..c506bc6255 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -374,6 +374,19 @@ void CWallet::UpgradeKeyMetadata() SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA); } +void CWallet::UpgradeDescriptorCache() +{ + if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) { + return; + } + + for (ScriptPubKeyMan* spkm : GetAllScriptPubKeyMans()) { + DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm); + desc_spkm->UpgradeDescriptorCache(); + } + SetWalletFlag(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED); +} + bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys) { CCrypter crypter; @@ -390,6 +403,8 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_key if (Unlock(_vMasterKey, accept_no_keys)) { // Now that we've unlocked, upgrade the key metadata UpgradeKeyMetadata(); + // Now that we've unlocked, upgrade the descriptor cache + UpgradeDescriptorCache(); return true; } } @@ -801,7 +816,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) success = false; } - NotifyTransactionChanged(this, originalHash, CT_UPDATED); + NotifyTransactionChanged(originalHash, CT_UPDATED); return success; } @@ -930,7 +945,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio wtx.MarkDirty(); // Notify UI of new or updated transaction - NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED); #if HAVE_SYSTEM // notify an external script when a wallet transaction comes in or is updated @@ -1104,7 +1119,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) wtx.setAbandoned(); wtx.MarkDirty(); batch.WriteTx(wtx); - NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED); + NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED); // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); while (iter != mapTxSpends.end() && iter->first.hash == now) { @@ -1944,7 +1959,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve for (const CTxIn& txin : tx->vin) { CWalletTx &coin = mapWallet.at(txin.prevout.hash); coin.MarkDirty(); - NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + NotifyTransactionChanged(coin.GetHash(), CT_UPDATED); } // Get the inserted-CWalletTx from mapWallet so that the @@ -1999,7 +2014,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 for (const auto& txin : it->second.tx->vin) mapTxSpends.erase(txin.prevout); mapWallet.erase(it); - NotifyTransactionChanged(this, hash, CT_DELETED); + NotifyTransactionChanged(hash, CT_DELETED); } if (nZapSelectTxRet == DBErrors::NEED_REWRITE) @@ -2033,8 +2048,8 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add m_address_book[address].purpose = strPurpose; is_mine = IsMine(address) != ISMINE_NO; } - NotifyAddressBookChanged(this, address, strName, is_mine, - strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); + NotifyAddressBookChanged(address, strName, is_mine, + strPurpose, (fUpdated ? CT_UPDATED : CT_NEW)); if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose)) return false; return batch.WriteName(EncodeDestination(address), strName); @@ -2069,7 +2084,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address) is_mine = IsMine(address) != ISMINE_NO; } - NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED); + NotifyAddressBookChanged(address, "", is_mine, "", CT_DELETED); batch.ErasePurpose(EncodeDestination(address)); return batch.EraseName(EncodeDestination(address)); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 001b94047a..3997751f52 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -117,6 +117,7 @@ static constexpr uint64_t KNOWN_WALLET_FLAGS = WALLET_FLAG_AVOID_REUSE | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_KEY_ORIGIN_METADATA + | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED | WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_DESCRIPTORS | WALLET_FLAG_EXTERNAL_SIGNER; @@ -128,6 +129,7 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{ {"avoid_reuse", WALLET_FLAG_AVOID_REUSE}, {"blank", WALLET_FLAG_BLANK_WALLET}, {"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA}, + {"last_hardened_xpub_cached", WALLET_FLAG_LAST_HARDENED_XPUB_CACHED}, {"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS}, {"descriptor_wallet", WALLET_FLAG_DESCRIPTORS}, {"external_signer", WALLET_FLAG_EXTERNAL_SIGNER} @@ -476,6 +478,9 @@ public: //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Upgrade DescriptorCaches + void UpgradeDescriptorCache() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; } //! Adds a destination data tuple to the store, without saving it to disk @@ -724,19 +729,18 @@ public: /** * Address book entry changed. - * @note called with lock cs_wallet held. + * @note called without lock cs_wallet held. */ - boost::signals2::signal<void (CWallet *wallet, const CTxDestination - &address, const std::string &label, bool isMine, - const std::string &purpose, - ChangeType status)> NotifyAddressBookChanged; + boost::signals2::signal<void(const CTxDestination& address, + const std::string& label, bool isMine, + const std::string& purpose, ChangeType status)> + NotifyAddressBookChanged; /** * Wallet transaction added, removed or updated. * @note called with lock cs_wallet held. */ - boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx, - ChangeType status)> NotifyTransactionChanged; + boost::signals2::signal<void(const uint256& hashTx, ChangeType status)> NotifyTransactionChanged; /** Show progress e.g. for rescan */ boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 203fca8dd6..1e5d8dfa3a 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -52,6 +52,7 @@ const std::string TX{"tx"}; const std::string VERSION{"version"}; const std::string WALLETDESCRIPTOR{"walletdescriptor"}; const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"}; +const std::string WALLETDESCRIPTORLHCACHE{"walletdescriptorlhcache"}; const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"}; const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"}; const std::string WATCHMETA{"watchmeta"}; @@ -254,6 +255,35 @@ bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint2 return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub); } +bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index) +{ + std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE); + xpub.Encode(ser_xpub.data()); + return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub); +} + +bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache) +{ + for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) { + if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) { + return false; + } + } + for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) { + for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) { + if (!WriteDescriptorDerivedCache(derived_xpub_pair.second, desc_id, derived_xpub_map_pair.first, derived_xpub_pair.first)) { + return false; + } + } + } + for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) { + if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) { + return false; + } + } + return true; +} + class CWalletScanState { public: unsigned int nKeys{0}; @@ -608,6 +638,17 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else { wss.m_descriptor_caches[desc_id].CacheDerivedExtPubKey(key_exp_index, der_index, xpub); } + } else if (strType == DBKeys::WALLETDESCRIPTORLHCACHE) { + uint256 desc_id; + uint32_t key_exp_index; + ssKey >> desc_id; + ssKey >> key_exp_index; + + std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE); + ssValue >> ser_xpub; + CExtPubKey xpub; + xpub.Decode(ser_xpub.data()); + wss.m_descriptor_caches[desc_id].CacheLastHardenedExtPubKey(key_exp_index, xpub); } else if (strType == DBKeys::WALLETDESCRIPTORKEY) { uint256 desc_id; CPubKey pubkey; @@ -849,6 +890,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) result = DBErrors::CORRUPT; } + // Upgrade all of the descriptor caches to cache the last hardened xpub + // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible + try { + pwallet->UpgradeDescriptorCache(); + } catch (...) { + result = DBErrors::CORRUPT; + } + // Set the inactive chain if (wss.m_hd_chains.size() > 0) { LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan(); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index d740aaadb3..9b775eb481 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -246,6 +246,8 @@ public: bool WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor); bool WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index); bool WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index); + bool WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index); + bool WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache); /// Write destination data key,value tuple to database bool WriteDestData(const std::string &address, const std::string &key, const std::string &value); diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index 0713f768c1..c75e1759bc 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -43,6 +43,9 @@ enum WalletFlags : uint64_t { // Indicates that the metadata has already been upgraded to contain key origins WALLET_FLAG_KEY_ORIGIN_METADATA = (1ULL << 1), + // Indicates that the descriptor cache has been upgraded to cache last hardened xpubs + WALLET_FLAG_LAST_HARDENED_XPUB_CACHED = (1ULL << 2), + // will enforce the rule that the wallet can't contain any private keys (only watch-only/pubkeys) WALLET_FLAG_DISABLE_PRIVATE_KEYS = (1ULL << 32), |