diff options
Diffstat (limited to 'src')
41 files changed, 1475 insertions, 727 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8fc7f61d4b..1ef62a656d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -484,6 +484,7 @@ libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ + compat/glibc_sanity_fdelt.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 7540122418..a8d3154107 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -6,93 +6,7 @@ bin_PROGRAMS += qt/bitcoin-qt EXTRA_LIBRARIES += qt/libbitcoinqt.a # bitcoin qt core # -QT_TS = \ - qt/locale/bitcoin_af.ts \ - qt/locale/bitcoin_af_ZA.ts \ - qt/locale/bitcoin_ar.ts \ - qt/locale/bitcoin_be_BY.ts \ - qt/locale/bitcoin_bg_BG.ts \ - qt/locale/bitcoin_bg.ts \ - qt/locale/bitcoin_ca_ES.ts \ - qt/locale/bitcoin_ca.ts \ - qt/locale/bitcoin_ca@valencia.ts \ - qt/locale/bitcoin_cs.ts \ - qt/locale/bitcoin_cy.ts \ - qt/locale/bitcoin_da.ts \ - qt/locale/bitcoin_de.ts \ - qt/locale/bitcoin_el_GR.ts \ - qt/locale/bitcoin_el.ts \ - qt/locale/bitcoin_en_GB.ts \ - qt/locale/bitcoin_en.ts \ - qt/locale/bitcoin_eo.ts \ - qt/locale/bitcoin_es_AR.ts \ - qt/locale/bitcoin_es_CL.ts \ - qt/locale/bitcoin_es_CO.ts \ - qt/locale/bitcoin_es_DO.ts \ - qt/locale/bitcoin_es_ES.ts \ - qt/locale/bitcoin_es_MX.ts \ - qt/locale/bitcoin_es.ts \ - qt/locale/bitcoin_es_UY.ts \ - qt/locale/bitcoin_es_VE.ts \ - qt/locale/bitcoin_et_EE.ts \ - qt/locale/bitcoin_et.ts \ - qt/locale/bitcoin_eu_ES.ts \ - qt/locale/bitcoin_fa_IR.ts \ - qt/locale/bitcoin_fa.ts \ - qt/locale/bitcoin_fi.ts \ - qt/locale/bitcoin_fr_CA.ts \ - qt/locale/bitcoin_fr_FR.ts \ - qt/locale/bitcoin_fr.ts \ - qt/locale/bitcoin_gl.ts \ - qt/locale/bitcoin_he.ts \ - qt/locale/bitcoin_hi_IN.ts \ - qt/locale/bitcoin_hr.ts \ - qt/locale/bitcoin_hu.ts \ - qt/locale/bitcoin_id_ID.ts \ - qt/locale/bitcoin_it_IT.ts \ - qt/locale/bitcoin_it.ts \ - qt/locale/bitcoin_ja.ts \ - qt/locale/bitcoin_ka.ts \ - qt/locale/bitcoin_kk_KZ.ts \ - qt/locale/bitcoin_ko_KR.ts \ - qt/locale/bitcoin_ku_IQ.ts \ - qt/locale/bitcoin_ky.ts \ - qt/locale/bitcoin_la.ts \ - qt/locale/bitcoin_lt.ts \ - qt/locale/bitcoin_lv_LV.ts \ - qt/locale/bitcoin_mk_MK.ts \ - qt/locale/bitcoin_mn.ts \ - qt/locale/bitcoin_ms_MY.ts \ - qt/locale/bitcoin_nb.ts \ - qt/locale/bitcoin_ne.ts \ - qt/locale/bitcoin_nl.ts \ - qt/locale/bitcoin_pam.ts \ - qt/locale/bitcoin_pl.ts \ - qt/locale/bitcoin_pt_BR.ts \ - qt/locale/bitcoin_pt_PT.ts \ - qt/locale/bitcoin_ro_RO.ts \ - qt/locale/bitcoin_ro.ts \ - qt/locale/bitcoin_ru_RU.ts \ - qt/locale/bitcoin_ru.ts \ - qt/locale/bitcoin_sk.ts \ - qt/locale/bitcoin_sl_SI.ts \ - qt/locale/bitcoin_sq.ts \ - qt/locale/bitcoin_sr@latin.ts \ - qt/locale/bitcoin_sr.ts \ - qt/locale/bitcoin_sv.ts \ - qt/locale/bitcoin_ta.ts \ - qt/locale/bitcoin_th_TH.ts \ - qt/locale/bitcoin_tr_TR.ts \ - qt/locale/bitcoin_tr.ts \ - qt/locale/bitcoin_uk.ts \ - qt/locale/bitcoin_ur_PK.ts \ - qt/locale/bitcoin_uz@Cyrl.ts \ - qt/locale/bitcoin_vi.ts \ - qt/locale/bitcoin_vi_VN.ts \ - qt/locale/bitcoin_zh_CN.ts \ - qt/locale/bitcoin_zh_HK.ts \ - qt/locale/bitcoin_zh.ts \ - qt/locale/bitcoin_zh_TW.ts +include Makefile.qt_locale.include QT_FORMS_UI = \ qt/forms/addressbookpage.ui \ diff --git a/src/Makefile.qt_locale.include b/src/Makefile.qt_locale.include new file mode 100644 index 0000000000..d9f23e8da2 --- /dev/null +++ b/src/Makefile.qt_locale.include @@ -0,0 +1,87 @@ +QT_TS = \ + qt/locale/bitcoin_af.ts \ + qt/locale/bitcoin_af_ZA.ts \ + qt/locale/bitcoin_ar.ts \ + qt/locale/bitcoin_be_BY.ts \ + qt/locale/bitcoin_bg_BG.ts \ + qt/locale/bitcoin_bg.ts \ + qt/locale/bitcoin_ca_ES.ts \ + qt/locale/bitcoin_ca.ts \ + qt/locale/bitcoin_ca@valencia.ts \ + qt/locale/bitcoin_cs.ts \ + qt/locale/bitcoin_cy.ts \ + qt/locale/bitcoin_da.ts \ + qt/locale/bitcoin_de.ts \ + qt/locale/bitcoin_el_GR.ts \ + qt/locale/bitcoin_el.ts \ + qt/locale/bitcoin_en_GB.ts \ + qt/locale/bitcoin_en.ts \ + qt/locale/bitcoin_eo.ts \ + qt/locale/bitcoin_es_AR.ts \ + qt/locale/bitcoin_es_CL.ts \ + qt/locale/bitcoin_es_CO.ts \ + qt/locale/bitcoin_es_DO.ts \ + qt/locale/bitcoin_es_ES.ts \ + qt/locale/bitcoin_es_MX.ts \ + qt/locale/bitcoin_es.ts \ + qt/locale/bitcoin_es_UY.ts \ + qt/locale/bitcoin_es_VE.ts \ + qt/locale/bitcoin_et_EE.ts \ + qt/locale/bitcoin_et.ts \ + qt/locale/bitcoin_eu_ES.ts \ + qt/locale/bitcoin_fa_IR.ts \ + qt/locale/bitcoin_fa.ts \ + qt/locale/bitcoin_fi.ts \ + qt/locale/bitcoin_fr_CA.ts \ + qt/locale/bitcoin_fr_FR.ts \ + qt/locale/bitcoin_fr.ts \ + qt/locale/bitcoin_gl.ts \ + qt/locale/bitcoin_he.ts \ + qt/locale/bitcoin_hi_IN.ts \ + qt/locale/bitcoin_hr.ts \ + qt/locale/bitcoin_hu.ts \ + qt/locale/bitcoin_id_ID.ts \ + qt/locale/bitcoin_it_IT.ts \ + qt/locale/bitcoin_it.ts \ + qt/locale/bitcoin_ja.ts \ + qt/locale/bitcoin_ka.ts \ + qt/locale/bitcoin_kk_KZ.ts \ + qt/locale/bitcoin_ko_KR.ts \ + qt/locale/bitcoin_ku_IQ.ts \ + qt/locale/bitcoin_ky.ts \ + qt/locale/bitcoin_la.ts \ + qt/locale/bitcoin_lt.ts \ + qt/locale/bitcoin_lv_LV.ts \ + qt/locale/bitcoin_mk_MK.ts \ + qt/locale/bitcoin_mn.ts \ + qt/locale/bitcoin_ms_MY.ts \ + qt/locale/bitcoin_nb.ts \ + qt/locale/bitcoin_ne.ts \ + qt/locale/bitcoin_nl.ts \ + qt/locale/bitcoin_pam.ts \ + qt/locale/bitcoin_pl.ts \ + qt/locale/bitcoin_pt_BR.ts \ + qt/locale/bitcoin_pt_PT.ts \ + qt/locale/bitcoin_ro_RO.ts \ + qt/locale/bitcoin_ro.ts \ + qt/locale/bitcoin_ru_RU.ts \ + qt/locale/bitcoin_ru.ts \ + qt/locale/bitcoin_sk.ts \ + qt/locale/bitcoin_sl_SI.ts \ + qt/locale/bitcoin_sq.ts \ + qt/locale/bitcoin_sr@latin.ts \ + qt/locale/bitcoin_sr.ts \ + qt/locale/bitcoin_sv.ts \ + qt/locale/bitcoin_ta.ts \ + qt/locale/bitcoin_th_TH.ts \ + qt/locale/bitcoin_tr_TR.ts \ + qt/locale/bitcoin_tr.ts \ + qt/locale/bitcoin_uk.ts \ + qt/locale/bitcoin_ur_PK.ts \ + qt/locale/bitcoin_uz@Cyrl.ts \ + qt/locale/bitcoin_vi.ts \ + qt/locale/bitcoin_vi_VN.ts \ + qt/locale/bitcoin_zh_CN.ts \ + qt/locale/bitcoin_zh_HK.ts \ + qt/locale/bitcoin_zh.ts \ + qt/locale/bitcoin_zh_TW.ts diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index 361fedf35a..eb7f0098ec 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -27,7 +27,7 @@ static void SetupWalletToolArgs() gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 83de684a2b..615b955f6e 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -25,24 +25,6 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; -/* Introduction text for doxygen: */ - -/*! \mainpage Developer documentation - * - * \section intro_sec Introduction - * - * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin, - * which enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate - * with no central authority: managing transactions and issuing money are carried out collectively by the network. - * - * The software is a community-driven open source project, released under the MIT license. - * - * See https://github.com/bitcoin/bitcoin and https://bitcoincore.org/ for further information about the project. - * - * \section Navigation - * Use the buttons <code>Namespaces</code>, <code>Classes</code> or <code>Files</code> at the top of the page to start navigating the code. - */ - static void WaitForShutdown() { while (!ShutdownRequested()) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ad766471dc..5964877eb8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -71,6 +71,7 @@ public: consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893 + consensus.MinBIP9WarningHeight = consensus.SegwitHeight + consensus.nMinerConfirmationWindow; consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; @@ -177,6 +178,7 @@ public: consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca + consensus.MinBIP9WarningHeight = consensus.SegwitHeight + consensus.nMinerConfirmationWindow; consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; @@ -261,6 +263,7 @@ public: consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests) consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests) consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden + consensus.MinBIP9WarningHeight = 0; consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; diff --git a/src/coins.h b/src/coins.h index dca1beabb6..d8135e0d9a 100644 --- a/src/coins.h +++ b/src/coins.h @@ -95,8 +95,16 @@ public: * This *must* return size_t. With Boost 1.46 on 32-bit systems the * unordered_map will behave unpredictably if the custom hasher returns a * uint64_t, resulting in failures when syncing the chain (#4634). + * + * Having the hash noexcept allows libstdc++'s unordered_map to recalculate + * the hash during rehash, so it does not have to cache the value. This + * reduces node's memory by sizeof(size_t). The required recalculation has + * a slight performance penalty (around 1.6%), but this is compensated by + * memory savings of about 9% which allow for a larger dbcache setting. + * + * @see https://gcc.gnu.org/onlinedocs/gcc-9.2.0/libstdc++/manual/manual/unordered_associative.html */ - size_t operator()(const COutPoint& id) const { + size_t operator()(const COutPoint& id) const noexcept { return SipHashUint256Extra(k0, k1, id.hash, id.n); } }; diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index 1ef66e27b4..cc74f28899 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,7 +9,7 @@ #include <cstddef> #if defined(HAVE_SYS_SELECT_H) -#include <sys/select.h> +bool sanity_test_fdelt(); #endif extern "C" void* memcpy(void* a, const void* b, size_t c); @@ -41,21 +41,6 @@ bool sanity_test_memcpy() } return true; } - -#if defined(HAVE_SYS_SELECT_H) -// trigger: Call FD_SET to trigger __fdelt_chk. FORTIFY_SOURCE must be defined -// as >0 and optimizations must be set to at least -O2. -// test: Add a file descriptor to an empty fd_set. Verify that it has been -// correctly added. -bool sanity_test_fdelt() -{ - fd_set fds; - FD_ZERO(&fds); - FD_SET(0, &fds); - return FD_ISSET(0, &fds); -} -#endif - } // namespace bool glibc_sanity_test() diff --git a/src/compat/glibc_sanity_fdelt.cpp b/src/compat/glibc_sanity_fdelt.cpp new file mode 100644 index 0000000000..87140d0c71 --- /dev/null +++ b/src/compat/glibc_sanity_fdelt.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#if defined(HAVE_SYS_SELECT_H) +#ifdef HAVE_CSTRING_DEPENDENT_FD_ZERO +#include <cstring> +#endif +#include <sys/select.h> + +// trigger: Call FD_SET to trigger __fdelt_chk. FORTIFY_SOURCE must be defined +// as >0 and optimizations must be set to at least -O2. +// test: Add a file descriptor to an empty fd_set. Verify that it has been +// correctly added. +bool sanity_test_fdelt() +{ + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + return FD_ISSET(0, &fds); +} +#endif diff --git a/src/consensus/params.h b/src/consensus/params.h index 8263b0fef4..2f8c490dc4 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -62,6 +62,9 @@ struct Params { * Note that segwit v0 script rules are enforced on all blocks except the * BIP 16 exception blocks. */ int SegwitHeight; + /** Don't warn about unknown BIP 9 activations below this height. + * This prevents us from warning about the CSV and segwit activations. */ + int MinBIP9WarningHeight; /** * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period, * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments. diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index 23ed3ecb53..00ebbbd1ab 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -18,7 +18,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize"); - // Check for negative or overflow output values + // Check for negative or overflow output values (see CVE-2010-5139) CAmount nValueOut = 0; for (const auto& txout : tx.vout) { diff --git a/src/init.cpp b/src/init.cpp index bb82130542..7c752d615a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1545,7 +1545,7 @@ bool AppInitMain(InitInterfaces& interfaces) } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate - if (!ReplayBlocks(chainparams, &::ChainstateActive().CoinsDB())) { + if (!::ChainstateActive().ReplayBlocks(chainparams)) { strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated; break; } @@ -1557,8 +1557,8 @@ bool AppInitMain(InitInterfaces& interfaces) is_coinsview_empty = fReset || fReindexChainState || ::ChainstateActive().CoinsTip().GetBestBlock().IsNull(); if (!is_coinsview_empty) { - // LoadChainTip sets ::ChainActive() based on CoinsTip()'s best block - if (!LoadChainTip(chainparams)) { + // LoadChainTip initializes the chain based on CoinsTip()'s best block + if (!::ChainstateActive().LoadChainTip(chainparams)) { strLoadError = _("Error initializing block database").translated; break; } diff --git a/src/net.cpp b/src/net.cpp index 89f82aa3d2..63b7833822 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -50,6 +50,9 @@ static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed" // Dump addresses to peers.dat every 15 minutes (900s) static constexpr int DUMP_PEERS_INTERVAL = 15 * 60; +/** Number of DNS seeds to query when the number of connections is low. */ +static constexpr int DNSSEEDS_TO_QUERY_AT_ONCE = 3; + // We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. #define FEELER_SLEEP_WINDOW 1 @@ -1535,35 +1538,41 @@ void StopMapPort() void CConnman::ThreadDNSAddressSeed() { - // goal: only query DNS seeds if address need is acute - // Avoiding DNS seeds when we don't need them improves user privacy by - // creating fewer identifying DNS requests, reduces trust by giving seeds - // less influence on the network topology, and reduces traffic to the seeds. - if ((addrman.size() > 0) && - (!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) { - if (!interruptNet.sleep_for(std::chrono::seconds(11))) - return; + FastRandomContext rng; + std::vector<std::string> seeds = Params().DNSSeeds(); + Shuffle(seeds.begin(), seeds.end(), rng); + int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections + int found = 0; - LOCK(cs_vNodes); - int nRelevant = 0; - for (const CNode* pnode : vNodes) { - nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound; - } - if (nRelevant >= 2) { - LogPrintf("P2P peers available. Skipped DNS seeding.\n"); - return; - } + if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) { + // When -forcednsseed is provided, query all. + seeds_right_now = seeds.size(); } - const std::vector<std::string> &vSeeds = Params().DNSSeeds(); - int found = 0; + for (const std::string& seed : seeds) { + // goal: only query DNS seed if address need is acute + // Avoiding DNS seeds when we don't need them improves user privacy by + // creating fewer identifying DNS requests, reduces trust by giving seeds + // less influence on the network topology, and reduces traffic to the seeds. + if (addrman.size() > 0 && seeds_right_now == 0) { + if (!interruptNet.sleep_for(std::chrono::seconds(11))) return; - LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); + LOCK(cs_vNodes); + int nRelevant = 0; + for (const CNode* pnode : vNodes) { + nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound; + } + if (nRelevant >= 2) { + LogPrintf("P2P peers available. Skipped DNS seeding.\n"); + return; + } + seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE; + } - for (const std::string &seed : vSeeds) { if (interruptNet) { return; } + LogPrintf("Loading addresses from DNS seed %s\n", seed); if (HaveNameProxy()) { AddOneShot(seed); } else { @@ -1576,13 +1585,11 @@ void CConnman::ThreadDNSAddressSeed() continue; } unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed - if (LookupHost(host.c_str(), vIPs, nMaxIPs, true)) - { - for (const CNetAddr& ip : vIPs) - { + if (LookupHost(host.c_str(), vIPs, nMaxIPs, true)) { + for (const CNetAddr& ip : vIPs) { int nOneDay = 24*3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); - addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + addr.nTime = GetTime() - 3*nOneDay - rng.randrange(4*nOneDay); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; } @@ -1593,8 +1600,8 @@ void CConnman::ThreadDNSAddressSeed() AddOneShot(seed); } } + --seeds_right_now; } - LogPrintf("%d addresses found from DNS seeds\n", found); } @@ -282,6 +282,12 @@ public: bool DisconnectNode(const CNetAddr& addr); bool DisconnectNode(NodeId id); + //! Used to convey which local services we are offering peers during node + //! connection. + //! + //! The data returned by this is used in CNode construction, + //! which is used to advertise which services we are offering + //! that peer during `net_processing.cpp:PushNodeVersion()`. ServiceFlags GetLocalServices() const; //!set the max outbound target in bytes @@ -413,7 +419,18 @@ private: std::atomic<NodeId> nLastNodeId{0}; unsigned int nPrevNodeCount{0}; - /** Services this instance offers */ + /** + * Services this instance offers. + * + * This data is replicated in each CNode instance we create during peer + * connection (in ConnectNode()) under a member also called + * nLocalServices. + * + * This data is not marked const, but after being set it should not + * change. See the note in CNode::nLocalServices documentation. + * + * \sa CNode::nLocalServices + */ ServiceFlags nLocalServices; std::unique_ptr<CSemaphore> semOutbound; @@ -786,8 +803,24 @@ public: private: const NodeId id; const uint64_t nLocalHostNonce; - // Services offered to this peer + + //! Services offered to this peer. + //! + //! This is supplied by the parent CConnman during peer connection + //! (CConnman::ConnectNode()) from its attribute of the same name. + //! + //! This is const because there is no protocol defined for renegotiating + //! services initially offered to a peer. The set of local services we + //! offer should not change after initialization. + //! + //! An interesting example of this is NODE_NETWORK and initial block + //! download: a node which starts up from scratch doesn't have any blocks + //! to serve, but still advertises NODE_NETWORK because it will eventually + //! fulfill this role after IBD completes. P2P code is written in such a + //! way that it can gracefully handle peers who don't make good on their + //! service advertisements. const ServiceFlags nLocalServices; + const int nMyStartingHeight; int nSendVersion{0}; NetPermissionFlags m_permissionFlags{ PF_NONE }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7f2fea5584..34d349e8e9 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -415,6 +415,9 @@ static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LO static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime) { + // Note that pnode->GetLocalServices() is a reflection of the local + // services we were offering when the CNode object was created for this + // peer. ServiceFlags nLocalNodeServices = pnode->GetLocalServices(); uint64_t nonce = pnode->GetLocalNonce(); int nNodeStartingHeight = pnode->GetMyStartingHeight(); @@ -2556,7 +2559,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } AddOrphanTx(ptx, pfrom->GetId()); - // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789) unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); if (nEvicted > 0) { diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index c9f17d12ec..2ababb5e1e 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -44,7 +44,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri switch(mode) { case Encrypt: // Ask passphrase x2 - ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.")); + ui->warningLabel->setText(tr("Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.")); ui->passLabel1->hide(); ui->passEdit1->hide(); setWindowTitle(tr("Encrypt wallet")); @@ -67,7 +67,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri break; case ChangePass: // Ask old passphrase + new passphrase x2 setWindowTitle(tr("Change passphrase")); - ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase to the wallet.")); + ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet.")); break; } textChanged(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c672171cfb..7671fde705 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -375,7 +375,9 @@ void BitcoinGUI::createActions() for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) { const std::string& path = i.first; QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path); - // Menu items remove single &. Single & are shown when && is in the string, but only the first occurrence. So replace only the first & with && + // Menu items remove single &. Single & are shown when && is in + // the string, but only the first occurrence. So replace only + // the first & with &&. name.replace(name.indexOf(QChar('&')), 1, QString("&&")); QAction* action = m_open_wallet_menu->addAction(name); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 5cde21eec6..3d40ee7823 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -178,6 +178,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate initial keys"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to generate keys"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -blockfilterindex value %s."), +QT_TRANSLATE_NOOP("bitcoin-core", "Unknown address type '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unknown change type '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported logging category %s=%s."), QT_TRANSLATE_NOOP("bitcoin-core", "Upgrading UTXO database"), diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp index 10262c37c3..8e6474b0d4 100644 --- a/src/qt/createwalletdialog.cpp +++ b/src/qt/createwalletdialog.cpp @@ -25,7 +25,8 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) : }); connect(ui->encrypt_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) { - // Disable disable_privkeys_checkbox when encrypt is set to true, enable it when encrypt is false + // Disable the disable_privkeys_checkbox when isEncryptWalletChecked is + // set to true, enable it when isEncryptWalletChecked is false. ui->disable_privkeys_checkbox->setEnabled(!checked); // When the disable_privkeys_checkbox is disabled, uncheck it. @@ -45,17 +46,17 @@ QString CreateWalletDialog::walletName() const return ui->wallet_name_line_edit->text(); } -bool CreateWalletDialog::encrypt() const +bool CreateWalletDialog::isEncryptWalletChecked() const { return ui->encrypt_wallet_checkbox->isChecked(); } -bool CreateWalletDialog::disablePrivateKeys() const +bool CreateWalletDialog::isDisablePrivateKeysChecked() const { return ui->disable_privkeys_checkbox->isChecked(); } -bool CreateWalletDialog::blank() const +bool CreateWalletDialog::isMakeBlankWalletChecked() const { return ui->blank_wallet_checkbox->isChecked(); } diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h index a1365b5969..30766107b9 100644 --- a/src/qt/createwalletdialog.h +++ b/src/qt/createwalletdialog.h @@ -24,9 +24,9 @@ public: virtual ~CreateWalletDialog(); QString walletName() const; - bool encrypt() const; - bool disablePrivateKeys() const; - bool blank() const; + bool isEncryptWalletChecked() const; + bool isDisablePrivateKeysChecked() const; + bool isMakeBlankWalletChecked() const; private: Ui::CreateWalletDialog *ui; diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index 69803989cd..e74d183818 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -95,7 +95,7 @@ <item row="3" column="1"> <widget class="QCheckBox" name="toggleShowPasswordButton"> <property name="text"> - <string>Show password</string> + <string>Show passphrase</string> </property> </widget> </item> diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui index 1fbaeeaaab..e49bab8f3b 100644 --- a/src/qt/forms/createwalletdialog.ui +++ b/src/qt/forms/createwalletdialog.ui @@ -62,7 +62,7 @@ </rect> </property> <property name="toolTip"> - <string>Encrypt the wallet. The wallet will be encrypted with a password of your choice.</string> + <string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string> </property> <property name="text"> <string>Encrypt Wallet</string> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 0d280f2993..0214356eaa 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -189,7 +189,7 @@ </widget> </item> <item> - <widget class="QCheckBox" name="useLegacyAddress"> + <widget class="QCheckBox" name="useBech32"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> @@ -206,10 +206,10 @@ <enum>Qt::StrongFocus</enum> </property> <property name="toolTip"> - <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When checked, an address compatible with older wallets will be created instead.</string> + <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</string> </property> <property name="text"> - <string>Generate legacy address</string> + <string>Generate native segwit (Bech32) address</string> </property> </widget> </item> @@ -360,7 +360,7 @@ <tabstops> <tabstop>reqLabel</tabstop> <tabstop>reqAmount</tabstop> - <tabstop>useLegacyAddress</tabstop> + <tabstop>useBech32</tabstop> <tabstop>reqMessage</tabstop> <tabstop>receiveButton</tabstop> <tabstop>clearButton</tabstop> diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 9e05c63aa0..53c80639b9 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -154,6 +154,7 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz storageRequiresMsg.arg(requiredSpace) + " " + tr("The wallet will also be stored in this directory.") ); + this->adjustSize(); startThread(); } diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 7864f97f31..d34fd9eb45 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -171,16 +171,11 @@ </message> <message> <location line="+14"/> - <source>Show password</source> + <source>Show passphrase</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../askpassphrasedialog.cpp" line="+46"/> - <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> + <location filename="../askpassphrasedialog.cpp" line="+50"/> <source>Encrypt wallet</source> <translation type="unfinished"></translation> </message> @@ -210,12 +205,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Enter the old passphrase and new passphrase to the wallet.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+45"/> + <location line="+46"/> <source>Confirm wallet encryption</source> <translation type="unfinished"></translation> </message> @@ -230,36 +220,61 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> - <location line="+58"/> + <location line="+19"/> + <location line="+57"/> <source>Wallet encrypted</source> <translation type="unfinished"></translation> </message> <message> - <location line="-56"/> - <source>Your wallet is now encrypted. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <location line="-145"/> + <source>Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+23"/> + <source>Enter the old passphrase and new passphrase for the wallet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+53"/> + <source>Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> <translation type="unfinished"></translation> </message> <message> <location line="+4"/> + <source>Wallet to be encrypted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Your wallet is about to be encrypted. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> + <source>Your wallet is now encrypted. </source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation type="unfinished"></translation> </message> <message> <location line="+8"/> - <location line="+7"/> + <location line="+8"/> <location line="+43"/> <location line="+6"/> <source>Wallet encryption failed</source> <translation type="unfinished"></translation> </message> <message> - <location line="-55"/> + <location line="-56"/> <source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+8"/> <location line="+49"/> <source>The supplied passphrases do not match.</source> <translation type="unfinished"></translation> @@ -310,17 +325,17 @@ <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+315"/> + <location filename="../bitcoingui.cpp" line="+316"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+637"/> + <location line="+623"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-715"/> + <location line="-701"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -400,7 +415,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+216"/> + <location line="+11"/> + <source>Create Wallet...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Create a new wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+190"/> <source>Wallet:</source> <translation type="unfinished"></translation> </message> @@ -435,7 +460,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-1035"/> + <location line="-1021"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -500,17 +525,17 @@ <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation> </message> <message> - <location line="+117"/> + <location line="+110"/> <source>&File</source> <translation>&File</translation> </message> <message> - <location line="+14"/> + <location line="+15"/> <source>&Settings</source> <translation>&Settings</translation> </message> <message> - <location line="+66"/> + <location line="+58"/> <source>&Help</source> <translation>&Help</translation> </message> @@ -520,7 +545,7 @@ <translation>Tabs toolbar</translation> </message> <message> - <location line="-270"/> + <location line="-256"/> <source>Request payments (generates QR codes and bitcoin: URIs)</source> <translation type="unfinished"></translation> </message> @@ -540,12 +565,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+13"/> <source>&Command-line options</source> <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+539"/> + <location line="+522"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -606,7 +631,7 @@ <translation>Up to date</translation> </message> <message> - <location line="-656"/> + <location line="-642"/> <source>&Sending addresses</source> <translation type="unfinished"></translation> </message> @@ -636,7 +661,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+7"/> <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source> <translation type="unfinished"></translation> </message> @@ -646,22 +671,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> - <source>Opening Wallet <b>%1</b>...</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+9"/> - <source>Open Wallet Failed</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+15"/> + <location line="+21"/> <source>No wallets available</source> <translation type="unfinished"></translation> </message> <message> - <location line="+48"/> + <location line="+55"/> <source>&Window</source> <translation type="unfinished">&Window</translation> </message> @@ -676,12 +691,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> - <source>Restore</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+12"/> + <location line="+18"/> <source>Main Window</source> <translation type="unfinished"></translation> </message> @@ -782,7 +792,7 @@ <translation>Wallet is <b>encrypted</b> and currently <b>locked</b></translation> </message> <message> - <location filename="../bitcoin.cpp" line="+382"/> + <location filename="../bitcoin.cpp" line="+386"/> <source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source> <translation type="unfinished"></translation> </message> @@ -978,6 +988,72 @@ </message> </context> <context> + <name>CreateWalletActivity</name> + <message> + <location filename="../walletcontroller.cpp" line="+201"/> + <source>Creating Wallet <b>%1</b>...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+26"/> + <source>Create wallet failed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Create wallet warning</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CreateWalletDialog</name> + <message> + <location filename="../forms/createwalletdialog.ui" line="+14"/> + <source>Create Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+38"/> + <source>Wallet Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> + <source>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Encrypt Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+19"/> + <source>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Disable Private Keys</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> + <source>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Make Blank Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../createwalletdialog.cpp" line="+19"/> + <source>Create</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>EditAddressDialog</name> <message> <location filename="../forms/editaddressdialog.ui" line="+14"/> @@ -1121,6 +1197,11 @@ </message> <message> <location line="+10"/> + <source>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> <source>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</source> <translation type="unfinished"></translation> </message> @@ -1130,7 +1211,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-160"/> + <location line="-170"/> <source>Use the default data directory</source> <translation>Use the default data directory</translation> </message> @@ -1145,7 +1226,12 @@ <translation type="unfinished">Bitcoin</translation> </message> <message> - <location line="+6"/> + <location line="+9"/> + <source>Discard blocks after verification, except most recent %1 GB (prune)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>At least %1 GB of data will be stored in this directory, and it will grow over time.</source> <translation type="unfinished"></translation> </message> @@ -1165,12 +1251,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+75"/> + <location line="+78"/> <source>Error: Specified data directory "%1" cannot be created.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+27"/> + <location line="+30"/> <source>Error</source> <translation>Error</translation> </message> @@ -1190,6 +1276,14 @@ <numerusform>(of %n GB needed)</numerusform> </translation> </message> + <message numerus="yes"> + <location line="+4"/> + <source>(%n GB needed for full chain)</source> + <translation type="unfinished"> + <numerusform></numerusform> + <numerusform></numerusform> + </translation> + </message> </context> <context> <name>ModalOverlay</name> @@ -1286,6 +1380,29 @@ </message> </context> <context> + <name>OpenWalletActivity</name> + <message> + <location filename="../walletcontroller.cpp" line="+39"/> + <source>Open wallet failed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Open wallet warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> + <source>default wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Opening Wallet <b>%1</b>...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>OptionsDialog</name> <message> <location filename="../forms/optionsdialog.ui" line="+14"/> @@ -1734,7 +1851,7 @@ <name>PaymentServer</name> <message> <location filename="../paymentserver.cpp" line="+226"/> - <location line="+346"/> + <location line="+350"/> <location line="+42"/> <location line="+108"/> <location line="+14"/> @@ -1743,7 +1860,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-527"/> + <location line="-531"/> <source>Cannot start bitcoin: click-to-pay handler</source> <translation type="unfinished"></translation> </message> @@ -1752,13 +1869,13 @@ <location line="+9"/> <location line="+16"/> <location line="+16"/> - <location line="+5"/> + <location line="+7"/> <location line="+7"/> <source>URI handling</source> <translation type="unfinished"></translation> </message> <message> - <location line="-53"/> + <location line="-55"/> <source>'bitcoin://' is not a valid URI. Use 'bitcoin:' instead.</source> <translation type="unfinished"></translation> </message> @@ -1774,12 +1891,24 @@ </message> <message> <location line="+16"/> - <location line="+36"/> + <location line="+38"/> <source>Cannot process payment request because BIP70 support was not compiled in.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-32"/> + <location line="-37"/> + <location line="+38"/> + <source>Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-37"/> + <location line="+38"/> + <source>If you are receiving this error you should request the merchant provide a BIP21 compatible URI.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-34"/> <source>Invalid payment address %1</source> <translation type="unfinished"></translation> </message> @@ -1800,7 +1929,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+199"/> + <location line="+201"/> <location line="+9"/> <location line="+31"/> <location line="+10"/> @@ -2032,7 +2161,7 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../bitcoin.cpp" line="+116"/> + <location filename="../bitcoin.cpp" line="+118"/> <source>Error: Specified data directory "%1" does not exist.</source> <translation type="unfinished"></translation> </message> @@ -2047,7 +2176,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+59"/> + <location line="+64"/> <source>%1 didn't yet exit safely...</source> <translation type="unfinished"></translation> </message> @@ -2567,17 +2696,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+136"/> - <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When checked, an address compatible with older wallets will be created instead.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> - <source>Generate legacy address</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="-178"/> + <location line="-39"/> <location line="+153"/> <source>An optional amount to request. Leave this empty or zero to not request a specific amount.</source> <translation type="unfinished"></translation> @@ -2598,7 +2717,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+142"/> + <location line="+78"/> + <source>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Generate native segwit (Bech32) address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+61"/> <source>Requested payments history</source> <translation type="unfinished"></translation> </message> @@ -3434,14 +3563,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos </message> </context> <context> - <name>SplashScreen</name> - <message> - <location filename="../networkstyle.cpp" line="+19"/> - <source>[testnet]</source> - <translation>[testnet]</translation> - </message> -</context> -<context> <name>TrafficGraphWidget</name> <message> <location filename="../trafficgraphwidget.cpp" line="+81"/> @@ -4036,13 +4157,13 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <context> <name>WalletController</name> <message> - <location filename="../walletcontroller.cpp" line="+73"/> + <location filename="../walletcontroller.cpp" line="-205"/> <source>Close wallet</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> - <source>Are you sure you wish to close wallet <i>%1</i>?</source> + <source>Are you sure you wish to close the wallet <i>%1</i>?</source> <translation type="unfinished"></translation> </message> <message> @@ -4410,12 +4531,22 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+22"/> + <source>Unknown address type '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Unknown change type '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> <source>Upgrading txindex database</source> <translation type="unfinished"></translation> </message> <message> - <location line="-44"/> + <location line="-46"/> <source>Loading P2P addresses...</source> <translation type="unfinished"></translation> </message> @@ -4475,7 +4606,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+6"/> <source>Unsupported logging category %s=%s.</source> <translation type="unfinished"></translation> </message> @@ -4500,7 +4631,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="-154"/> + <location line="-156"/> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation type="unfinished"></translation> </message> @@ -4641,7 +4772,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+9"/> <source>Verifying wallet(s)...</source> <translation type="unfinished"></translation> </message> @@ -4656,7 +4787,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="-177"/> + <location line="-179"/> <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> <translation type="unfinished"></translation> </message> @@ -4726,12 +4857,12 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satos <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+11"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-50"/> + <location line="-52"/> <source>Insufficient funds</source> <translation>Insufficient funds</translation> </message> diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index e8cf432131..df8d5115d5 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -96,13 +96,13 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) if (model->node().isAddressTypeSet()) { // user explicitly set the type, use it if (model->wallet().getDefaultAddressType() == OutputType::BECH32) { - ui->useLegacyAddress->setCheckState(Qt::Unchecked); + ui->useBech32->setCheckState(Qt::Checked); } else { - ui->useLegacyAddress->setCheckState(Qt::Checked); + ui->useBech32->setCheckState(Qt::Unchecked); } } else { // Always fall back to bech32 in the gui - ui->useLegacyAddress->setCheckState(Qt::Unchecked); + ui->useBech32->setCheckState(Qt::Checked); } // Set the button to be enabled or disabled based on whether the wallet can give out new addresses. @@ -155,7 +155,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() QString label = ui->reqLabel->text(); /* Generate new receiving address */ OutputType address_type; - if (!ui->useLegacyAddress->isChecked()) { + if (ui->useBech32->isChecked()) { address_type = OutputType::BECH32; } else { address_type = model->wallet().getDefaultAddressType(); diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 8b8283d3d8..fa6f9f3f16 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -75,7 +75,7 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent) { QMessageBox box(parent); box.setWindowTitle(tr("Close wallet")); - box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName()))); + box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName()))); box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled.")); box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel); box.setDefaultButton(QMessageBox::Yes); @@ -179,9 +179,10 @@ CreateWalletActivity::~CreateWalletActivity() delete m_passphrase_dialog; } -void CreateWalletActivity::askPasshprase() +void CreateWalletActivity::askPassphrase() { m_passphrase_dialog = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, m_parent_widget, &m_passphrase); + m_passphrase_dialog->setWindowModality(Qt::ApplicationModal); m_passphrase_dialog->show(); connect(m_passphrase_dialog, &QObject::destroyed, [this] { @@ -201,10 +202,10 @@ void CreateWalletActivity::createWallet() std::string name = m_create_wallet_dialog->walletName().toStdString(); uint64_t flags = 0; - if (m_create_wallet_dialog->disablePrivateKeys()) { + if (m_create_wallet_dialog->isDisablePrivateKeysChecked()) { flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS; } - if (m_create_wallet_dialog->blank()) { + if (m_create_wallet_dialog->isMakeBlankWalletChecked()) { flags |= WALLET_FLAG_BLANK_WALLET; } @@ -246,8 +247,8 @@ void CreateWalletActivity::create() Q_EMIT finished(); }); connect(m_create_wallet_dialog, &QDialog::accepted, [this] { - if (m_create_wallet_dialog->encrypt()) { - askPasshprase(); + if (m_create_wallet_dialog->isEncryptWalletChecked()) { + askPassphrase(); } else { createWallet(); } diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h index 4e1a772f3a..fb37b7292c 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -118,7 +118,7 @@ Q_SIGNALS: void created(WalletModel* wallet_model); private: - void askPasshprase(); + void askPassphrase(); void createWallet(); void finish(); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 9513c2b9ac..02717fa80f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1159,7 +1159,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam { bip9.pushKV("bit", consensusParams.vDeployments[id].bit); } - bip9.pushKV("startTime", consensusParams.vDeployments[id].nStartTime); + bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id); bip9.pushKV("since", since_height); @@ -1213,7 +1213,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n" " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" - " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"start_time\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" " \"since\": xx, (numeric) height of the first block to which the status applies\n" " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n" diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index fb8ea8c227..f548d356cf 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -14,9 +14,11 @@ #include <node/coin.h> #include <node/psbt.h> #include <node/transaction.h> +#include <policy/policy.h> #include <policy/rbf.h> #include <primitives/transaction.h> #include <psbt.h> +#include <random.h> #include <rpc/rawtransaction_util.h> #include <rpc/server.h> #include <rpc/util.h> @@ -37,11 +39,11 @@ #include <univalue.h> -/** High fee for sendrawtransaction and testmempoolaccept. - * By default, transaction with a fee higher than this will be rejected by the - * RPCs. This can be overridden with the maxfeerate argument. +/** Maximum fee rate for sendrawtransaction and testmempoolaccept. + * By default, a transaction with a fee rate higher than this will be rejected + * by the RPCs. This can be overridden with the maxfeerate argument. */ -constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10}; +static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10}; static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { @@ -774,7 +776,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n", { {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"}, - {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), + {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB.\nSet to 0 to accept any fee rate.\n"}, }, @@ -804,19 +806,17 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); CTransactionRef tx(MakeTransactionRef(std::move(mtx))); - CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE; + CFeeRate max_raw_tx_fee_rate = DEFAULT_MAX_RAW_TX_FEE_RATE; // TODO: temporary migration code for old clients. Remove in v0.20 if (request.params[1].isBool()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0."); } else if (!request.params[1].isNull()) { - size_t weight = GetTransactionWeight(*tx); - CFeeRate fr(AmountFromValue(request.params[1])); - // the +3/4 part rounds the value up, and is the same formula used when - // calculating the fee for a transaction - // (see GetVirtualTransactionSize) - max_raw_tx_fee = fr.GetFee((weight+3)/4); + max_raw_tx_fee_rate = CFeeRate(AmountFromValue(request.params[1])); } + int64_t virtual_size = GetVirtualTransactionSize(*tx); + CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); + std::string err_string; AssertLockNotHeld(cs_main); const TransactionError err = BroadcastTransaction(tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true); @@ -840,7 +840,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, }, }, - {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, + {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, }, RPCResult{ "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" @@ -880,19 +880,17 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const uint256& tx_hash = tx->GetHash(); - CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE; + CFeeRate max_raw_tx_fee_rate = DEFAULT_MAX_RAW_TX_FEE_RATE; // TODO: temporary migration code for old clients. Remove in v0.20 if (request.params[1].isBool()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0."); } else if (!request.params[1].isNull()) { - size_t weight = GetTransactionWeight(*tx); - CFeeRate fr(AmountFromValue(request.params[1])); - // the +3/4 part rounds the value up, and is the same formula used when - // calculating the fee for a transaction - // (see GetVirtualTransactionSize) - max_raw_tx_fee = fr.GetFee((weight+3)/4); + max_raw_tx_fee_rate = CFeeRate(AmountFromValue(request.params[1])); } + int64_t virtual_size = GetVirtualTransactionSize(*tx); + CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); + UniValue result(UniValue::VARR); UniValue result_0(UniValue::VOBJ); result_0.pushKV("txid", tx_hash.GetHex()); @@ -1615,8 +1613,30 @@ UniValue joinpsbts(const JSONRPCRequest& request) merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end()); } + // Generate list of shuffled indices for shuffling inputs and outputs of the merged PSBT + std::vector<int> input_indices(merged_psbt.inputs.size()); + std::iota(input_indices.begin(), input_indices.end(), 0); + std::vector<int> output_indices(merged_psbt.outputs.size()); + std::iota(output_indices.begin(), output_indices.end(), 0); + + // Shuffle input and output indicies lists + Shuffle(input_indices.begin(), input_indices.end(), FastRandomContext()); + Shuffle(output_indices.begin(), output_indices.end(), FastRandomContext()); + + PartiallySignedTransaction shuffled_psbt; + shuffled_psbt.tx = CMutableTransaction(); + shuffled_psbt.tx->nVersion = merged_psbt.tx->nVersion; + shuffled_psbt.tx->nLockTime = merged_psbt.tx->nLockTime; + for (int i : input_indices) { + shuffled_psbt.AddInput(merged_psbt.tx->vin[i], merged_psbt.inputs[i]); + } + for (int i : output_indices) { + shuffled_psbt.AddOutput(merged_psbt.tx->vout[i], merged_psbt.outputs[i]); + } + shuffled_psbt.unknown.insert(merged_psbt.unknown.begin(), merged_psbt.unknown.end()); + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << merged_psbt; + ssTx << shuffled_psbt; return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()); } diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index f1d176ba4d..fe98fff4bb 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -268,7 +268,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst } } -UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType) +UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType) { int nHashType = ParseSighashString(hashType); diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index b35e6da4ca..5b92650764 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -19,11 +19,11 @@ class SigningProvider; * * @param mtx The transaction to-be-signed * @param keystore Temporary keystore containing signing keys - * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call + * @param coins Map of unspent outputs * @param hashType The signature hash type * @returns JSON object with details of signed transaction */ -UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, std::map<COutPoint, Coin>& coins, const UniValue& hashType); +UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType); /** * Parse a prevtxs UniValue array and get the map of coins from it diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f8701b6d01..20fae2eebf 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -334,7 +334,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) - return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. + return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes (CVE-2010-5137). // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) @@ -1483,6 +1483,8 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY); } + // scriptSig and scriptPubKey must be evaluated sequentially on the same stack + // rather than being simply concatenated (see CVE-2010-5141) std::vector<std::vector<unsigned char> > stack, stackCopy; if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror)) // serror is set diff --git a/src/streams.h b/src/streams.h index 4e600f1826..517eefc932 100644 --- a/src/streams.h +++ b/src/streams.h @@ -735,16 +735,17 @@ protected: size_t nBytes = fread((void*)&vchBuf[pos], 1, readNow, src); if (nBytes == 0) { throw std::ios_base::failure(feof(src) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed"); - } else { - nSrcPos += nBytes; - return true; } + nSrcPos += nBytes; + return true; } public: CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, 0) { + if (nRewindIn >= nBufSize) + throw std::ios_base::failure("Rewind limit must be less than buffer size"); src = fileIn; } @@ -777,8 +778,6 @@ public: void read(char *pch, size_t nSize) { if (nSize + nReadPos > nReadLimit) throw std::ios_base::failure("Read attempted past buffer limit"); - if (nSize + nRewind > vchBuf.size()) - throw std::ios_base::failure("Read larger than buffer size"); while (nSize > 0) { if (nReadPos == nSrcPos) Fill(); @@ -802,16 +801,19 @@ public: //! rewind to a given reading position bool SetPos(uint64_t nPos) { - nReadPos = nPos; - if (nReadPos + nRewind < nSrcPos) { - nReadPos = nSrcPos - nRewind; + size_t bufsize = vchBuf.size(); + if (nPos + bufsize < nSrcPos) { + // rewinding too far, rewind as far as possible + nReadPos = nSrcPos - bufsize; return false; - } else if (nReadPos > nSrcPos) { + } + if (nPos > nSrcPos) { + // can't go this far forward, go as far as possible nReadPos = nSrcPos; return false; - } else { - return true; } + nReadPos = nPos; + return true; } bool Seek(uint64_t nPos) { diff --git a/src/test/README.md b/src/test/README.md index 8901fae7bd..96dcb072bc 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -1,3 +1,15 @@ +# Unit tests + +The sources in this directory are unit test cases. Boost includes a +unit testing framework, and since Bitcoin Core already uses Boost, it makes +sense to simply use this framework rather than require developers to +configure some other framework (we want as few impediments to creating +unit tests as possible). + +The build system is set up to compile an executable called `test_bitcoin` +that runs all of the unit tests. The main source file is called +`setup_common.cpp`. + ### Compiling/running unit tests Unit tests will be automatically compiled if dependencies were met in `./configure` @@ -12,7 +24,7 @@ to run the bitcoind tests. To add more bitcoind tests, add `BOOST_AUTO_TEST_CASE` functions to the existing .cpp files in the `test/` directory or add new .cpp files that -implement new BOOST_AUTO_TEST_SUITE sections. +implement new `BOOST_AUTO_TEST_SUITE` sections. To run the bitcoin-qt tests manually, launch `src/qt/test/test_bitcoin-qt` @@ -32,20 +44,24 @@ example, to run just the getarg_tests verbosely: Run `test_bitcoin --help` for the full list. -### Note on adding test cases - -The sources in this directory are unit test cases. Boost includes a -unit testing framework, and since bitcoin already uses boost, it makes -sense to simply use this framework rather than require developers to -configure some other framework (we want as few impediments to creating -unit tests as possible). +### Adding test cases -The build system is setup to compile an executable called `test_bitcoin` -that runs all of the unit tests. The main source file is called -setup_common.cpp. To add a new unit test file to our test suite you need +To add a new unit test file to our test suite you need to add the file to `src/Makefile.test.include`. The pattern is to create one test file for each class or source file for which you want to create -unit tests. The file naming convention is `<source_filename>_tests.cpp` +unit tests. The file naming convention is `<source_filename>_tests.cpp` and such files should wrap their tests in a test suite called `<source_filename>_tests`. For an example of this pattern, -examine `uint256_tests.cpp`. +see `uint256_tests.cpp`. + +### Logging and debugging in unit tests + +To write to logs from unit tests you need to use specific message methods +provided by Boost. The simplest is `BOOST_TEST_MESSAGE`. + +For debugging you can launch the test_bitcoin executable with `gdb`or `lldb` and +start debugging, just like you would with bitcoind: + +```bash +gdb src/test/test_bitcoin +``` diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 9b320b6943..3241f32f56 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -829,15 +829,16 @@ ["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], ["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["TEST DISABLED OP CODES (CVE-2010-5137)"], ["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], ["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], ["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"], ["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"], ["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "LEFT disabled"], ["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "RIGHT disabled"], - -["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], - ["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"], ["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "AND disabled"], ["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "OR disabled"], diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 1684258c9f..dc38a1a818 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -249,4 +249,104 @@ BOOST_AUTO_TEST_CASE(merkle_test) } } + +BOOST_AUTO_TEST_CASE(merkle_test_empty_block) +{ + bool mutated = false; + CBlock block; + uint256 root = BlockMerkleRoot(block, &mutated); + + BOOST_CHECK_EQUAL(root.IsNull(), true); + BOOST_CHECK_EQUAL(mutated, false); +} + +BOOST_AUTO_TEST_CASE(merkle_test_oneTx_block) +{ + bool mutated = false; + CBlock block; + + block.vtx.resize(1); + CMutableTransaction mtx; + mtx.nLockTime = 0; + block.vtx[0] = MakeTransactionRef(std::move(mtx)); + uint256 root = BlockMerkleRoot(block, &mutated); + BOOST_CHECK_EQUAL(root, block.vtx[0]->GetHash()); + BOOST_CHECK_EQUAL(mutated, false); +} + +BOOST_AUTO_TEST_CASE(merkle_test_OddTxWithRepeatedLastTx_block) +{ + bool mutated; + CBlock block, blockWithRepeatedLastTx; + + block.vtx.resize(3); + + for (std::size_t pos = 0; pos < block.vtx.size(); pos++) { + CMutableTransaction mtx; + mtx.nLockTime = pos; + block.vtx[pos] = MakeTransactionRef(std::move(mtx)); + } + + blockWithRepeatedLastTx = block; + blockWithRepeatedLastTx.vtx.push_back(blockWithRepeatedLastTx.vtx.back()); + + uint256 rootofBlock = BlockMerkleRoot(block, &mutated); + BOOST_CHECK_EQUAL(mutated, false); + + uint256 rootofBlockWithRepeatedLastTx = BlockMerkleRoot(blockWithRepeatedLastTx, &mutated); + BOOST_CHECK_EQUAL(rootofBlock, rootofBlockWithRepeatedLastTx); + BOOST_CHECK_EQUAL(mutated, true); +} + +BOOST_AUTO_TEST_CASE(merkle_test_LeftSubtreeRightSubtree) +{ + CBlock block, leftSubtreeBlock, rightSubtreeBlock; + + block.vtx.resize(4); + std::size_t pos; + for (pos = 0; pos < block.vtx.size(); pos++) { + CMutableTransaction mtx; + mtx.nLockTime = pos; + block.vtx[pos] = MakeTransactionRef(std::move(mtx)); + } + + for (pos = 0; pos < block.vtx.size() / 2; pos++) + leftSubtreeBlock.vtx.push_back(block.vtx[pos]); + + for (pos = block.vtx.size() / 2; pos < block.vtx.size(); pos++) + rightSubtreeBlock.vtx.push_back(block.vtx[pos]); + + uint256 root = BlockMerkleRoot(block); + uint256 rootOfLeftSubtree = BlockMerkleRoot(leftSubtreeBlock); + uint256 rootOfRightSubtree = BlockMerkleRoot(rightSubtreeBlock); + std::vector<uint256> leftRight; + leftRight.push_back(rootOfLeftSubtree); + leftRight.push_back(rootOfRightSubtree); + uint256 rootOfLR = ComputeMerkleRoot(leftRight); + + BOOST_CHECK_EQUAL(root, rootOfLR); +} + +BOOST_AUTO_TEST_CASE(merkle_test_BlockWitness) +{ + CBlock block; + + block.vtx.resize(2); + for (std::size_t pos = 0; pos < block.vtx.size(); pos++) { + CMutableTransaction mtx; + mtx.nLockTime = pos; + block.vtx[pos] = MakeTransactionRef(std::move(mtx)); + } + + uint256 blockWitness = BlockWitnessMerkleRoot(block); + + std::vector<uint256> hashes; + hashes.resize(block.vtx.size()); + hashes[0].SetNull(); + hashes[1] = block.vtx[1]->GetHash(); + + uint256 merkelRootofHashes = ComputeMerkleRoot(hashes); + + BOOST_CHECK_EQUAL(merkelRootofHashes, blockWitness); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index b812cef801..638819d564 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <random.h> #include <streams.h> #include <test/setup_common.h> @@ -202,4 +203,247 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor) std::string(ds.begin(), ds.end())); } +BOOST_AUTO_TEST_CASE(streams_buffered_file) +{ + FILE* file = fsbridge::fopen("streams_test_tmp", "w+b"); + // The value at each offset is the offset. + for (uint8_t j = 0; j < 40; ++j) { + fwrite(&j, 1, 1, file); + } + rewind(file); + + // The buffer size (second arg) must be greater than the rewind + // amount (third arg). + try { + CBufferedFile bfbad(file, 25, 25, 222, 333); + BOOST_CHECK(false); + } catch (const std::exception& e) { + BOOST_CHECK(strstr(e.what(), + "Rewind limit must be less than buffer size") != nullptr); + } + + // The buffer is 25 bytes, allow rewinding 10 bytes. + CBufferedFile bf(file, 25, 10, 222, 333); + BOOST_CHECK(!bf.eof()); + + // These two members have no functional effect. + BOOST_CHECK_EQUAL(bf.GetType(), 222); + BOOST_CHECK_EQUAL(bf.GetVersion(), 333); + + uint8_t i; + bf >> i; + BOOST_CHECK_EQUAL(i, 0); + bf >> i; + BOOST_CHECK_EQUAL(i, 1); + + // After reading bytes 0 and 1, we're positioned at 2. + BOOST_CHECK_EQUAL(bf.GetPos(), 2); + + // Rewind to offset 0, ok (within the 10 byte window). + BOOST_CHECK(bf.SetPos(0)); + bf >> i; + BOOST_CHECK_EQUAL(i, 0); + + // We can go forward to where we've been, but beyond may fail. + BOOST_CHECK(bf.SetPos(2)); + bf >> i; + BOOST_CHECK_EQUAL(i, 2); + + // If you know the maximum number of bytes that should be + // read to deserialize the variable, you can limit the read + // extent. The current file offset is 3, so the following + // SetLimit() allows zero bytes to be read. + BOOST_CHECK(bf.SetLimit(3)); + try { + bf >> i; + BOOST_CHECK(false); + } catch (const std::exception& e) { + BOOST_CHECK(strstr(e.what(), + "Read attempted past buffer limit") != nullptr); + } + // The default argument removes the limit completely. + BOOST_CHECK(bf.SetLimit()); + // The read position should still be at 3 (no change). + BOOST_CHECK_EQUAL(bf.GetPos(), 3); + + // Read from current offset, 3, forward until position 10. + for (uint8_t j = 3; j < 10; ++j) { + bf >> i; + BOOST_CHECK_EQUAL(i, j); + } + BOOST_CHECK_EQUAL(bf.GetPos(), 10); + + // We're guaranteed (just barely) to be able to rewind to zero. + BOOST_CHECK(bf.SetPos(0)); + BOOST_CHECK_EQUAL(bf.GetPos(), 0); + bf >> i; + BOOST_CHECK_EQUAL(i, 0); + + // We can set the position forward again up to the farthest + // into the stream we've been, but no farther. (Attempting + // to go farther may succeed, but it's not guaranteed.) + BOOST_CHECK(bf.SetPos(10)); + bf >> i; + BOOST_CHECK_EQUAL(i, 10); + BOOST_CHECK_EQUAL(bf.GetPos(), 11); + + // Now it's only guaranteed that we can rewind to offset 1 + // (current read position, 11, minus rewind amount, 10). + BOOST_CHECK(bf.SetPos(1)); + BOOST_CHECK_EQUAL(bf.GetPos(), 1); + bf >> i; + BOOST_CHECK_EQUAL(i, 1); + + // We can stream into large variables, even larger than + // the buffer size. + BOOST_CHECK(bf.SetPos(11)); + { + uint8_t a[40 - 11]; + bf >> a; + for (uint8_t j = 0; j < sizeof(a); ++j) { + BOOST_CHECK_EQUAL(a[j], 11 + j); + } + } + BOOST_CHECK_EQUAL(bf.GetPos(), 40); + + // We've read the entire file, the next read should throw. + try { + bf >> i; + BOOST_CHECK(false); + } catch (const std::exception& e) { + BOOST_CHECK(strstr(e.what(), + "CBufferedFile::Fill: end of file") != nullptr); + } + // Attempting to read beyond the end sets the EOF indicator. + BOOST_CHECK(bf.eof()); + + // Still at offset 40, we can go back 10, to 30. + BOOST_CHECK_EQUAL(bf.GetPos(), 40); + BOOST_CHECK(bf.SetPos(30)); + bf >> i; + BOOST_CHECK_EQUAL(i, 30); + BOOST_CHECK_EQUAL(bf.GetPos(), 31); + + // We're too far to rewind to position zero. + BOOST_CHECK(!bf.SetPos(0)); + // But we should now be positioned at least as far back as allowed + // by the rewind window (relative to our farthest read position, 40). + BOOST_CHECK(bf.GetPos() <= 30); + + // We can explicitly close the file, or the destructor will do it. + bf.fclose(); + + fs::remove("streams_test_tmp"); +} + +BOOST_AUTO_TEST_CASE(streams_buffered_file_rand) +{ + // Make this test deterministic. + SeedInsecureRand(true); + + for (int rep = 0; rep < 50; ++rep) { + FILE* file = fsbridge::fopen("streams_test_tmp", "w+b"); + size_t fileSize = InsecureRandRange(256); + for (uint8_t i = 0; i < fileSize; ++i) { + fwrite(&i, 1, 1, file); + } + rewind(file); + + size_t bufSize = InsecureRandRange(300) + 1; + size_t rewindSize = InsecureRandRange(bufSize); + CBufferedFile bf(file, bufSize, rewindSize, 222, 333); + size_t currentPos = 0; + size_t maxPos = 0; + for (int step = 0; step < 100; ++step) { + if (currentPos >= fileSize) + break; + + // We haven't read to the end of the file yet. + BOOST_CHECK(!bf.eof()); + BOOST_CHECK_EQUAL(bf.GetPos(), currentPos); + + // Pretend the file consists of a series of objects of varying + // sizes; the boundaries of the objects can interact arbitrarily + // with the CBufferFile's internal buffer. These first three + // cases simulate objects of various sizes (1, 2, 5 bytes). + switch (InsecureRandRange(5)) { + case 0: { + uint8_t a[1]; + if (currentPos + 1 > fileSize) + continue; + bf.SetLimit(currentPos + 1); + bf >> a; + for (uint8_t i = 0; i < 1; ++i) { + BOOST_CHECK_EQUAL(a[i], currentPos); + currentPos++; + } + break; + } + case 1: { + uint8_t a[2]; + if (currentPos + 2 > fileSize) + continue; + bf.SetLimit(currentPos + 2); + bf >> a; + for (uint8_t i = 0; i < 2; ++i) { + BOOST_CHECK_EQUAL(a[i], currentPos); + currentPos++; + } + break; + } + case 2: { + uint8_t a[5]; + if (currentPos + 5 > fileSize) + continue; + bf.SetLimit(currentPos + 5); + bf >> a; + for (uint8_t i = 0; i < 5; ++i) { + BOOST_CHECK_EQUAL(a[i], currentPos); + currentPos++; + } + break; + } + case 3: { + // Find a byte value (that is at or ahead of the current position). + size_t find = currentPos + InsecureRandRange(8); + if (find >= fileSize) + find = fileSize - 1; + bf.FindByte(static_cast<char>(find)); + // The value at each offset is the offset. + BOOST_CHECK_EQUAL(bf.GetPos(), find); + currentPos = find; + + bf.SetLimit(currentPos + 1); + uint8_t i; + bf >> i; + BOOST_CHECK_EQUAL(i, currentPos); + currentPos++; + break; + } + case 4: { + size_t requestPos = InsecureRandRange(maxPos + 4); + bool okay = bf.SetPos(requestPos); + // The new position may differ from the requested position + // because we may not be able to rewind beyond the rewind + // window, and we may not be able to move forward beyond the + // farthest position we've reached so far. + currentPos = bf.GetPos(); + BOOST_CHECK_EQUAL(okay, currentPos == requestPos); + // Check that we can position within the rewind window. + if (requestPos <= maxPos && + maxPos > rewindSize && + requestPos >= maxPos - rewindSize) { + // We requested a position within the rewind window. + BOOST_CHECK(okay); + } + break; + } + } + if (maxPos < currentPos) + maxPos = currentPos; + } + } + fs::remove("streams_test_tmp"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/validation.cpp b/src/validation.cpp index 49b05f350a..1faaa411c4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -428,21 +428,134 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt return CheckInputs(tx, state, view, flags, cacheSigStore, true, txdata); } -/** - * @param[out] coins_to_uncache Return any outpoints which were not previously present in the - * coins cache, but were added as a result of validating the tx - * for mempool acceptance. This allows the caller to optionally - * remove the cache additions if the associated transaction ends - * up being rejected by the mempool. - */ -static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, - bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +namespace { + +class MemPoolAccept { - const CTransaction& tx = *ptx; - const uint256 hash = tx.GetHash(); - AssertLockHeld(cs_main); - LOCK(pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool()) +public: + MemPoolAccept(CTxMemPool& mempool) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&::ChainstateActive().CoinsTip(), m_pool), + m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)), + m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000), + m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)), + m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {} + + // We put the arguments we're handed into a struct, so we can pass them + // around easier. + struct ATMPArgs { + const CChainParams& m_chainparams; + CValidationState &m_state; + bool* m_missing_inputs; + const int64_t m_accept_time; + std::list<CTransactionRef>* m_replaced_transactions; + const bool m_bypass_limits; + const CAmount& m_absurd_fee; + /* + * Return any outpoints which were not previously present in the coins + * cache, but were added as a result of validating the tx for mempool + * acceptance. This allows the caller to optionally remove the cache + * additions if the associated transaction ends up being rejected by + * the mempool. + */ + std::vector<COutPoint>& m_coins_to_uncache; + const bool m_test_accept; + }; + + // Single transaction acceptance + bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + +private: + // All the intermediate state that gets passed between the various levels + // of checking a given transaction. + struct Workspace { + Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {} + std::set<uint256> m_conflicts; + CTxMemPool::setEntries m_all_conflicting; + CTxMemPool::setEntries m_ancestors; + std::unique_ptr<CTxMemPoolEntry> m_entry; + + bool m_replacement_transaction; + CAmount m_modified_fees; + CAmount m_conflicting_fees; + size_t m_conflicting_size; + + const CTransactionRef& m_ptx; + const uint256& m_hash; + }; + + // Run the policy checks on a given transaction, excluding any script checks. + // Looks up inputs, calculates feerate, considers replacement, evaluates + // package limits, etc. As this function can be invoked for "free" by a peer, + // only tests that are fast should be done here (to avoid CPU DoS). + bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); + + // Run the script checks using our policy flags. As this can be slow, we should + // only invoke this on transactions that have otherwise passed policy checks. + bool PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + // Re-run the script checks, using consensus flags, and try to cache the + // result in the scriptcache. This should be done after + // PolicyScriptChecks(). This requires that all inputs either be in our + // utxo set or in the mempool. + bool ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + // Try to add the transaction to the mempool, removing any conflicts first. + // Returns true if the transaction is in the mempool after any size + // limiting is performed, false otherwise. + bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); + + // Compare a package's feerate against minimum allowed. + bool CheckFeeRate(size_t package_size, CAmount package_fee, CValidationState& state) + { + CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size); + if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee)); + } + + if (package_fee < ::minRelayTxFee.GetFee(package_size)) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size))); + } + return true; + } + +private: + CTxMemPool& m_pool; + CCoinsViewCache m_view; + CCoinsViewMemPool m_viewmempool; + CCoinsView m_dummy; + + // The package limits in effect at the time of invocation. + const size_t m_limit_ancestors; + const size_t m_limit_ancestor_size; + // These may be modified while evaluating a transaction (eg to account for + // in-mempool conflicts; see below). + size_t m_limit_descendants; + size_t m_limit_descendant_size; +}; + +bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) +{ + const CTransactionRef& ptx = ws.m_ptx; + const CTransaction& tx = *ws.m_ptx; + const uint256& hash = ws.m_hash; + + // Copy/alias what we need out of args + CValidationState &state = args.m_state; + bool* pfMissingInputs = args.m_missing_inputs; + const int64_t nAcceptTime = args.m_accept_time; + const bool bypass_limits = args.m_bypass_limits; + const CAmount& nAbsurdFee = args.m_absurd_fee; + std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache; + + // Alias what we need out of ws + std::set<uint256>& setConflicts = ws.m_conflicts; + CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting; + CTxMemPool::setEntries& setAncestors = ws.m_ancestors; + std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry; + bool& fReplacementTransaction = ws.m_replacement_transaction; + CAmount& nModifiedFees = ws.m_modified_fees; + CAmount& nConflictingFees = ws.m_conflicting_fees; + size_t& nConflictingSize = ws.m_conflicting_size; + if (pfMissingInputs) { *pfMissingInputs = false; } @@ -461,7 +574,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Do not work on transactions that are too small. // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes. - // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead. + // Transactions smaller than this are not relayed to mitigate CVE-2017-12842 by not relaying + // 64-byte transactions. if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE) return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "tx-size-small"); @@ -472,15 +586,14 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? - if (pool.exists(hash)) { + if (m_pool.exists(hash)) { return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool"); } // Check for conflicts with in-memory transactions - std::set<uint256> setConflicts; for (const CTxIn &txin : tx.vin) { - const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout); + const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout); if (ptxConflicting) { if (!setConflicts.count(ptxConflicting->GetHash())) { @@ -514,395 +627,436 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } } - { - CCoinsView dummy; - CCoinsViewCache view(&dummy); - - LockPoints lp; - CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool viewMemPool(&coins_cache, pool); - view.SetBackend(viewMemPool); - - // do all inputs exist? - for (const CTxIn& txin : tx.vin) { - if (!coins_cache.HaveCoinInCache(txin.prevout)) { - coins_to_uncache.push_back(txin.prevout); - } + LockPoints lp; + m_view.SetBackend(m_viewmempool); - // Note: this call may add txin.prevout to the coins cache - // (CoinsTip().cacheCoins) by way of FetchCoin(). It should be removed - // later (via coins_to_uncache) if this tx turns out to be invalid. - if (!view.HaveCoin(txin.prevout)) { - // Are inputs missing because we already have the tx? - for (size_t out = 0; out < tx.vout.size(); out++) { - // Optimistically just do efficient check of cache for outputs - if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known"); - } - } - // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet - if (pfMissingInputs) { - *pfMissingInputs = true; + CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip(); + // do all inputs exist? + for (const CTxIn& txin : tx.vin) { + if (!coins_cache.HaveCoinInCache(txin.prevout)) { + coins_to_uncache.push_back(txin.prevout); + } + + // Note: this call may add txin.prevout to the coins cache + // (coins_cache.cacheCoins) by way of FetchCoin(). It should be removed + // later (via coins_to_uncache) if this tx turns out to be invalid. + if (!m_view.HaveCoin(txin.prevout)) { + // Are inputs missing because we already have the tx? + for (size_t out = 0; out < tx.vout.size(); out++) { + // Optimistically just do efficient check of cache for outputs + if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { + return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known"); } - return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } + // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet + if (pfMissingInputs) { + *pfMissingInputs = true; + } + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } + } - // Bring the best block into scope - view.GetBestBlock(); + // Bring the best block into scope + m_view.GetBestBlock(); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); + // we have all inputs cached now, so switch back to dummy (to protect + // against bugs where we pull more inputs from disk that miss being added + // to coins_to_uncache) + m_view.SetBackend(m_dummy); - // Only accept BIP68 sequence locked transactions that can be mined in the next - // block; we don't want our mempool filled up with transactions that can't - // be mined yet. - // Must keep pool.cs for this unless we change CheckSequenceLocks to take a - // CoinsViewCache instead of create its own - if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final"); + // Only accept BIP68 sequence locked transactions that can be mined in the next + // block; we don't want our mempool filled up with transactions that can't + // be mined yet. + // Must keep pool.cs for this unless we change CheckSequenceLocks to take a + // CoinsViewCache instead of create its own + if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) + return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final"); - CAmount nFees = 0; - if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { - return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); - } + CAmount nFees = 0; + if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + } - // Check for non-standard pay-to-script-hash in inputs - if (fRequireStandard && !AreInputsStandard(tx, view)) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); + // Check for non-standard pay-to-script-hash in inputs + if (fRequireStandard && !AreInputsStandard(tx, m_view)) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); - // Check for non-standard witness in P2WSH - if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view)) - return state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, REJECT_NONSTANDARD, "bad-witness-nonstandard"); + // Check for non-standard witness in P2WSH + if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view)) + return state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, REJECT_NONSTANDARD, "bad-witness-nonstandard"); - int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS); - // nModifiedFees includes any fee deltas from PrioritiseTransaction - CAmount nModifiedFees = nFees; - pool.ApplyDelta(hash, nModifiedFees); + // nModifiedFees includes any fee deltas from PrioritiseTransaction + nModifiedFees = nFees; + m_pool.ApplyDelta(hash, nModifiedFees); - // Keep track of transactions that spend a coinbase, which we re-scan - // during reorgs to ensure COINBASE_MATURITY is still met. - bool fSpendsCoinbase = false; - for (const CTxIn &txin : tx.vin) { - const Coin &coin = view.AccessCoin(txin.prevout); - if (coin.IsCoinBase()) { - fSpendsCoinbase = true; - break; - } + // Keep track of transactions that spend a coinbase, which we re-scan + // during reorgs to ensure COINBASE_MATURITY is still met. + bool fSpendsCoinbase = false; + for (const CTxIn &txin : tx.vin) { + const Coin &coin = m_view.AccessCoin(txin.prevout); + if (coin.IsCoinBase()) { + fSpendsCoinbase = true; + break; } + } - CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), - fSpendsCoinbase, nSigOpsCost, lp); - unsigned int nSize = entry.GetTxSize(); + entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), + fSpendsCoinbase, nSigOpsCost, lp)); + unsigned int nSize = entry->GetTxSize(); - if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", + if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", strprintf("%d", nSigOpsCost)); - CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); - } + // No transactions are allowed below minRelayTxFee except from disconnected + // blocks + if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false; - // No transactions are allowed below minRelayTxFee except from disconnected blocks - if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize))); - } - - if (nAbsurdFee && nFees > nAbsurdFee) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, + if (nAbsurdFee && nFees > nAbsurdFee) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee)); - const CTxMemPool::setEntries setIterConflicting = pool.GetIterSet(setConflicts); - // Calculate in-mempool ancestors, up to a limit. - CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; - size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; - - if (setConflicts.size() == 1) { - // In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we - // would meet the chain limits after the conflicts have been removed. However, there isn't a practical - // way to do this short of calculating the ancestor and descendant sets with an overlay cache of - // changed mempool entries. Due to both implementation and runtime complexity concerns, this isn't - // very realistic, thus we only ensure a limited set of transactions are RBF'able despite mempool - // conflicts here. Importantly, we need to ensure that some transactions which were accepted using - // the below carve-out are able to be RBF'ed, without impacting the security the carve-out provides - // for off-chain contract systems (see link in the comment below). - // - // Specifically, the subset of RBF transactions which we allow despite chain limits are those which - // conflict directly with exactly one other transaction (but may evict children of said transaction), - // and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies" - // check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is - // amended, we may need to move that check to here instead of removing it wholesale. - // - // Such transactions are clearly not merging any existing packages, so we are only concerned with - // ensuring that (a) no package is growing past the package size (not count) limits and (b) we are - // not allowing something to effectively use the (below) carve-out spot when it shouldn't be allowed - // to. - // - // To check these we first check if we meet the RBF criteria, above, and increment the descendant - // limits by the direct conflict and its descendants (as these are recalculated in - // CalculateMempoolAncestors by assuming the new transaction being added is a new descendant, with no - // removals, of each parent's existing dependant set). The ancestor count limits are unmodified (as - // the ancestor limits should be the same for both our new transaction and any conflicts). - // We don't bother incrementing nLimitDescendants by the full removal count as that limit never comes - // into force here (as we're only adding a single transaction). - assert(setIterConflicting.size() == 1); - CTxMemPool::txiter conflict = *setIterConflicting.begin(); - - nLimitDescendants += 1; - nLimitDescendantSize += conflict->GetSizeWithDescendants(); - } - - std::string errString; - if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { - setAncestors.clear(); - // If CalculateMemPoolAncestors fails second time, we want the original error string. - std::string dummy_err_string; - // Contracting/payment channels CPFP carve-out: - // If the new transaction is relatively small (up to 40k weight) - // and has at most one ancestor (ie ancestor limit of 2, including - // the new transaction), allow it if its parent has exactly the - // descendant limit descendants. - // - // This allows protocols which rely on distrusting counterparties - // being able to broadcast descendants of an unconfirmed transaction - // to be secure by simply only having two immediately-spendable - // outputs - one for each counterparty. For more info on the uses for - // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html - if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || - !pool.CalculateMemPoolAncestors(entry, setAncestors, 2, nLimitAncestorSize, nLimitDescendants + 1, nLimitDescendantSize + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString); - } - } - - // A transaction that spends outputs that would be replaced by it is invalid. Now - // that we have the set of all ancestors we can detect this - // pathological case by making sure setConflicts and setAncestors don't - // intersect. - for (CTxMemPool::txiter ancestorIt : setAncestors) + const CTxMemPool::setEntries setIterConflicting = m_pool.GetIterSet(setConflicts); + // Calculate in-mempool ancestors, up to a limit. + if (setConflicts.size() == 1) { + // In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we + // would meet the chain limits after the conflicts have been removed. However, there isn't a practical + // way to do this short of calculating the ancestor and descendant sets with an overlay cache of + // changed mempool entries. Due to both implementation and runtime complexity concerns, this isn't + // very realistic, thus we only ensure a limited set of transactions are RBF'able despite mempool + // conflicts here. Importantly, we need to ensure that some transactions which were accepted using + // the below carve-out are able to be RBF'ed, without impacting the security the carve-out provides + // for off-chain contract systems (see link in the comment below). + // + // Specifically, the subset of RBF transactions which we allow despite chain limits are those which + // conflict directly with exactly one other transaction (but may evict children of said transaction), + // and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies" + // check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is + // amended, we may need to move that check to here instead of removing it wholesale. + // + // Such transactions are clearly not merging any existing packages, so we are only concerned with + // ensuring that (a) no package is growing past the package size (not count) limits and (b) we are + // not allowing something to effectively use the (below) carve-out spot when it shouldn't be allowed + // to. + // + // To check these we first check if we meet the RBF criteria, above, and increment the descendant + // limits by the direct conflict and its descendants (as these are recalculated in + // CalculateMempoolAncestors by assuming the new transaction being added is a new descendant, with no + // removals, of each parent's existing dependant set). The ancestor count limits are unmodified (as + // the ancestor limits should be the same for both our new transaction and any conflicts). + // We don't bother incrementing m_limit_descendants by the full removal count as that limit never comes + // into force here (as we're only adding a single transaction). + assert(setIterConflicting.size() == 1); + CTxMemPool::txiter conflict = *setIterConflicting.begin(); + + m_limit_descendants += 1; + m_limit_descendant_size += conflict->GetSizeWithDescendants(); + } + + std::string errString; + if (!m_pool.CalculateMemPoolAncestors(*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) { + setAncestors.clear(); + // If CalculateMemPoolAncestors fails second time, we want the original error string. + std::string dummy_err_string; + // Contracting/payment channels CPFP carve-out: + // If the new transaction is relatively small (up to 40k weight) + // and has at most one ancestor (ie ancestor limit of 2, including + // the new transaction), allow it if its parent has exactly the + // descendant limit descendants. + // + // This allows protocols which rely on distrusting counterparties + // being able to broadcast descendants of an unconfirmed transaction + // to be secure by simply only having two immediately-spendable + // outputs - one for each counterparty. For more info on the uses for + // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html + if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || + !m_pool.CalculateMemPoolAncestors(*entry, setAncestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString); + } + } + + // A transaction that spends outputs that would be replaced by it is invalid. Now + // that we have the set of all ancestors we can detect this + // pathological case by making sure setConflicts and setAncestors don't + // intersect. + for (CTxMemPool::txiter ancestorIt : setAncestors) + { + const uint256 &hashAncestor = ancestorIt->GetTx().GetHash(); + if (setConflicts.count(hashAncestor)) { - const uint256 &hashAncestor = ancestorIt->GetTx().GetHash(); - if (setConflicts.count(hashAncestor)) - { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-spends-conflicting-tx", - strprintf("%s spends conflicting transaction %s", - hash.ToString(), - hashAncestor.ToString())); - } + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-spends-conflicting-tx", + strprintf("%s spends conflicting transaction %s", + hash.ToString(), + hashAncestor.ToString())); } + } - // Check if it's economically rational to mine this transaction rather - // than the ones it replaces. - CAmount nConflictingFees = 0; - size_t nConflictingSize = 0; - uint64_t nConflictingCount = 0; - CTxMemPool::setEntries allConflicting; - - // If we don't hold the lock allConflicting might be incomplete; the - // subsequent RemoveStaged() and addUnchecked() calls don't guarantee - // mempool consistency for us. - const bool fReplacementTransaction = setConflicts.size(); - if (fReplacementTransaction) - { - CFeeRate newFeeRate(nModifiedFees, nSize); - std::set<uint256> setConflictsParents; - const int maxDescendantsToVisit = 100; - for (const auto& mi : setIterConflicting) { - // Don't allow the replacement to reduce the feerate of the - // mempool. - // - // We usually don't want to accept replacements with lower - // feerates than what they replaced as that would lower the - // feerate of the next block. Requiring that the feerate always - // be increased is also an easy-to-reason about way to prevent - // DoS attacks via replacements. - // - // We only consider the feerates of transactions being directly - // replaced, not their indirect descendants. While that does - // mean high feerate children are ignored when deciding whether - // or not to replace, we do require the replacement to pay more - // overall fees too, mitigating most cases. - CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); - if (newFeeRate <= oldFeeRate) - { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee", - strprintf("rejecting replacement %s; new feerate %s <= old feerate %s", - hash.ToString(), - newFeeRate.ToString(), - oldFeeRate.ToString())); - } - - for (const CTxIn &txin : mi->GetTx().vin) - { - setConflictsParents.insert(txin.prevout.hash); - } + // Check if it's economically rational to mine this transaction rather + // than the ones it replaces. + nConflictingFees = 0; + nConflictingSize = 0; + uint64_t nConflictingCount = 0; - nConflictingCount += mi->GetCountWithDescendants(); - } - // This potentially overestimates the number of actual descendants - // but we just want to be conservative to avoid doing too much - // work. - if (nConflictingCount <= maxDescendantsToVisit) { - // If not too many to replace, then calculate the set of - // transactions that would have to be evicted - for (CTxMemPool::txiter it : setIterConflicting) { - pool.CalculateDescendants(it, allConflicting); - } - for (CTxMemPool::txiter it : allConflicting) { - nConflictingFees += it->GetModifiedFee(); - nConflictingSize += it->GetTxSize(); - } - } else { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too many potential replacements", - strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n", + // If we don't hold the lock allConflicting might be incomplete; the + // subsequent RemoveStaged() and addUnchecked() calls don't guarantee + // mempool consistency for us. + fReplacementTransaction = setConflicts.size(); + if (fReplacementTransaction) + { + CFeeRate newFeeRate(nModifiedFees, nSize); + std::set<uint256> setConflictsParents; + const int maxDescendantsToVisit = 100; + for (const auto& mi : setIterConflicting) { + // Don't allow the replacement to reduce the feerate of the + // mempool. + // + // We usually don't want to accept replacements with lower + // feerates than what they replaced as that would lower the + // feerate of the next block. Requiring that the feerate always + // be increased is also an easy-to-reason about way to prevent + // DoS attacks via replacements. + // + // We only consider the feerates of transactions being directly + // replaced, not their indirect descendants. While that does + // mean high feerate children are ignored when deciding whether + // or not to replace, we do require the replacement to pay more + // overall fees too, mitigating most cases. + CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); + if (newFeeRate <= oldFeeRate) + { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee", + strprintf("rejecting replacement %s; new feerate %s <= old feerate %s", hash.ToString(), - nConflictingCount, - maxDescendantsToVisit)); + newFeeRate.ToString(), + oldFeeRate.ToString())); } - for (unsigned int j = 0; j < tx.vin.size(); j++) + for (const CTxIn &txin : mi->GetTx().vin) { - // We don't want to accept replacements that require low - // feerate junk to be mined first. Ideally we'd keep track of - // the ancestor feerates and make the decision based on that, - // but for now requiring all new inputs to be confirmed works. - // - // Note that if you relax this to make RBF a little more useful, - // this may break the CalculateMempoolAncestors RBF relaxation, - // above. See the comment above the first CalculateMempoolAncestors - // call for more info. - if (!setConflictsParents.count(tx.vin[j].prevout.hash)) - { - // Rather than check the UTXO set - potentially expensive - - // it's cheaper to just check if the new input refers to a - // tx that's in the mempool. - if (pool.exists(tx.vin[j].prevout.hash)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "replacement-adds-unconfirmed", - strprintf("replacement %s adds unconfirmed input, idx %d", - hash.ToString(), j)); - } - } + setConflictsParents.insert(txin.prevout.hash); } - // The replacement must pay greater fees than the transactions it - // replaces - if we did the bandwidth used by those conflicting - // transactions would not be paid for. - if (nModifiedFees < nConflictingFees) - { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee", - strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s", - hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees))); + nConflictingCount += mi->GetCountWithDescendants(); + } + // This potentially overestimates the number of actual descendants + // but we just want to be conservative to avoid doing too much + // work. + if (nConflictingCount <= maxDescendantsToVisit) { + // If not too many to replace, then calculate the set of + // transactions that would have to be evicted + for (CTxMemPool::txiter it : setIterConflicting) { + m_pool.CalculateDescendants(it, allConflicting); } - - // Finally in addition to paying more fees than the conflicts the - // new transaction must pay for its own bandwidth. - CAmount nDeltaFees = nModifiedFees - nConflictingFees; - if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize)) - { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee", - strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", - hash.ToString(), - FormatMoney(nDeltaFees), - FormatMoney(::incrementalRelayFee.GetFee(nSize)))); + for (CTxMemPool::txiter it : allConflicting) { + nConflictingFees += it->GetModifiedFee(); + nConflictingSize += it->GetTxSize(); } + } else { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too many potential replacements", + strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n", + hash.ToString(), + nConflictingCount, + maxDescendantsToVisit)); } - constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; - - // Check against previous transactions - // The first loop above does all the inexpensive checks. - // Only if ALL inputs pass do we perform expensive ECDSA signature checks. - // Helps prevent CPU exhaustion denial-of-service attacks. - PrecomputedTransactionData txdata(tx); - if (!CheckInputs(tx, state, view, scriptVerifyFlags, true, false, txdata)) { - // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we - // need to turn both off, and compare against just turning off CLEANSTACK - // to see if the failure is specifically due to witness validation. - CValidationState stateDummy; // Want reported failures to be from first CheckInputs - if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) && - !CheckInputs(tx, stateDummy, view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) { - // Only the witness is missing, so the transaction itself may be fine. - state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, - state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage()); + for (unsigned int j = 0; j < tx.vin.size(); j++) + { + // We don't want to accept replacements that require low + // feerate junk to be mined first. Ideally we'd keep track of + // the ancestor feerates and make the decision based on that, + // but for now requiring all new inputs to be confirmed works. + // + // Note that if you relax this to make RBF a little more useful, + // this may break the CalculateMempoolAncestors RBF relaxation, + // above. See the comment above the first CalculateMempoolAncestors + // call for more info. + if (!setConflictsParents.count(tx.vin[j].prevout.hash)) + { + // Rather than check the UTXO set - potentially expensive - + // it's cheaper to just check if the new input refers to a + // tx that's in the mempool. + if (m_pool.exists(tx.vin[j].prevout.hash)) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "replacement-adds-unconfirmed", + strprintf("replacement %s adds unconfirmed input, idx %d", + hash.ToString(), j)); + } } - assert(IsTransactionReason(state.GetReason())); - return false; // state filled in by CheckInputs } - // Check again against the current block tip's script verification - // flags to cache our script execution flags. This is, of course, - // useless if the next block has different script flags from the - // previous one, but because the cache tracks script flags for us it - // will auto-invalidate and we'll just have a few blocks of extra - // misses on soft-fork activation. - // - // This is also useful in case of bugs in the standard flags that cause - // transactions to pass as valid when they're actually invalid. For - // instance the STRICTENC flag was incorrectly allowing certain - // CHECKSIG NOT scripts to pass, even though they were invalid. - // - // There is a similar check in CreateNewBlock() to prevent creating - // invalid blocks (using TestBlockValidity), however allowing such - // transactions into the mempool can be exploited as a DoS attack. - unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus()); - if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) { - return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s", - __func__, hash.ToString(), FormatStateMessage(state)); - } - - if (test_accept) { - // Tx was accepted, but not added - return true; + // The replacement must pay greater fees than the transactions it + // replaces - if we did the bandwidth used by those conflicting + // transactions would not be paid for. + if (nModifiedFees < nConflictingFees) + { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee", + strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s", + hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees))); } - // Remove conflicting transactions from the mempool - for (CTxMemPool::txiter it : allConflicting) + // Finally in addition to paying more fees than the conflicts the + // new transaction must pay for its own bandwidth. + CAmount nDeltaFees = nModifiedFees - nConflictingFees; + if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize)) { - LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", - it->GetTx().GetHash().ToString(), - hash.ToString(), - FormatMoney(nModifiedFees - nConflictingFees), - (int)nSize - (int)nConflictingSize); - if (plTxnReplaced) - plTxnReplaced->push_back(it->GetSharedTx()); + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "insufficient fee", + strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", + hash.ToString(), + FormatMoney(nDeltaFees), + FormatMoney(::incrementalRelayFee.GetFee(nSize)))); } - pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); + } + return true; +} + +bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) +{ + const CTransaction& tx = *ws.m_ptx; - // This transaction should only count for fee estimation if: - // - it isn't a BIP 125 replacement transaction (may not be widely supported) - // - it's not being re-added during a reorg which bypasses typical mempool fee limits - // - the node is not behind - // - the transaction is not dependent on any other transactions in the mempool - bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + CValidationState &state = args.m_state; - // Store transaction in memory - pool.addUnchecked(entry, setAncestors, validForFeeEstimation); + constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; - // trim mempool and check if tx was trimmed - if (!bypass_limits) { - LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - if (!pool.exists(hash)) - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full"); + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata)) { + // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we + // need to turn both off, and compare against just turning off CLEANSTACK + // to see if the failure is specifically due to witness validation. + CValidationState stateDummy; // Want reported failures to be from first CheckInputs + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) && + !CheckInputs(tx, stateDummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) { + // Only the witness is missing, so the transaction itself may be fine. + state.Invalid(ValidationInvalidReason::TX_WITNESS_MUTATED, false, + state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage()); } + assert(IsTransactionReason(state.GetReason())); + return false; // state filled in by CheckInputs } + return true; +} + +bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) +{ + const CTransaction& tx = *ws.m_ptx; + const uint256& hash = ws.m_hash; + + CValidationState &state = args.m_state; + const CChainParams& chainparams = args.m_chainparams; + + // Check again against the current block tip's script verification + // flags to cache our script execution flags. This is, of course, + // useless if the next block has different script flags from the + // previous one, but because the cache tracks script flags for us it + // will auto-invalidate and we'll just have a few blocks of extra + // misses on soft-fork activation. + // + // This is also useful in case of bugs in the standard flags that cause + // transactions to pass as valid when they're actually invalid. For + // instance the STRICTENC flag was incorrectly allowing certain + // CHECKSIG NOT scripts to pass, even though they were invalid. + // + // There is a similar check in CreateNewBlock() to prevent creating + // invalid blocks (using TestBlockValidity), however allowing such + // transactions into the mempool can be exploited as a DoS attack. + unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus()); + if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, true, txdata)) { + return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); + } + + return true; +} + +bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws) +{ + const CTransaction& tx = *ws.m_ptx; + const uint256& hash = ws.m_hash; + CValidationState &state = args.m_state; + const bool bypass_limits = args.m_bypass_limits; + + CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting; + CTxMemPool::setEntries& setAncestors = ws.m_ancestors; + const CAmount& nModifiedFees = ws.m_modified_fees; + const CAmount& nConflictingFees = ws.m_conflicting_fees; + const size_t& nConflictingSize = ws.m_conflicting_size; + const bool fReplacementTransaction = ws.m_replacement_transaction; + std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry; + + // Remove conflicting transactions from the mempool + for (CTxMemPool::txiter it : allConflicting) + { + LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", + it->GetTx().GetHash().ToString(), + hash.ToString(), + FormatMoney(nModifiedFees - nConflictingFees), + (int)entry->GetTxSize() - (int)nConflictingSize); + if (args.m_replaced_transactions) + args.m_replaced_transactions->push_back(it->GetSharedTx()); + } + m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); + + // This transaction should only count for fee estimation if: + // - it isn't a BIP 125 replacement transaction (may not be widely supported) + // - it's not being re-added during a reorg which bypasses typical mempool fee limits + // - the node is not behind + // - the transaction is not dependent on any other transactions in the mempool + bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && m_pool.HasNoInputsOf(tx); + + // Store transaction in memory + m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation); + + // trim mempool and check if tx was trimmed + if (!bypass_limits) { + LimitMempoolSize(m_pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (!m_pool.exists(hash)) + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full"); + } + return true; +} + +bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) +{ + AssertLockHeld(cs_main); + LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool()) + + Workspace workspace(ptx); + + if (!PreChecks(args, workspace)) return false; + + // Only compute the precomputed transaction data if we need to verify + // scripts (ie, other policy checks pass). We perform the inexpensive + // checks first and avoid hashing and signature verification unless those + // checks pass, to mitigate CPU exhaustion denial-of-service attacks. + PrecomputedTransactionData txdata(*ptx); + + if (!PolicyScriptChecks(args, workspace, txdata)) return false; + + if (!ConsensusScriptChecks(args, workspace, txdata)) return false; + + // Tx was accepted, but not added + if (args.m_test_accept) return true; + + if (!Finalize(args, workspace)) return false; + GetMainSignals().TransactionAddedToMempool(ptx); return true; } +} // anon namespace + /** (try to) add transaction to memory pool with a specified acceptance time **/ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::vector<COutPoint> coins_to_uncache; - bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept); + MemPoolAccept::ATMPArgs args { chainparams, state, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept }; + bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args); if (!res) { // Remove coins that were not present in the coins cache before calling ATMPW; // this is to prevent memory DoS in case we receive a large number of @@ -1667,7 +1821,8 @@ public: bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { - return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + return pindex->nHeight >= params.MinBIP9WarningHeight && + ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && ((pindex->nVersion >> bit) & 1) != 0 && ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; } @@ -1828,7 +1983,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // If such overwrites are allowed, coinbases and transactions depending upon those // can be duplicated to remove the ability to spend the first instance -- even after // being sent to another address. - // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information. // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool // already refuses previously-known transaction ids entirely. // This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC. @@ -2508,6 +2663,8 @@ void CChainState::PruneBlockIndexCandidates() { /** * Try to make some progress towards making pindexMostWork the active block. * pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork. + * + * @returns true unless a system error occurred */ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) { @@ -2627,15 +2784,6 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) { } } -/** - * Make the best chain active, in multiple steps. The result is either failure - * or an activated best chain. pblock is either nullptr or a pointer to a block - * that is already loaded (to avoid loading it again from disk). - * - * ActivateBestChain is split into steps (see ActivateBestChainStep) so that - * we avoid holding cs_main for an extended period of time; the length of this - * call may be quite long during reindexing or a substantial reorg. - */ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, 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 @@ -2683,8 +2831,10 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& 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, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) { + // A system error occurred return false; + } blocks_connected = true; if (fInvalidFound) { @@ -3105,6 +3255,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-multiple", "more than one coinbase"); // Check transactions + // Must check for duplicate inputs (see CVE-2018-17144) for (const auto& tx : block.vtx) if (!CheckTransaction(*tx, state, true)) return state.Invalid(state.GetReason(), false, state.GetRejectCode(), state.GetRejectReason(), @@ -3130,9 +3281,7 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa return (height >= params.SegwitHeight); } -// Compute at which vout of the block's coinbase transaction the witness -// commitment occurs, or -1 if not found. -static int GetWitnessCommitmentIndex(const CBlock& block) +int GetWitnessCommitmentIndex(const CBlock& block) { int commitpos = -1; if (!block.vtx.empty()) { @@ -3937,28 +4086,31 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE return true; } -bool LoadChainTip(const CChainParams& chainparams) +bool CChainState::LoadChainTip(const CChainParams& chainparams) { AssertLockHeld(cs_main); - const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip(); + const CCoinsViewCache& coins_cache = CoinsTip(); assert(!coins_cache.GetBestBlock().IsNull()); // Never called when the coins view is empty + const CBlockIndex* tip = m_chain.Tip(); - if (::ChainActive().Tip() && - ::ChainActive().Tip()->GetBlockHash() == coins_cache.GetBestBlock()) return true; + if (tip && tip->GetBlockHash() == coins_cache.GetBestBlock()) { + return true; + } // Load pointer to end of best chain CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock()); if (!pindex) { return false; } - ::ChainActive().SetTip(pindex); - - ::ChainstateActive().PruneBlockIndexCandidates(); + m_chain.SetTip(pindex); + PruneBlockIndexCandidates(); + tip = m_chain.Tip(); LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", - ::ChainActive().Tip()->GetBlockHash().ToString(), ::ChainActive().Height(), - FormatISO8601DateTime(::ChainActive().Tip()->GetBlockTime()), - GuessVerificationProgress(chainparams.TxData(), ::ChainActive().Tip())); + tip->GetBlockHash().ToString(), + m_chain.Height(), + FormatISO8601DateTime(tip->GetBlockTime()), + GuessVerificationProgress(chainparams.TxData(), tip)); return true; } @@ -4093,13 +4245,14 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i return true; } -bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view) +bool CChainState::ReplayBlocks(const CChainParams& params) { LOCK(cs_main); - CCoinsViewCache cache(view); + CCoinsView& db = this->CoinsDB(); + CCoinsViewCache cache(&db); - std::vector<uint256> hashHeads = view->GetHeadBlocks(); + std::vector<uint256> hashHeads = db.GetHeadBlocks(); if (hashHeads.empty()) return true; // We're already in a consistent state. if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state"); @@ -4159,10 +4312,6 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view) return true; } -bool ReplayBlocks(const CChainParams& params, CCoinsView* view) { - return ::ChainstateActive().ReplayBlocks(params, view); -} - //! Helper for CChainState::RewindBlockIndex void CChainState::EraseBlockData(CBlockIndex* index) { diff --git a/src/validation.h b/src/validation.h index 99850f71d9..96d249b6d3 100644 --- a/src/validation.h +++ b/src/validation.h @@ -211,7 +211,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; * @param[in] pblock The block we want to process. * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers. * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call - * @return True if state.IsValid() + * @returns If the block was processed, independently of block validity */ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main); @@ -240,8 +240,6 @@ bool LoadGenesisBlock(const CChainParams& chainparams); /** 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); -/** Update the chain tip based on database information. */ -bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Unload database information */ void UnloadBlockIndex(); /** Run an instance of the script checking thread */ @@ -386,6 +384,9 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa /** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */ bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main); +/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */ +int GetWitnessCommitmentIndex(const CBlock& block); + /** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); @@ -400,9 +401,6 @@ public: bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); }; -/** Replay blocks that aren't fully applied to the database. */ -bool ReplayBlocks(const CChainParams& params, CCoinsView* view); - CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Find the last common block between the parameter chain and a locator. */ @@ -653,6 +651,8 @@ public: * * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything * besides checking if we need to prune. + * + * @returns true unless a system error occurred */ bool FlushStateToDisk( const CChainParams& chainparams, @@ -667,7 +667,24 @@ public: //! if we pruned. void PruneAndFlush(); - bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main); + /** + * Make the best chain active, in multiple steps. The result is either failure + * or an activated best chain. pblock is either nullptr or a pointer to a block + * that is already loaded (to avoid loading it again from disk). + * + * ActivateBestChain is split into steps (see ActivateBestChainStep) so that + * we avoid holding cs_main for an extended period of time; the length of this + * call may be quite long during reindexing or a substantial reorg. + * + * May not be called with cs_main held. May not be called in a + * validationinterface callback. + * + * @returns true unless a system error occurred + */ + bool ActivateBestChain( + CValidationState& state, + const CChainParams& chainparams, + std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main); bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -684,7 +701,8 @@ public: bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool ReplayBlocks(const CChainParams& params, CCoinsView* view); + /** Replay blocks that aren't fully applied to the database. */ + bool ReplayBlocks(const CChainParams& params); bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main); bool LoadGenesisBlock(const CChainParams& chainparams); @@ -702,6 +720,9 @@ public: */ void CheckBlockIndex(const Consensus::Params& consensusParams); + /** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */ + bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + private: bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs); bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9952e868f9..216205ed61 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1648,8 +1648,10 @@ static UniValue gettransaction(const JSONRPCRequest& request) "\nGet detailed information about in-wallet transaction <txid>\n", { {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"}, - {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses in balance calculation and details[]"}, - {"verbose", RPCArg::Type::BOOL, /* default */ "false", "Whether to add a field with additional transaction details"}, + {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", + "Whether to include watch-only addresses in balance calculation and details[]"}, + {"verbose", RPCArg::Type::BOOL, /* default */ "false", + "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"}, }, RPCResult{ "{\n" @@ -1685,7 +1687,8 @@ static UniValue gettransaction(const JSONRPCRequest& request) " ,...\n" " ],\n" " \"hex\" : \"data\" (string) Raw data for transaction\n" - " \"details\" : transaction (json object) Optional, additional transaction details. This object contains the same transaction details as the `getrawtransaction` RPC method\n" + " \"decoded\" : transaction (json object) Optional, the decoded transaction (only present when `verbose` is passed), equivalent to the\n" + " RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed.\n" "}\n" }, RPCExamples{ @@ -1739,9 +1742,9 @@ static UniValue gettransaction(const JSONRPCRequest& request) entry.pushKV("hex", strHex); if (verbose) { - UniValue details(UniValue::VOBJ); - TxToUniv(*wtx.tx, uint256(), details, false); - entry.pushKV("details", details); + UniValue decoded(UniValue::VOBJ); + TxToUniv(*wtx.tx, uint256(), decoded, false); + entry.pushKV("decoded", decoded); } return entry; |