diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/addrman.cpp | 10 | ||||
-rw-r--r-- | src/bench/coin_selection.cpp | 29 | ||||
-rw-r--r-- | src/hash.h | 10 | ||||
-rw-r--r-- | src/net.cpp | 20 | ||||
-rw-r--r-- | src/net.h | 38 | ||||
-rw-r--r-- | src/netbase.cpp | 4 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 8 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 2 | ||||
-rw-r--r-- | src/qt/overviewpage.cpp | 32 | ||||
-rw-r--r-- | src/test/addrman_tests.cpp | 2 | ||||
-rw-r--r-- | src/uint256.h | 11 | ||||
-rw-r--r-- | src/validation.cpp | 8 | ||||
-rw-r--r-- | src/validation.h | 6 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 44 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 11 | ||||
-rw-r--r-- | src/wallet/wallet.h | 3 |
16 files changed, 149 insertions, 89 deletions
diff --git a/src/addrman.cpp b/src/addrman.cpp index 093b263ab3..44328c3056 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -11,22 +11,22 @@ int CAddrInfo::GetTriedBucket(const uint256& nKey) const { - uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash(); - uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetCheapHash(); + uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash(); + uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash(); return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; } int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src) const { std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(); - uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetHash().GetCheapHash(); - uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetHash().GetCheapHash(); + uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetCheapHash(); + uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash(); return hash2 % ADDRMAN_NEW_BUCKET_COUNT; } int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const { - uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetHash().GetCheapHash(); + uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetCheapHash(); return hash1 % ADDRMAN_BUCKET_SIZE; } diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 8552ed34fd..74641191a1 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -4,25 +4,19 @@ #include <bench/bench.h> #include <interfaces/chain.h> -#include <wallet/wallet.h> #include <wallet/coinselection.h> +#include <wallet/wallet.h> #include <set> -static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<OutputGroup>& groups) +static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<std::unique_ptr<CWalletTx>>& wtxs) { - int nInput = 0; - static int nextLockTime = 0; CMutableTransaction tx; tx.nLockTime = nextLockTime++; // so all transactions get different hashes - tx.vout.resize(nInput + 1); - tx.vout[nInput].nValue = nValue; - CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx))); - - int nAge = 6 * 24; - COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); - groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0); + tx.vout.resize(1); + tx.vout[0].nValue = nValue; + wtxs.push_back(MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)))); } // Simple benchmark for wallet coin selection. Note that it maybe be necessary @@ -36,14 +30,21 @@ static void CoinSelection(benchmark::State& state) { auto chain = interfaces::MakeChain(); const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy()); + std::vector<std::unique_ptr<CWalletTx>> wtxs; LOCK(wallet.cs_wallet); // Add coins. - std::vector<OutputGroup> groups; for (int i = 0; i < 1000; ++i) { - addCoin(1000 * COIN, wallet, groups); + addCoin(1000 * COIN, wallet, wtxs); + } + addCoin(3 * COIN, wallet, wtxs); + + // Create groups + std::vector<OutputGroup> groups; + for (const auto& wtx : wtxs) { + COutput output(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */); + groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0); } - addCoin(3 * COIN, wallet, groups); const CoinEligibilityFilter filter_standard(1, 6, 0); const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0); diff --git a/src/hash.h b/src/hash.h index 6acab0b161..c295568a3e 100644 --- a/src/hash.h +++ b/src/hash.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H +#include <crypto/common.h> #include <crypto/ripemd160.h> #include <crypto/sha256.h> #include <prevector.h> @@ -138,6 +139,15 @@ public: return result; } + /** + * Returns the first 64 bits from the resulting hash. + */ + inline uint64_t GetCheapHash() { + unsigned char result[CHash256::OUTPUT_SIZE]; + ctx.Finalize(result); + return ReadLE64(result); + } + template<typename T> CHashWriter& operator<<(const T& obj) { // Serialize to this stream diff --git a/src/net.cpp b/src/net.cpp index e065ac0f28..b85a8c2c1d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -82,8 +82,8 @@ bool fDiscover = true; bool fListen = true; bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; -std::map<CNetAddr, LocalServiceInfo> mapLocalHost; -static bool vfLimited[NET_MAX] = {}; +std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost); +static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {}; std::string strSubVersion; limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); @@ -715,7 +715,10 @@ void CNode::copyStats(CNodeStats &stats) X(nRecvBytes); } X(fWhitelisted); - X(minFeeFilter); + { + LOCK(cs_feeFilter); + X(minFeeFilter); + } // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. @@ -874,16 +877,7 @@ const uint256& CNetMessage::GetMessageHash() const return data_hash; } - - - - - - - - -// requires LOCK(cs_vSend) -size_t CConnman::SocketSendData(CNode *pnode) const +size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend) { auto it = pnode->vSendMsg.begin(); size_t nSentSize = 0; @@ -400,12 +400,12 @@ private: std::vector<ListenSocket> vhListenSocket; std::atomic<bool> fNetworkActive; - banmap_t setBanned; + banmap_t setBanned GUARDED_BY(cs_setBanned); CCriticalSection cs_setBanned; - bool setBannedIsDirty; + bool setBannedIsDirty GUARDED_BY(cs_setBanned); bool fAddressesInitialized; CAddrMan addrman; - std::deque<std::string> vOneShots; + std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots); CCriticalSection cs_vOneShots; std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes); CCriticalSection cs_vAddedNodes; @@ -540,7 +540,7 @@ struct LocalServiceInfo { }; extern CCriticalSection cs_mapLocalHost; -extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost; +extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost); typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes class CNodeStats @@ -630,23 +630,23 @@ class CNode public: // socket std::atomic<ServiceFlags> nServices; - SOCKET hSocket; + SOCKET hSocket GUARDED_BY(cs_hSocket); size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent - uint64_t nSendBytes; - std::deque<std::vector<unsigned char>> vSendMsg; + uint64_t nSendBytes GUARDED_BY(cs_vSend); + std::deque<std::vector<unsigned char>> vSendMsg GUARDED_BY(cs_vSend); CCriticalSection cs_vSend; CCriticalSection cs_hSocket; CCriticalSection cs_vRecv; CCriticalSection cs_vProcessMsg; - std::list<CNetMessage> vProcessMsg; + std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg); size_t nProcessQueueSize; CCriticalSection cs_sendProcessing; std::deque<CInv> vRecvGetData; - uint64_t nRecvBytes; + uint64_t nRecvBytes GUARDED_BY(cs_vRecv); std::atomic<int> nRecvVersion; std::atomic<int64_t> nLastSend; @@ -662,7 +662,7 @@ public: // to be printed out, displayed to humans in various forms and so on. So we sanitize it and // store the sanitized version in cleanSubVer. The original should be used when dealing with // the network or wire types and the cleaned string used when displayed or logged. - std::string strSubVer, cleanSubVer; + std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer); CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer bool fWhitelisted; // This peer can bypass DoS banning. bool fFeeler; // If true this node is being used as a short lived feeler. @@ -681,7 +681,7 @@ public: bool fSentAddr; CSemaphoreGrant grantOutbound; mutable CCriticalSection cs_filter; - std::unique_ptr<CBloomFilter> pfilter; + std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter); std::atomic<int> nRefCount; const uint64_t nKeyedNetGroup; @@ -690,7 +690,7 @@ public: protected: mapMsgCmdSize mapSendBytesPerMsgCmd; - mapMsgCmdSize mapRecvBytesPerMsgCmd; + mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); public: uint256 hashContinue; @@ -701,18 +701,18 @@ public: CRollingBloomFilter addrKnown; bool fGetAddr; std::set<uint256> setKnown; - int64_t nNextAddrSend; - int64_t nNextLocalAddrSend; + int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing); + int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing); // inventory based relay - CRollingBloomFilter filterInventoryKnown; + CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory); // Set of transaction ids we still have to announce. // They are sorted by the mempool before relay, so the order is not important. std::set<uint256> setInventoryTxToSend; // List of block ids we still have announce. // There is no final sorting before sending, as they are always sent immediately // and in the order requested. - std::vector<uint256> vInventoryBlockToSend; + std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory); CCriticalSection cs_inventory; std::set<uint256> setAskFor; std::multimap<int64_t, CInv> mapAskFor; @@ -741,7 +741,7 @@ public: // Whether a ping is requested. std::atomic<bool> fPingQueued; // Minimum fee rate with which to filter inv's to this node - CAmount minFeeFilter; + CAmount minFeeFilter GUARDED_BY(cs_feeFilter); CCriticalSection cs_feeFilter; CAmount lastSentFeeFilter; int64_t nextSendTimeFeeFilter; @@ -761,10 +761,10 @@ private: std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread mutable CCriticalSection cs_addrName; - std::string addrName; + std::string addrName GUARDED_BY(cs_addrName); // Our address, as reported by the peer - CService addrLocal; + CService addrLocal GUARDED_BY(cs_addrLocal); mutable CCriticalSection cs_addrLocal; public: diff --git a/src/netbase.cpp b/src/netbase.cpp index 6a750d5141..1c043fc981 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -26,9 +26,9 @@ #endif // Settings -static proxyType proxyInfo[NET_MAX]; -static proxyType nameProxy; static CCriticalSection cs_proxyInfos; +static proxyType proxyInfo[NET_MAX] GUARDED_BY(cs_proxyInfos); +static proxyType nameProxy GUARDED_BY(cs_proxyInfos); int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; bool fNameLookup = DEFAULT_NAME_LOOKUP; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ef82351551..ed705d6ba8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1089,10 +1089,10 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) return false; } -void BitcoinGUI::setHDStatus(int hdEnabled) +void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled) { - labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); - labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>")); + labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(privkeyDisabled ? ":/icons/eye" : hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>")); // eventually disable the QLabel to set its opacity to 50% labelWalletHDStatusIcon->setEnabled(hdEnabled); @@ -1138,7 +1138,7 @@ void BitcoinGUI::updateWalletStatus() } WalletModel * const walletModel = walletView->getWalletModel(); setEncryptionStatus(walletModel->getEncryptionStatus()); - setHDStatus(walletModel->wallet().hdEnabled()); + setHDStatus(walletModel->privateKeysDisabled(), walletModel->wallet().hdEnabled()); } #endif // ENABLE_WALLET diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e8b857c17c..aeff5dae30 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -223,7 +223,7 @@ private: @param[in] hdEnabled current hd enabled status @see WalletModel::EncryptionStatus */ - void setHDStatus(int hdEnabled); + void setHDStatus(bool privkeyDisabled, int hdEnabled); public Q_SLOTS: bool handlePaymentRequest(const SendCoinsRecipient& recipient); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 1db9609979..bec79335e7 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -161,15 +161,21 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances) { int unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways)); - ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways)); - ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); - ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); - + if (walletModel->privateKeysDisabled()) { + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); + ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); + } else { + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways)); + ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); + } // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users bool showImmature = balances.immature_balance != 0; @@ -178,7 +184,7 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances) // for symmetry reasons also show immature label when the watch-only one is shown ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); - ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance + ui->labelWatchImmature->setVisible(!walletModel->privateKeysDisabled() && showWatchOnlyImmature); // show watch-only immature balance } // show/hide watch-only labels @@ -231,8 +237,10 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); - updateWatchOnlyLabels(wallet.haveWatchOnly()); - connect(model, &WalletModel::notifyWatchonlyChanged, this, &OverviewPage::updateWatchOnlyLabels); + updateWatchOnlyLabels(wallet.haveWatchOnly() && !model->privateKeysDisabled()); + connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) { + updateWatchOnlyLabels(showWatchOnly && !walletModel->privateKeysDisabled()); + }); } // update the display unit, to not use the default ("BTC") diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 8c2873d916..55fe19cebe 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -34,7 +34,7 @@ public: int RandomInt(int nMax) override { - state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash(); + state = (CHashWriter(SER_GETHASH, 0) << state).GetCheapHash(); return (unsigned int)(state % nMax); } diff --git a/src/uint256.h b/src/uint256.h index 26a3331d92..97e0cfa015 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -12,7 +12,6 @@ #include <stdint.h> #include <string> #include <vector> -#include <crypto/common.h> /** Template base class for fixed-sized opaque blobs. */ template<unsigned int BITS> @@ -123,16 +122,6 @@ class uint256 : public base_blob<256> { public: uint256() {} explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {} - - /** A cheap hash function that just returns 64 bits from the result, it can be - * used when the contents are considered uniformly random. It is not appropriate - * when the value can easily be influenced from outside as e.g. a network adversary could - * provide values to trigger worst-case behavior. - */ - uint64_t GetCheapHash() const - { - return ReadLE64(data); - } }; /* uint256 from const char *. diff --git a/src/validation.cpp b/src/validation.cpp index 6333dd98d2..512a3619ca 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3530,12 +3530,14 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CBlockIndex *pindex = nullptr; if (fNewBlock) *fNewBlock = false; CValidationState state; - // Ensure that CheckBlock() passes before calling AcceptBlock, as - // belt-and-suspenders. - bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus()); + // CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race. + // Therefore, the following critical section must include the CheckBlock() call as well. LOCK(cs_main); + // Ensure that CheckBlock() passes before calling AcceptBlock, as + // belt-and-suspenders. + bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus()); if (ret) { // Store to disk ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); diff --git a/src/validation.h b/src/validation.h index 3e98ebc866..b5548a9293 100644 --- a/src/validation.h +++ b/src/validation.h @@ -12,6 +12,7 @@ #include <amount.h> #include <coins.h> +#include <crypto/common.h> // for ReadLE64 #include <fs.h> #include <protocol.h> // For CMessageHeader::MessageStartChars #include <policy/feerate.h> @@ -138,7 +139,10 @@ static const int DEFAULT_STOPATHEIGHT = 0; struct BlockHasher { - size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } + // this used to call `GetCheapHash()` in uint256, which was later moved; the + // cheap hash function simply calls ReadLE64() however, so the end result is + // identical + size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } }; extern CScript COINBASE_FLAGS; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index c6aac8aad5..623c5c39a2 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -17,6 +17,7 @@ #include <validation.h> #include <wallet/coincontrol.h> #include <wallet/test/wallet_test_fixture.h> +#include <policy/policy.h> #include <boost/test/unit_test.hpp> #include <univalue.h> @@ -394,4 +395,47 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup) BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false)); } +// Explicit calculation which is used to test the wallet constant +// We get the same virtual size due to rounding(weight/4) for both use_max_sig values +static size_t CalculateNestedKeyhashInputSize(bool use_max_sig) +{ + // Generate ephemeral valid pubkey + CKey key; + key.MakeNewKey(true); + CPubKey pubkey = key.GetPubKey(); + + // Generate pubkey hash + uint160 key_hash(Hash160(pubkey.begin(), pubkey.end())); + + // Create inner-script to enter into keystore. Key hash can't be 0... + CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end()); + + // Create outer P2SH script for the output + uint160 script_id(Hash160(inner_script.begin(), inner_script.end())); + CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL; + + // Add inner-script to key store and key to watchonly + CBasicKeyStore keystore; + keystore.AddCScript(inner_script); + keystore.AddKeyPubKey(key, pubkey); + + // Fill in dummy signatures for fee calculation. + SignatureData sig_data; + + if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) { + // We're hand-feeding it correct arguments; shouldn't happen + assert(false); + } + + CTxIn tx_in; + UpdateInput(tx_in, sig_data); + return (size_t)GetVirtualTransactionInputSize(tx_in); +} + +BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup) +{ + BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE); + BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 360d0f177c..d7798e005f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1530,8 +1530,6 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, CMutableTransaction txn; txn.vin.push_back(CTxIn(COutPoint())); if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) { - // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) - // implies that we can sign for every input. return -1; } return GetVirtualTransactionInputSize(txn.vin[0]); @@ -2755,7 +2753,14 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std if (pick_new_inputs) { nValueIn = 0; setCoins.clear(); - coin_selection_params.change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this); + int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this); + // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh + // as lower-bound to allow BnB to do it's thing + if (change_spend_size == -1) { + coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE; + } else { + coin_selection_params.change_spend_size = (size_t)change_spend_size; + } coin_selection_params.effective_fee = nFeeRateNeeded; if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used)) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f96798201f..4291163bea 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -85,6 +85,9 @@ static const bool DEFAULT_WALLET_RBF = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; +//! Pre-calculated constants for input size estimation in *virtual size* +static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91; + class CBlockIndex; class CCoinControl; class COutput; |