diff --git a/depends/README.md b/depends/README.md index a2a7b749c25e3..9f29498cdb918 100644 --- a/depends/README.md +++ b/depends/README.md @@ -12,15 +12,18 @@ For example: make HOST=x86_64-w64-mingw32 -j4 -**Dash Core's configure script by default will ignore the depends output.** In +**Dash Core's `configure` script by default will ignore the depends output.** In order for it to pick up libraries, tools, and settings from the depends build, -you must point it at the appropriate `--prefix` directory generated by the -build. In the above example, a prefix dir named x86_64-w64-mingw32 will be -created. To use it for Dash: +you must set the `CONFIG_SITE` environment variable to point to a `config.site` settings file. +In the above example, a file named `depends/x86_64-w64-mingw32/share/config.site` will be +created. To use it during compilation: - ./configure --prefix=$PWD/depends/x86_64-w64-mingw32 + CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure -Common `host-platform-triplets` for cross compilation are: +The default install prefix when using `config.site` is `--prefix=depends/`, +so depends build outputs will be installed in that location. + +Common `host-platform-triplet`s for cross compilation are: - `i686-pc-linux-gnu` for Linux 32 bit - `x86_64-pc-linux-gnu` for x86 Linux @@ -138,4 +141,3 @@ This is an example command for a default build with no disabled dependencies: - [description.md](description.md): General description of the depends system - [packages.md](packages.md): Steps for adding packages - diff --git a/doc/build-unix.md b/doc/build-unix.md index d5e33db56d31a..fe9075bbde4b4 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -331,7 +331,8 @@ To build executables for ARM: cd depends make HOST=arm-linux-gnueabihf NO_QT=1 cd .. - ./configure --prefix=$PWD/depends/arm-linux-gnueabihf --enable-reduce-exports LDFLAGS=-static-libstdc++ + ./autogen.sh + CONFIG_SITE=$PWD/depends/arm-linux-gnueabihf/share/config.site ./configure --enable-reduce-exports LDFLAGS=-static-libstdc++ make diff --git a/src/Makefile.test.include b/src/Makefile.test.include index f4e925714856e..bbd5543d95f9d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -182,6 +182,7 @@ BITCOIN_TESTS += \ wallet/test/coinjoin_tests.cpp \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/wallet_tests.cpp \ + wallet/test/walletdb_tests.cpp \ wallet/test/wallet_crypto_tests.cpp \ wallet/test/coinselector_tests.cpp \ wallet/test/init_tests.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index 091677c229123..446d0f12b745b 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector &asmap) const { @@ -115,12 +117,12 @@ CAddrInfo* CAddrMan::Find(const CService& addr, int* pnId) addr2.SetPort(0); } - std::map::iterator it = mapAddr.find(addr2); + const auto it = mapAddr.find(addr2); if (it == mapAddr.end()) return nullptr; if (pnId) *pnId = (*it).second; - std::map::iterator it2 = mapInfo.find((*it).second); + const auto it2 = mapInfo.find((*it).second); if (it2 != mapInfo.end()) return &(*it2).second; return nullptr; @@ -482,8 +484,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const int CAddrMan::Check_() { AssertLockHeld(cs); - std::set setTried; - std::map mapNew; + std::unordered_set setTried; + std::unordered_map mapNew; if (vRandom.size() != (size_t)(nTried + nNew)) return -7; diff --git a/src/addrman.h b/src/addrman.h index efbdbc051714f..7f28962595e26 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -8,22 +8,22 @@ #include #include +#include +#include #include #include #include +#include #include #include #include #include -#include -#include #include -#include #include #include #include -#include +#include #include /** @@ -257,7 +257,7 @@ class CAddrMan int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); s << nUBuckets; - std::map mapUnkIds; + std::unordered_map mapUnkIds; int nIds = 0; for (const auto& entry : mapInfo) { mapUnkIds[entry.first] = nIds; @@ -448,13 +448,13 @@ class CAddrMan // Prune new entries with refcount 0 (as a result of collisions). int nLostUnk = 0; - for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) { + for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) { if (it->second.fInTried == false && it->second.nRefCount == 0) { - std::map::const_iterator itCopy = it++; + const auto itCopy = it++; Delete(itCopy->first); - nLostUnk++; + ++nLostUnk; } else { - it++; + ++it; } } if (nLost + nLostUnk > 0) { @@ -682,10 +682,10 @@ class CAddrMan int nIdCount GUARDED_BY(cs); //! table with information about all nIds - std::map mapInfo GUARDED_BY(cs); + std::unordered_map mapInfo GUARDED_BY(cs); //! find an nId based on its network address - std::map mapAddr GUARDED_BY(cs); + std::unordered_map mapAddr GUARDED_BY(cs); //! randomly-ordered vector of all nIds //! This is mutable because it is unobservable outside the class, so any diff --git a/src/netaddress.h b/src/netaddress.h index b8001a4dd7327..2914e36bfb66f 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -11,7 +11,9 @@ #include #include +#include #include +#include #include #include #include @@ -251,6 +253,7 @@ class CNetAddr } } + friend class CNetAddrHash; friend class CSubNet; private: @@ -465,6 +468,22 @@ class CNetAddr } }; +class CNetAddrHash +{ +public: + size_t operator()(const CNetAddr& a) const noexcept + { + CSipHasher hasher(m_salt_k0, m_salt_k1); + hasher.Write(a.m_net); + hasher.Write(a.m_addr.data(), a.m_addr.size()); + return static_cast(hasher.Finalize()); + } + +private: + const uint64_t m_salt_k0 = GetRand(std::numeric_limits::max()); + const uint64_t m_salt_k1 = GetRand(std::numeric_limits::max()); +}; + class CSubNet { protected: diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 2e17b7e2bd559..0c7440e9781c4 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -4,6 +4,7 @@ #include +#include #include #include @@ -365,7 +366,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector& group void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size_t ancestors, size_t descendants) { m_outputs.push_back(output); m_from_me &= from_me; - m_value += output.effective_value; + m_value += output.txout.nValue; m_depth = std::min(m_depth, depth); // ancestors here express the number of ancestors the new coin will end up having, which is // the sum, rather than the max; this will overestimate in the cases where multiple inputs @@ -374,15 +375,19 @@ void OutputGroup::Insert(const CInputCoin& output, int depth, bool from_me, size // descendants is the count as seen from the top ancestor, not the descendants as seen from the // coin itself; thus, this value is counted as the max, not the sum m_descendants = std::max(m_descendants, descendants); - effective_value = m_value; + effective_value += output.effective_value; + fee += output.m_fee; + long_term_fee += output.m_long_term_fee; } std::vector::iterator OutputGroup::Discard(const CInputCoin& output) { auto it = m_outputs.begin(); while (it != m_outputs.end() && it->outpoint != output.outpoint) ++it; if (it == m_outputs.end()) return it; - m_value -= output.effective_value; + m_value -= output.txout.nValue; effective_value -= output.effective_value; + fee -= output.m_fee; + long_term_fee -= output.m_long_term_fee; return m_outputs.erase(it); } @@ -401,3 +406,35 @@ bool OutputGroup::EligibleForSpending(const CoinEligibilityFilter& eligibility_f && m_ancestors <= eligibility_filter.max_ancestors && m_descendants <= eligibility_filter.max_descendants; } + +void OutputGroup::SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate) +{ + fee = 0; + long_term_fee = 0; + effective_value = 0; + for (CInputCoin& coin : m_outputs) { + coin.m_fee = coin.m_input_bytes < 0 ? 0 : effective_feerate.GetFee(coin.m_input_bytes); + fee += coin.m_fee; + + coin.m_long_term_fee = coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes); + long_term_fee += coin.m_long_term_fee; + + coin.effective_value = coin.txout.nValue - coin.m_fee; + effective_value += coin.effective_value; + } +} + +OutputGroup OutputGroup::GetPositiveOnlyGroup() +{ + OutputGroup group(*this); + for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) { + const CInputCoin& coin = *it; + // Only include outputs that are positive effective value (i.e. not dust) + if (coin.effective_value <= 0) { + it = group.Discard(coin); + } else { + ++it; + } + } + return group; +} diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index fe5fb3c680002..1e98b18f30b52 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -9,6 +9,8 @@ #include #include +class CFeeRate; + //! target minimum change amount static constexpr CAmount MIN_CHANGE{COIN / 100}; //! final minimum change amount after paying for fees @@ -36,6 +38,8 @@ class CInputCoin { COutPoint outpoint; CTxOut txout; CAmount effective_value; + CAmount m_fee{0}; + CAmount m_long_term_fee{0}; /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */ int m_input_bytes{-1}; @@ -92,6 +96,10 @@ struct OutputGroup std::vector::iterator Discard(const CInputCoin& output); bool IsLockedByInstantSend() const; bool EligibleForSpending(const CoinEligibilityFilter& eligibility_filter) const; + + //! Update the OutputGroup's fee, long_term_fee, and effective_value based on the given feerates + void SetFees(const CFeeRate effective_feerate, const CFeeRate long_term_feerate); + OutputGroup GetPositiveOnlyGroup(); }; bool SelectCoinsBnB(std::vector& utxo_pool, const CAmount& target_value, const CAmount& cost_of_change, std::set& out_set, CAmount& value_ret, CAmount not_input_fees); diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp new file mode 100644 index 0000000000000..a3859e2e4b793 --- /dev/null +++ b/src/wallet/test/walletdb_tests.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2012-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. + +#include +#include +#include +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(walletdb_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue) +{ + /** + * When ReadKeyValue() reads from either a "key" or "wkey" it first reads the CDataStream steam into a + * CPrivKey or CWalletKey respectively and then reads a hash of the pubkey and privkey into a uint256. + * Wallets from 0.8 or before do not store the pubkey/privkey hash, trying to read the hash from old + * wallets throws an exception, for backwards compatibility this read is wrapped in a try block to + * silently fail. The test here makes sure the type of exception thrown from CDataStream::read() + * matches the type we expect, otherwise we need to update the "key"/"wkey" exception type caught. + */ + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + uint256 dummy; + BOOST_CHECK_THROW(ssValue >> dummy, std::ios_base::failure); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bd55468a57845..e9f73855c90d5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2745,27 +2745,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil for (OutputGroup& group : groups) { if (!group.EligibleForSpending(eligibility_filter)) continue; - group.fee = 0; - group.long_term_fee = 0; - group.effective_value = 0; - for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) { - const CInputCoin& coin = *it; - CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes)); - // Only include outputs that are positive effective value (i.e. not dust) - if (effective_value > 0) { - group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes); - group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes); - if (coin_selection_params.m_subtract_fee_outputs) { - group.effective_value += coin.txout.nValue; - } else { - group.effective_value += effective_value; - } - ++it; - } else { - it = group.Discard(coin); - } + if (coin_selection_params.m_subtract_fee_outputs) { + // Set the effective feerate to 0 as we don't want to use the effective value since the fees will be deducted from the output + group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate); + } else { + group.SetFees(coin_selection_params.effective_fee, long_term_feerate); } - if (group.effective_value > 0) utxo_pool.push_back(group); + + OutputGroup pos_group = group.GetPositiveOnlyGroup(); + if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group); } // Calculate the fees for things that aren't inputs CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 9ea4bba0cc532..9b922201bb3fa 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -327,7 +327,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { ssValue >> hash; } - catch (...) {} + catch (const std::ios_base::failure&) {} bool fSkipCheck = false; diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index f3053cff40fbb..8b49d472ef83f 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -5,6 +5,7 @@ """Test block processing.""" import copy import struct +import time from test_framework.blocktools import ( create_block, @@ -600,6 +601,8 @@ def run_test(self): b44.hashPrevBlock = self.tip.sha256 b44.nBits = 0x207fffff b44.vtx.append(coinbase) + tx = self.create_and_sign_transaction(out[14], 1) + b44.vtx.append(tx) b44.hashMerkleRoot = b44.calc_merkle_root() b44.solve() self.tip = b44 @@ -687,7 +690,7 @@ def run_test(self): # Test block timestamps # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) # \-> b54 (15) - # + # -> b44 (14)\-> b48 () self.move_tip(43) b53 = self.next_block(53, spend=out[14]) self.send_blocks([b53], False) @@ -707,6 +710,21 @@ def run_test(self): self.send_blocks([b55], True) self.save_spendable_output() + # The block which was previously rejected because of being "too far(3 hours)" must be accepted 2 hours later. + # The new block is only 1 hour into future now and we must reorg onto to the new longer chain. + # The new bestblock b48p is invalidated manually. + # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) + # \-> b54 (15) + # -> b44 (14)\-> b48 () -> b48p () + self.log.info("Accept a previously rejected future block at a later time") + node.setmocktime(int(time.time()) + 2*60*60) + self.move_tip(48) + self.block_heights[b48.sha256] = self.block_heights[b44.sha256] + 1 # b48 is a parent of b44 + b48p = self.next_block("48p") + self.send_blocks([b48, b48p], success=True) # Reorg to the longer chain + node.invalidateblock(b48p.hash) # mark b48p as invalid + node.setmocktime(0) + # Test Merkle tree malleability # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16) @@ -1317,7 +1335,7 @@ def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE, OP_DROP] * 15 + return create_tx_with_script(spend_tx, n, amount=value, script_pub_key=script) # sign a transaction, using the key we know about - # this signs input 0 in tx, which is assumed to be spending output n in spend_tx + # this signs input 0 in tx, which is assumed to be spending output 0 in spend_tx def sign_tx(self, tx, spend_tx): scriptPubKey = bytearray(spend_tx.vout[0].scriptPubKey) if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend diff --git a/test/functional/p2p_fingerprint.py b/test/functional/p2p_fingerprint.py index 9a64d2027d75a..595f53d6972a0 100755 --- a/test/functional/p2p_fingerprint.py +++ b/test/functional/p2p_fingerprint.py @@ -118,7 +118,7 @@ def run_test(self): block_hash = int(tip, 16) self.send_block_request(block_hash, node0) self.send_header_request(block_hash, node0) - node0.sync_with_ping() + node0.wait_for_block(block_hash, timeout=3) # Request for very old stale block should now fail self.send_block_request(stale_hash, node0) @@ -144,5 +144,6 @@ def run_test(self): test_function = lambda: self.last_header_equals(block_hash, node0) wait_until(test_function, timeout=3) + if __name__ == '__main__': P2PFingerprintTest().main()