aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.test.include6
-rw-r--r--src/addrdb.cpp22
-rw-r--r--src/addrman.cpp44
-rw-r--r--src/addrman.h96
-rw-r--r--src/bench/duplicate_inputs.cpp1
-rw-r--r--src/bitcoin-tx.cpp3
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/core_read.cpp1
-rw-r--r--src/external_signer.cpp8
-rw-r--r--src/external_signer.h16
-rw-r--r--src/index/base.cpp1
-rw-r--r--src/init.cpp24
-rw-r--r--src/init.h2
-rw-r--r--src/init/bitcoin-node.cpp2
-rw-r--r--src/init/bitcoind.cpp2
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/interfaces/wallet.h4
-rw-r--r--src/key.cpp21
-rw-r--r--src/key.h12
-rw-r--r--src/miner.cpp12
-rw-r--r--src/net.cpp128
-rw-r--r--src/net.h26
-rw-r--r--src/net_processing.cpp127
-rw-r--r--src/netaddress.h19
-rw-r--r--src/node/blockstorage.cpp1
-rw-r--r--src/node/coin.cpp1
-rw-r--r--src/node/coinstats.cpp1
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h2
-rw-r--r--src/node/interfaces.cpp65
-rw-r--r--src/node/psbt.cpp6
-rw-r--r--src/node/transaction.cpp1
-rw-r--r--src/policy/packages.cpp62
-rw-r--r--src/policy/packages.h10
-rw-r--r--src/protocol.h83
-rw-r--r--src/psbt.cpp31
-rw-r--r--src/psbt.h11
-rw-r--r--src/pubkey.cpp4
-rw-r--r--src/pubkey.h10
-rw-r--r--src/qt/addressbookpage.cpp8
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoingui.cpp7
-rw-r--r--src/qt/coincontroldialog.cpp12
-rw-r--r--src/qt/createwalletdialog.cpp7
-rw-r--r--src/qt/createwalletdialog.h9
-rw-r--r--src/qt/optionsdialog.cpp5
-rw-r--r--src/qt/overviewpage.cpp9
-rw-r--r--src/qt/overviewpage.h1
-rw-r--r--src/qt/psbtoperationsdialog.cpp6
-rw-r--r--src/qt/qrimagewidget.cpp4
-rw-r--r--src/qt/receivecoinsdialog.cpp10
-rw-r--r--src/qt/receiverequestdialog.cpp2
-rw-r--r--src/qt/rpcconsole.cpp12
-rw-r--r--src/qt/sendcoinsdialog.cpp8
-rw-r--r--src/qt/test/apptests.cpp5
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/transactionview.cpp20
-rw-r--r--src/qt/walletcontroller.cpp3
-rw-r--r--src/qt/walletframe.cpp5
-rw-r--r--src/qt/walletframe.h5
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletview.cpp7
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp1
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp16
-rw-r--r--src/script/descriptor.cpp4
-rw-r--r--src/script/interpreter.cpp14
-rw-r--r--src/script/interpreter.h5
-rw-r--r--src/script/sign.cpp195
-rw-r--r--src/script/sign.h9
-rw-r--r--src/script/signingprovider.cpp13
-rw-r--r--src/script/signingprovider.h4
-rw-r--r--src/script/standard.cpp57
-rw-r--r--src/script/standard.h46
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/blockfilter_index_tests.cpp26
-rw-r--r--src/test/coinstatsindex_tests.cpp8
-rw-r--r--src/test/denialofservice_tests.cpp49
-rw-r--r--src/test/fuzz/addrman.cpp22
-rw-r--r--src/test/fuzz/coins_view.cpp4
-rw-r--r--src/test/fuzz/crypto.cpp6
-rw-r--r--src/test/fuzz/deserialize.cpp47
-rw-r--r--src/test/fuzz/load_external_block_file.cpp7
-rw-r--r--src/test/fuzz/node_eviction.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp12
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp7
-rw-r--r--src/test/interfaces_tests.cpp2
-rw-r--r--src/test/key_tests.cpp42
-rw-r--r--src/test/miner_tests.cpp93
-rw-r--r--src/test/net_peer_eviction_tests.cpp504
-rw-r--r--src/test/txindex_tests.cpp2
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp48
-rw-r--r--src/test/util/mining.cpp4
-rw-r--r--src/test/util/net.h12
-rw-r--r--src/test/util/setup_common.cpp23
-rw-r--r--src/test/validation_block_tests.cpp16
-rw-r--r--src/txmempool.cpp7
-rw-r--r--src/txmempool.h3
-rw-r--r--src/util/system.cpp6
-rw-r--r--src/util/system.h2
-rw-r--r--src/validation.cpp154
-rw-r--r--src/validation.h39
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.cpp8
-rw-r--r--src/wallet/external_signer_scriptpubkeyman.h5
-rw-r--r--src/wallet/interfaces.cpp4
-rw-r--r--src/wallet/rpcdump.cpp12
-rw-r--r--src/wallet/rpcwallet.cpp12
-rw-r--r--src/wallet/scriptpubkeyman.cpp9
-rw-r--r--src/wallet/scriptpubkeyman.h6
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp38
-rw-r--r--src/wallet/wallet.cpp18
-rw-r--r--src/wallet/wallet.h1
118 files changed, 1709 insertions, 958 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 66cb7cec2a..e2ed70556d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -351,6 +351,7 @@ libbitcoin_server_a_SOURCES = \
node/ui_interface.cpp \
noui.cpp \
policy/fees.cpp \
+ policy/packages.cpp \
policy/rbf.cpp \
policy/settings.cpp \
pow.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 105d09f730..fc2fd80166 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -35,11 +35,12 @@ BITCOIN_TEST_SUITE = \
$(TEST_UTIL_H)
FUZZ_SUITE_LD_COMMON = \
+ $(LIBTEST_UTIL) \
+ $(LIBTEST_FUZZ) \
$(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
- $(LIBTEST_UTIL) \
- $(LIBTEST_FUZZ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBBITCOIN_CLI) \
@@ -160,7 +161,6 @@ BITCOIN_TESTS += \
wallet/test/scriptpubkeyman_tests.cpp
FUZZ_SUITE_LD_COMMON +=\
- $(LIBBITCOIN_WALLET) \
$(SQLITE_LIBS) \
$(BDB_LIBS)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index c376aced10..bf2f6c7614 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -23,7 +23,7 @@ bool SerializeDB(Stream& stream, const Data& data)
{
// Write and commit header, data
try {
- CHashWriter hasher(SER_DISK, CLIENT_VERSION);
+ CHashWriter hasher(stream.GetType(), stream.GetVersion());
stream << Params().MessageStart() << data;
hasher << Params().MessageStart() << data;
stream << hasher.GetHash();
@@ -35,7 +35,7 @@ bool SerializeDB(Stream& stream, const Data& data)
}
template <typename Data>
-bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
+bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
{
// Generate random temporary filename
uint16_t randv = 0;
@@ -45,7 +45,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
// open temp output file, and associate with CAutoFile
fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
- CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile fileout(file, SER_DISK, version);
if (fileout.IsNull()) {
fileout.fclose();
remove(pathTmp);
@@ -106,11 +106,11 @@ bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data)
+bool DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
- CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
LogPrintf("Missing or invalid file %s\n", path.string());
return false;
@@ -125,12 +125,12 @@ CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path
bool CBanDB::Write(const banmap_t& banSet)
{
- return SerializeFileDB("banlist", m_ban_list_path, banSet);
+ return SerializeFileDB("banlist", m_ban_list_path, banSet, CLIENT_VERSION);
}
bool CBanDB::Read(banmap_t& banSet)
{
- return DeserializeFileDB(m_ban_list_path, banSet);
+ return DeserializeFileDB(m_ban_list_path, banSet, CLIENT_VERSION);
}
CAddrDB::CAddrDB()
@@ -140,12 +140,12 @@ CAddrDB::CAddrDB()
bool CAddrDB::Write(const CAddrMan& addr)
{
- return SerializeFileDB("peers", pathAddr, addr);
+ return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr)
{
- return DeserializeFileDB(pathAddr, addr);
+ return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
@@ -161,13 +161,13 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
{
LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
- SerializeFileDB("anchors", anchors_db_path, anchors);
+ SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
}
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors)) {
+ if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
} else {
anchors.clear();
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 14b412a038..8f702b5a8c 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -12,6 +12,8 @@
#include <cmath>
#include <optional>
+#include <unordered_map>
+#include <unordered_set>
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@@ -77,12 +79,14 @@ double CAddrInfo::GetChance(int64_t nNow) const
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
- std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
+ AssertLockHeld(cs);
+
+ const auto it = mapAddr.find(addr);
if (it == mapAddr.end())
return nullptr;
if (pnId)
*pnId = (*it).second;
- std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
+ const auto it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return nullptr;
@@ -90,6 +94,8 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
+ AssertLockHeld(cs);
+
int nId = nIdCount++;
mapInfo[nId] = CAddrInfo(addr, addrSource);
mapAddr[addr] = nId;
@@ -102,6 +108,8 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
{
+ AssertLockHeld(cs);
+
if (nRndPos1 == nRndPos2)
return;
@@ -122,6 +130,8 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
void CAddrMan::Delete(int nId)
{
+ AssertLockHeld(cs);
+
assert(mapInfo.count(nId) != 0);
CAddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
@@ -136,6 +146,8 @@ void CAddrMan::Delete(int nId)
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
{
+ AssertLockHeld(cs);
+
// if there is an entry in the specified bucket, delete it.
if (vvNew[nUBucket][nUBucketPos] != -1) {
int nIdDelete = vvNew[nUBucket][nUBucketPos];
@@ -151,6 +163,8 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
{
+ AssertLockHeld(cs);
+
// remove the entry from all new buckets
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
int pos = info.GetBucketPosition(nKey, true, bucket);
@@ -199,6 +213,8 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
+ AssertLockHeld(cs);
+
int nId;
nLastGood = nTime;
@@ -265,6 +281,8 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
+ AssertLockHeld(cs);
+
if (!addr.IsRoutable())
return false;
@@ -338,6 +356,8 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -360,7 +380,9 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
CAddrInfo CAddrMan::Select_(bool newOnly)
{
- if (size() == 0)
+ AssertLockHeld(cs);
+
+ if (vRandom.empty())
return CAddrInfo();
if (newOnly && nNew == 0)
@@ -408,8 +430,10 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
#ifdef DEBUG_ADDRMAN
int CAddrMan::Check_()
{
- std::set<int> setTried;
- std::map<int, int> mapNew;
+ AssertLockHeld(cs);
+
+ std::unordered_set<int> setTried;
+ std::unordered_map<int, int> mapNew;
if (vRandom.size() != (size_t)(nTried + nNew))
return -7;
@@ -485,6 +509,8 @@ int CAddrMan::Check_()
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
{
+ AssertLockHeld(cs);
+
size_t nNodes = vRandom.size();
if (max_pct != 0) {
nNodes = max_pct * nNodes / 100;
@@ -517,6 +543,8 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
void CAddrMan::Connected_(const CService& addr, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -537,6 +565,8 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -555,6 +585,8 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
void CAddrMan::ResolveCollisions_()
{
+ AssertLockHeld(cs);
+
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
int id_new = *it;
@@ -614,6 +646,8 @@ void CAddrMan::ResolveCollisions_()
CAddrInfo CAddrMan::SelectTriedCollision_()
{
+ AssertLockHeld(cs);
+
if (m_tried_collisions.size() == 0) return CAddrInfo();
std::set<int>::iterator it = m_tried_collisions.begin();
diff --git a/src/addrman.h b/src/addrman.h
index 41994288db..665e253192 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -8,22 +8,22 @@
#include <clientversion.h>
#include <config/bitcoin-config.h>
+#include <fs.h>
+#include <hash.h>
#include <netaddress.h>
#include <protocol.h>
#include <random.h>
+#include <streams.h>
#include <sync.h>
#include <timedata.h>
#include <tinyformat.h>
#include <util/system.h>
-#include <fs.h>
-#include <hash.h>
#include <iostream>
-#include <map>
#include <optional>
#include <set>
#include <stdint.h>
-#include <streams.h>
+#include <unordered_map>
#include <vector>
/**
@@ -231,6 +231,7 @@ public:
*/
template <typename Stream>
void Serialize(Stream& s_) const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
@@ -251,7 +252,7 @@ public:
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
- std::map<int, int> mapUnkIds;
+ std::unordered_map<int, int> mapUnkIds;
int nIds = 0;
for (const auto& entry : mapInfo) {
mapUnkIds[entry.first] = nIds;
@@ -296,10 +297,11 @@ public:
template <typename Stream>
void Unserialize(Stream& s_)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
- Clear();
+ assert(vRandom.empty());
Format format;
s_ >> Using<CustomUintFormatter<1>>(format);
@@ -435,13 +437,13 @@ public:
// Prune new entries with refcount 0 (as a result of collisions).
int nLostUnk = 0;
- for (std::map<int, CAddrInfo>::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<int, CAddrInfo>::const_iterator itCopy = it++;
+ const auto itCopy = it++;
Delete(itCopy->first);
- nLostUnk++;
+ ++nLostUnk;
} else {
- it++;
+ ++it;
}
}
if (nLost + nLostUnk > 0) {
@@ -452,6 +454,7 @@ public:
}
void Clear()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
std::vector<int>().swap(vRandom);
@@ -487,26 +490,15 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
return vRandom.size();
}
- //! Consistency check
- void Check()
- {
-#ifdef DEBUG_ADDRMAN
- {
- LOCK(cs);
- int err;
- if ((err=Check_()))
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- }
-#endif
- }
-
//! Add a single address.
bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
bool fRet = false;
@@ -521,6 +513,7 @@ public:
//! Add multiple addresses.
bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
int nAdd = 0;
@@ -536,6 +529,7 @@ public:
//! Mark an entry as accessible.
void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -545,6 +539,7 @@ public:
//! Mark an entry as connection attempted to.
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -554,6 +549,7 @@ public:
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void ResolveCollisions()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -563,14 +559,12 @@ public:
//! Randomly select an address in tried that another address is attempting to evict.
CAddrInfo SelectTriedCollision()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo ret;
- {
- LOCK(cs);
- Check();
- ret = SelectTriedCollision_();
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo ret = SelectTriedCollision_();
+ Check();
return ret;
}
@@ -578,14 +572,12 @@ public:
* Choose an address to connect to.
*/
CAddrInfo Select(bool newOnly = false)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo addrRet;
- {
- LOCK(cs);
- Check();
- addrRet = Select_(newOnly);
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo addrRet = Select_(newOnly);
+ Check();
return addrRet;
}
@@ -597,19 +589,19 @@ public:
* @param[in] network Select only addresses of this network (nullopt = all).
*/
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
+ LOCK(cs);
Check();
std::vector<CAddress> vAddr;
- {
- LOCK(cs);
- GetAddr_(vAddr, max_addresses, max_pct, network);
- }
+ GetAddr_(vAddr, max_addresses, max_pct, network);
Check();
return vAddr;
}
//! Outer function for Connected_()
void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -618,6 +610,7 @@ public:
}
void SetServices(const CService &addr, ServiceFlags nServices)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -633,8 +626,8 @@ protected:
FastRandomContext insecure_rand;
private:
- //! critical section to protect the inner data structures
- mutable RecursiveMutex cs;
+ //! A mutex to protect the inner data structures.
+ mutable Mutex cs;
//! Serialization versions.
enum Format : uint8_t {
@@ -662,10 +655,10 @@ private:
int nIdCount GUARDED_BY(cs);
//! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
+ std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
+ std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
std::vector<int> vRandom GUARDED_BY(cs);
@@ -725,6 +718,19 @@ private:
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+ //! Consistency check
+ void Check()
+ EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+#ifdef DEBUG_ADDRMAN
+ AssertLockHeld(cs);
+ const int err = Check_();
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ }
+#endif
+ }
+
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 4f6e1122b8..8703a1cf94 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -25,7 +25,6 @@ static void DuplicateInputs(benchmark::Bench& bench)
CMutableTransaction naughtyTx{};
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(testing_setup->m_node.chainman->ActiveChain()));
CBlockIndex* pindexPrev = testing_setup->m_node.chainman->ActiveChain().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 93ac4b8f7e..3fc87ae1ff 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -506,11 +506,12 @@ static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOut
tx.vout.erase(tx.vout.begin() + outIdx);
}
-static const unsigned int N_SIGHASH_OPTS = 6;
+static const unsigned int N_SIGHASH_OPTS = 7;
static const struct {
const char *flagStr;
int flags;
} sighashOptions[N_SIGHASH_OPTS] = {
+ {"DEFAULT", SIGHASH_DEFAULT},
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index cf9e4fad44..654679af27 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -112,8 +112,8 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
util::ThreadSetInternalName("init");
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
- SetupServerArgs(node);
ArgsManager& args = *Assert(node.args);
+ SetupServerArgs(args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
diff --git a/src/core_read.cpp b/src/core_read.cpp
index b5fc93886d..6108961010 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -260,6 +260,7 @@ int ParseSighashString(const UniValue& sighash)
int hash_type = SIGHASH_ALL;
if (!sighash.isNull()) {
static std::map<std::string, int> map_sighash_values = {
+ {std::string("DEFAULT"), int(SIGHASH_DEFAULT)},
{std::string("ALL"), int(SIGHASH_ALL)},
{std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
{std::string("NONE"), int(SIGHASH_NONE)},
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index f16d21fa60..d6388b759a 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -13,9 +13,7 @@
#include <string>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
-ExternalSigner::ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name): m_command(command), m_fingerprint(fingerprint), m_chain(chain), m_name(name) {}
+ExternalSigner::ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name): m_command(command), m_chain(chain), m_fingerprint(fingerprint), m_name(name) {}
const std::string ExternalSigner::NetworkArg() const
{
@@ -55,7 +53,7 @@ bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalS
if (model_field.isStr() && model_field.getValStr() != "") {
name += model_field.getValStr();
}
- signers.push_back(ExternalSigner(command, fingerprintStr, chain, name));
+ signers.push_back(ExternalSigner(command, chain, fingerprintStr, name));
}
return true;
}
@@ -116,5 +114,3 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
return true;
}
-
-#endif // ENABLE_EXTERNAL_SIGNER
diff --git a/src/external_signer.h b/src/external_signer.h
index b3b202091a..e40fd7f010 100644
--- a/src/external_signer.h
+++ b/src/external_signer.h
@@ -11,8 +11,6 @@
#include <string>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
struct PartiallySignedTransaction;
//! Enables interaction with an external signing device or service, such as
@@ -23,24 +21,24 @@ private:
//! The command which handles interaction with the external signer.
std::string m_command;
+ //! Bitcoin mainnet, testnet, etc
+ std::string m_chain;
+
+ const std::string NetworkArg() const;
+
public:
//! @param[in] command the command which handles interaction with the external signer
//! @param[in] fingerprint master key fingerprint of the signer
//! @param[in] chain "main", "test", "regtest" or "signet"
//! @param[in] name device name
- ExternalSigner(const std::string& command, const std::string& fingerprint, const std::string chain, const std::string name);
+ ExternalSigner(const std::string& command, const std::string chain, const std::string& fingerprint, const std::string name);
//! Master key fingerprint of the signer
std::string m_fingerprint;
- //! Bitcoin mainnet, testnet, etc
- std::string m_chain;
-
//! Name of signer
std::string m_name;
- const std::string NetworkArg() const;
-
//! Obtain a list of signers. Calls `<command> enumerate`.
//! @param[in] command the command which handles interaction with the external signer
//! @param[in,out] signers vector to which new signers (with a unique master key fingerprint) are added
@@ -65,6 +63,4 @@ public:
bool SignTransaction(PartiallySignedTransaction& psbt, std::string& error);
};
-#endif // ENABLE_EXTERNAL_SIGNER
-
#endif // BITCOIN_EXTERNAL_SIGNER_H
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3a61af28b7..6fd2701e2e 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -340,7 +340,6 @@ void BaseIndex::Interrupt()
bool BaseIndex::Start(CChainState& active_chainstate)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
m_chainstate = &active_chainstate;
// Need to register this ValidationInterface before running Init(), so that
// callbacks are not missed if Init sets m_synced to true.
diff --git a/src/init.cpp b/src/init.cpp
index 7f64b1acfa..4dc82811f9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -283,7 +283,7 @@ void Shutdown(NodeContext& node)
init::UnsetGlobals();
node.mempool.reset();
node.fee_estimator.reset();
- node.chainman = nullptr;
+ node.chainman.reset();
node.scheduler.reset();
try {
@@ -347,12 +347,8 @@ static void OnRPCStopped()
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-void SetupServerArgs(NodeContext& node)
+void SetupServerArgs(ArgsManager& argsman)
{
- assert(!node.args);
- node.args = &gArgs;
- ArgsManager& argsman = *node.args;
-
SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
@@ -1179,8 +1175,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio);
assert(!node.chainman);
- node.chainman = &g_chainman;
- ChainstateManager& chainman = *Assert(node.chainman);
+ node.chainman = std::make_unique<ChainstateManager>();
+ ChainstateManager& chainman = *node.chainman;
assert(!node.peerman);
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
@@ -1385,7 +1381,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!chainman.BlockIndex().empty() &&
- !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
+ !chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
@@ -1400,7 +1396,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
- if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
@@ -1549,21 +1545,21 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
- if (!g_txindex->Start(::ChainstateActive())) {
+ if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false;
}
}
for (const auto& filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
- if (!GetBlockFilterIndex(filter_type)->Start(::ChainstateActive())) {
+ if (!GetBlockFilterIndex(filter_type)->Start(chainman.ActiveChainstate())) {
return false;
}
}
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
g_coin_stats_index = std::make_unique<CoinStatsIndex>(/* cache size */ 0, false, fReindex);
- if (!g_coin_stats_index->Start(::ChainstateActive())) {
+ if (!g_coin_stats_index->Start(chainman.ActiveChainstate())) {
return false;
}
}
@@ -1611,7 +1607,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
- if (::ChainActive().Tip() == nullptr) {
+ if (chainman.ActiveChain().Tip() == nullptr) {
block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
diff --git a/src/init.h b/src/init.h
index 328eda9c7e..b856468e5d 100644
--- a/src/init.h
+++ b/src/init.h
@@ -69,7 +69,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info
/**
* Register all arguments with the ArgsManager
*/
-void SetupServerArgs(NodeContext& node);
+void SetupServerArgs(ArgsManager& argsman);
/** Returns licensing information (for -version) */
std::string LicenseInfo();
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index 49684ede83..6b6157c139 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -6,6 +6,7 @@
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -20,6 +21,7 @@ public:
: m_node(node),
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
{
+ m_node.args = &gArgs;
m_node.init = this;
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 1e17ce4d3c..1d4504c24f 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -4,6 +4,7 @@
#include <interfaces/init.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -14,6 +15,7 @@ class BitcoindInit : public interfaces::Init
public:
BitcoindInit(NodeContext& node) : m_node(node)
{
+ m_node.args = &gArgs;
m_node.init = this;
}
NodeContext& m_node;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 3395741b1b..7cac435e96 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -277,6 +277,9 @@ public:
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications& notifications) = 0;
+
+ //! Check if Taproot has activated
+ virtual bool isTaprootActive() const = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 35b6160cea..77129423db 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -111,10 +111,8 @@ public:
//! Disconnect node by id.
virtual bool disconnectById(NodeId id) = 0;
-#ifdef ENABLE_EXTERNAL_SIGNER
//! List external signers
virtual std::vector<ExternalSigner> externalSigners() = 0;
-#endif
//! Get total bytes recv.
virtual int64_t getTotalBytesRecv() = 0;
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a0cb2787b7..fb1febc11b 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -198,9 +198,9 @@ public:
virtual TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) = 0;
+ bool& complete) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
diff --git a/src/key.cpp b/src/key.cpp
index 5666adebb8..dcad386e77 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -7,10 +7,13 @@
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
+#include <hash.h>
#include <random.h>
#include <secp256k1.h>
+#include <secp256k1_extrakeys.h>
#include <secp256k1_recovery.h>
+#include <secp256k1_schnorrsig.h>
static secp256k1_context* secp256k1_context_sign = nullptr;
@@ -258,6 +261,24 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true;
}
+bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256* aux) const
+{
+ assert(sig.size() == 64);
+ secp256k1_keypair keypair;
+ if (!secp256k1_keypair_create(secp256k1_context_sign, &keypair, begin())) return false;
+ if (merkle_root) {
+ secp256k1_xonly_pubkey pubkey;
+ if (!secp256k1_keypair_xonly_pub(secp256k1_context_sign, &pubkey, nullptr, &keypair)) return false;
+ unsigned char pubkey_bytes[32];
+ if (!secp256k1_xonly_pubkey_serialize(secp256k1_context_sign, pubkey_bytes, &pubkey)) return false;
+ uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
+ if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false;
+ }
+ bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, secp256k1_nonce_function_bip340, aux ? (void*)aux->data() : nullptr);
+ memory_cleanse(&keypair, sizeof(keypair));
+ return ret;
+}
+
bool CKey::Load(const CPrivKey &seckey, const CPubKey &vchPubKey, bool fSkipCheck=false) {
if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), seckey.data(), seckey.size()))
return false;
diff --git a/src/key.h b/src/key.h
index 3ee49a778b..d47e54800c 100644
--- a/src/key.h
+++ b/src/key.h
@@ -128,6 +128,18 @@ public:
*/
bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const;
+ /**
+ * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this,
+ * optionally tweaked by *merkle_root. Additional nonce entropy can be provided through
+ * aux.
+ *
+ * When merkle_root is not nullptr, this results in a signature with a modified key as
+ * specified in BIP341:
+ * - If merkle_root->IsNull(): key + H_TapTweak(pubkey)*G
+ * - Otherwise: key + H_TapTweak(pubkey || *merkle_root)
+ */
+ bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const;
+
//! Derive BIP32 child key.
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
diff --git a/src/miner.cpp b/src/miner.cpp
index eccddbb04f..0cf303eb3c 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -45,15 +45,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
block.vtx.at(0) = MakeTransactionRef(tx);
- CBlockIndex* prev_block;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman.m_blockman));
- prev_block = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
- }
+ CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
@@ -124,7 +116,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, m_mempool.cs);
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*m_chainstate.m_chain.Tip()));
CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
@@ -184,7 +175,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_chainstate));
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
diff --git a/src/net.cpp b/src/net.cpp
index 6f9f17ed4e..60059249ed 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -42,6 +42,7 @@
#endif
#include <algorithm>
+#include <array>
#include <cstdint>
#include <functional>
#include <optional>
@@ -841,18 +842,6 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
-static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
-{
- if (a.m_is_local != b.m_is_local) return b.m_is_local;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
-static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b)
-{
- if (a.m_is_onion != b.m_is_onion) return b.m_is_onion;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -883,6 +872,26 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
return a.nTimeConnected > b.nTimeConnected;
}
+/**
+ * Sort eviction candidates by network/localhost and connection uptime.
+ * Candidates near the beginning are more likely to be evicted, and those
+ * near the end are more likely to be protected, e.g. less likely to be evicted.
+ * - First, nodes that are not `is_local` and that do not belong to `network`,
+ * sorted by increasing uptime (from most recently connected to connected longer).
+ * - Then, nodes that are `is_local` or belong to `network`, sorted by increasing uptime.
+ */
+struct CompareNodeNetworkTime {
+ const bool m_is_local;
+ const Network m_network;
+ CompareNodeNetworkTime(bool is_local, Network network) : m_is_local(is_local), m_network(network) {}
+ bool operator()(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) const
+ {
+ if (m_is_local && a.m_is_local != b.m_is_local) return b.m_is_local;
+ if ((a.m_network == m_network) != (b.m_network == m_network)) return b.m_network == m_network;
+ return a.nTimeConnected > b.nTimeConnected;
+ };
+};
+
//! Sort an array by the specified comparator, then erase the last K elements where predicate is true.
template <typename T, typename Comparator>
static void EraseLastKElements(
@@ -894,40 +903,72 @@ static void EraseLastKElements(
elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end());
}
-void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
+void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& eviction_candidates)
{
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- // To favorise the diversity of our peer connections, reserve up to (half + 2) of
- // these protected spots for onion and localhost peers, if any, even if they're not
- // longest uptime overall. This helps protect tor peers, which tend to be otherwise
+ // To favorise the diversity of our peer connections, reserve up to half of these protected
+ // spots for Tor/onion, localhost and I2P peers, even if they're not longest uptime overall.
+ // This helps protect these higher-latency peers that tend to be otherwise
// disadvantaged under our eviction criteria.
- const size_t initial_size = vEvictionCandidates.size();
- size_t total_protect_size = initial_size / 2;
- const size_t onion_protect_size = total_protect_size / 2;
-
- if (onion_protect_size) {
- // Pick out up to 1/4 peers connected via our onion service, sorted by longest uptime.
- EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, onion_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_onion; });
- }
-
- const size_t localhost_min_protect_size{2};
- if (onion_protect_size >= localhost_min_protect_size) {
- // Allocate any remaining slots of the 1/4, or minimum 2 additional slots,
- // to localhost peers, sorted by longest uptime, as manually configured
- // hidden services not using `-bind=addr[:port]=onion` will not be detected
- // as inbound onion connections.
- const size_t remaining_tor_slots{onion_protect_size - (initial_size - vEvictionCandidates.size())};
- const size_t localhost_protect_size{std::max(remaining_tor_slots, localhost_min_protect_size)};
- EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, localhost_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_local; });
+ const size_t initial_size = eviction_candidates.size();
+ const size_t total_protect_size{initial_size / 2};
+
+ // Disadvantaged networks to protect: I2P, localhost, Tor/onion. In case of equal counts, earlier
+ // array members have first opportunity to recover unused slots from the previous iteration.
+ struct Net { bool is_local; Network id; size_t count; };
+ std::array<Net, 3> networks{
+ {{false, NET_I2P, 0}, {/* localhost */ true, NET_MAX, 0}, {false, NET_ONION, 0}}};
+
+ // Count and store the number of eviction candidates per network.
+ for (Net& n : networks) {
+ n.count = std::count_if(eviction_candidates.cbegin(), eviction_candidates.cend(),
+ [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ }
+ // Sort `networks` by ascending candidate count, to give networks having fewer candidates
+ // the first opportunity to recover unused protected slots from the previous iteration.
+ std::stable_sort(networks.begin(), networks.end(), [](Net a, Net b) { return a.count < b.count; });
+
+ // Protect up to 25% of the eviction candidates by disadvantaged network.
+ const size_t max_protect_by_network{total_protect_size / 2};
+ size_t num_protected{0};
+
+ while (num_protected < max_protect_by_network) {
+ const size_t disadvantaged_to_protect{max_protect_by_network - num_protected};
+ const size_t protect_per_network{
+ std::max(disadvantaged_to_protect / networks.size(), static_cast<size_t>(1))};
+
+ // Early exit flag if there are no remaining candidates by disadvantaged network.
+ bool protected_at_least_one{false};
+
+ for (const Net& n : networks) {
+ if (n.count == 0) continue;
+ const size_t before = eviction_candidates.size();
+ EraseLastKElements(eviction_candidates, CompareNodeNetworkTime(n.is_local, n.id),
+ protect_per_network, [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ const size_t after = eviction_candidates.size();
+ if (before > after) {
+ protected_at_least_one = true;
+ num_protected += before - after;
+ if (num_protected >= max_protect_by_network) {
+ break;
+ }
+ }
+ }
+ if (!protected_at_least_one) {
+ break;
+ }
}
// Calculate how many we removed, and update our total number of peers that
// we want to protect based on uptime accordingly.
- total_protect_size -= initial_size - vEvictionCandidates.size();
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
+ assert(num_protected == initial_size - eviction_candidates.size());
+ const size_t remaining_to_protect{total_protect_size - num_protected};
+ EraseLastKElements(eviction_candidates, ReverseCompareNodeTimeConnected, remaining_to_protect);
}
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
@@ -944,8 +985,7 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvict
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
- const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
- EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size,
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8,
[](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; });
// Protect 4 nodes that most recently sent us novel blocks.
@@ -1024,7 +1064,7 @@ bool CConnman::AttemptToEvictConnection()
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
node->m_prefer_evict, node->addr.IsLocal(),
- node->m_inbound_onion};
+ node->ConnectedThroughNetwork()};
vEvictionCandidates.push_back(candidate);
}
}
@@ -2173,6 +2213,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
void CConnman::ThreadMessageHandler()
{
+ FastRandomContext rng;
while (!flagInterruptMsgProc)
{
std::vector<CNode*> vNodesCopy;
@@ -2186,6 +2227,11 @@ void CConnman::ThreadMessageHandler()
bool fMoreWork = false;
+ // Randomize the order in which we process messages from/to our peers.
+ // This prevents attacks in which an attacker exploits having multiple
+ // consecutive connections in the vNodes list.
+ Shuffle(vNodesCopy.begin(), vNodesCopy.end(), rng);
+
for (CNode* pnode : vNodesCopy)
{
if (pnode->fDisconnect)
diff --git a/src/net.h b/src/net.h
index b43916c55e..01658e8973 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1209,7 +1209,7 @@ struct NodeEvictionCandidate
uint64_t nKeyedNetGroup;
bool prefer_evict;
bool m_is_local;
- bool m_is_onion;
+ Network m_network;
};
/**
@@ -1227,20 +1227,20 @@ struct NodeEvictionCandidate
* longest, to replicate the non-eviction implicit behavior and preclude attacks
* that start later.
*
- * Half of these protected spots (1/4 of the total) are reserved for onion peers
- * connected via our tor control service, if any, sorted by longest uptime, even
- * if they're not longest uptime overall. Any remaining slots of the 1/4 are
- * then allocated to protect localhost peers, if any (or up to 2 localhost peers
- * if no slots remain and 2 or more onion peers were protected), sorted by
- * longest uptime, as manually configured hidden services not using
- * `-bind=addr[:port]=onion` will not be detected as inbound onion connections.
+ * Half of these protected spots (1/4 of the total) are reserved for the
+ * following categories of peers, sorted by longest uptime, even if they're not
+ * longest uptime overall:
+ *
+ * - onion peers connected via our tor control service
+ *
+ * - localhost peers, as manually configured hidden services not using
+ * `-bind=addr[:port]=onion` will not be detected as inbound onion connections
*
- * This helps protect onion peers, which tend to be otherwise disadvantaged
- * under our eviction criteria for their higher min ping times relative to IPv4
- * and IPv6 peers, and favorise the diversity of peer connections.
+ * - I2P peers
*
- * This function was extracted from SelectNodeToEvict() to be able to test the
- * ratio-based protection logic deterministically.
+ * This helps protect these privacy network peers, which tend to be otherwise
+ * disadvantaged under our eviction criteria for their higher min ping times
+ * relative to IPv4/IPv6 peers, and favorise the diversity of peer connections.
*/
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 65224b4259..b2112cfd2e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -159,10 +159,10 @@ static constexpr size_t MAX_ADDR_TO_SEND{1000};
namespace {
/** Blocks that are in flight, and that are in the queue to be downloaded. */
struct QueuedBlock {
- uint256 hash;
- const CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+ /** BlockIndex. We must have this since we only request blocks when we've already validated the header. */
+ const CBlockIndex* pindex;
+ /** Optional, used for CMPCTBLOCK downloads */
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock;
};
/**
@@ -463,16 +463,20 @@ private:
Mutex m_recent_confirmed_transactions_mutex;
std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex);
- /* Returns a bool indicating whether we requested this block.
- * Also used if a block was /not/ received and timed out or started with another peer
+ /** Have we requested this block from a peer */
+ bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Remove this block from our tracked requested blocks. Called if:
+ * - the block has been recieved from a peer
+ * - the request for the block has timed out
*/
- bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void RemoveBlockRequest(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/* Mark a block as in flight
* Returns false, still setting pit, if the block was already in flight from the same peer
* pit will only be valid as long as the same cs_main lock is being held
*/
- bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -512,7 +516,7 @@ private:
std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
/** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
/** Storage for orphan information */
TxOrphanage m_orphanage;
@@ -627,7 +631,6 @@ struct CNodeState {
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
std::chrono::microseconds m_downloading_since{0us};
int nBlocksInFlight{0};
- int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer.
bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements.
@@ -758,32 +761,42 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
-bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash)
+bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
{
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end()) {
- CNodeState *state = State(itInFlight->second.first);
- assert(state != nullptr);
- state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
- if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
- // Last validated block on the queue was received.
- nPeersWithValidatedDownloads--;
- }
- if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
- // First block on the queue was received, update the start download time for the next one
- state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
- }
- state->vBlocksInFlight.erase(itInFlight->second.second);
- state->nBlocksInFlight--;
- state->m_stalling_since = 0us;
- mapBlocksInFlight.erase(itInFlight);
- return true;
+ return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end();
+}
+
+void PeerManagerImpl::RemoveBlockRequest(const uint256& hash)
+{
+ auto it = mapBlocksInFlight.find(hash);
+ if (it == mapBlocksInFlight.end()) {
+ // Block was not requested
+ return;
}
- return false;
+
+ auto [node_id, list_it] = it->second;
+ CNodeState *state = State(node_id);
+ assert(state != nullptr);
+
+ if (state->vBlocksInFlight.begin() == list_it) {
+ // First block on the queue was received, update the start download time for the next one
+ state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
+ }
+ state->vBlocksInFlight.erase(list_it);
+
+ state->nBlocksInFlight--;
+ if (state->nBlocksInFlight == 0) {
+ // Last validated block on the queue was received.
+ m_peers_downloading_from--;
+ }
+ state->m_stalling_since = 0us;
+ mapBlocksInFlight.erase(it);
}
-bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit)
+bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit)
{
+ const uint256& hash{block.GetBlockHash()};
+
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -797,22 +810,20 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
}
// Make sure it's not listed somewhere already.
- MarkBlockAsReceived(hash);
+ RemoveBlockRequest(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
+ {&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++;
- state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
state->m_downloading_since = GetTime<std::chrono::microseconds>();
- }
- if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
- nPeersWithValidatedDownloads++;
+ m_peers_downloading_from++;
}
itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
- if (pit)
+ if (pit) {
*pit = &itInFlight->second.second;
+ }
return true;
}
@@ -978,7 +989,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
- } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
+ } else if (!IsBlockRequested(pindex->GetBlockHash())) {
// The block is not already downloaded, and not yet in flight.
if (pindex->nHeight > nWindowEnd) {
// We reached the end of the window.
@@ -1129,13 +1140,13 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
nSyncStarted--;
for (const QueuedBlock& entry : state->vBlocksInFlight) {
- mapBlocksInFlight.erase(entry.hash);
+ mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
}
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
- nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
- assert(nPeersWithValidatedDownloads >= 0);
+ m_peers_downloading_from -= (state->nBlocksInFlight != 0);
+ assert(m_peers_downloading_from >= 0);
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(m_outbound_peers_with_protect_from_disconnect >= 0);
m_wtxid_relay_peers -= state->m_wtxid_relay;
@@ -1147,7 +1158,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
// Do a consistency check after the last peer is removed.
assert(mapBlocksInFlight.empty());
assert(nPreferredDownload == 0);
- assert(nPeersWithValidatedDownloads == 0);
+ assert(m_peers_downloading_from == 0);
assert(m_outbound_peers_with_protect_from_disconnect == 0);
assert(m_wtxid_relay_peers == 0);
assert(m_txrequest.Size() == 0);
@@ -1350,7 +1361,6 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_stale_tip_check_time(0),
m_ignore_incoming_txs(ignore_incoming_txs)
{
- assert(std::addressof(g_chainman) == std::addressof(m_chainman));
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -2056,7 +2066,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
+ !IsBlockRequested(pindexWalk->GetBlockHash()) &&
(!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
@@ -2081,7 +2091,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pfrom.GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -2827,7 +2837,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
- if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
+ if (!fAlreadyHave && !fImporting && !fReindex && !IsBlockRequested(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
// it's probably for a re-org. The final block hash
@@ -3384,7 +3394,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!BlockRequested(pfrom.GetId(), *pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
@@ -3397,7 +3407,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3492,7 +3502,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// process from some other peer. We do this after calling
// ProcessNewBlock so that a malleated cmpctblock announcement
// can't be used to interfere with block relay.
- MarkBlockAsReceived(pblock->GetHash());
+ RemoveBlockRequest(pblock->GetHash());
}
}
return;
@@ -3524,7 +3534,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3550,7 +3560,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is
// updated, etc.
- MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
+ RemoveBlockRequest(resp.blockhash); // it is now an empty pointer
fBlockRead = true;
// mapBlockSource is used for potentially punishing peers and
// updating which peers send us compact blocks, so the race
@@ -3615,9 +3625,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
- // Also always process if we requested the block explicitly, as we may
- // need it even though it is not a candidate for a new best tip.
- forceProcessing |= MarkBlockAsReceived(hash);
+ // Always process the block if we requested it, since we may
+ // need it even when it's not a candidate for a new best tip.
+ forceProcessing = IsBlockRequested(hash);
+ RemoveBlockRequest(hash);
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
@@ -4712,9 +4723,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// to unreasonably increase our timeout.
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
- int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
+ int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;
if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.pindex->GetBlockHash().ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
@@ -4767,7 +4778,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pto->GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId());
}
diff --git a/src/netaddress.h b/src/netaddress.h
index 0d04ab88fb..dd47ab5749 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -11,7 +11,9 @@
#include <attributes.h>
#include <compat.h>
+#include <crypto/siphash.h>
#include <prevector.h>
+#include <random.h>
#include <serialize.h>
#include <tinyformat.h>
#include <util/strencodings.h>
@@ -251,6 +253,7 @@ class CNetAddr
}
}
+ friend class CNetAddrHash;
friend class CSubNet;
private:
@@ -464,6 +467,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<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
+};
+
class CSubNet
{
protected:
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 6c66c565ad..013d61282b 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -248,7 +248,6 @@ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight,
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index 23d4fa2aae..50fddf3ab0 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -13,7 +13,6 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
assert(node.mempool);
assert(node.chainman);
LOCK2(cs_main, node.mempool->cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.mempool);
for (auto& coin : coins) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 38c1d29250..67e497c218 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -97,7 +97,6 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
if (!pindex) {
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
pindex = blockman.LookupBlockIndex(view->GetBestBlock());
}
}
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 6d22a6b110..9afadd09a9 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -12,6 +12,7 @@
#include <policy/fees.h>
#include <scheduler.h>
#include <txmempool.h>
+#include <validation.h>
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
diff --git a/src/node/context.h b/src/node/context.h
index 06adb33a80..135f9ea1c6 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -44,7 +44,7 @@ struct NodeContext {
std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
std::unique_ptr<PeerManager> peerman;
- ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
+ std::unique_ptr<ChainstateManager> chainman;
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 171f15d4fb..fce3c1809c 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -6,6 +6,7 @@
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
+#include <external_signer.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
@@ -170,16 +171,24 @@ public:
}
return false;
}
-#ifdef ENABLE_EXTERNAL_SIGNER
std::vector<ExternalSigner> externalSigners() override
{
+#ifdef ENABLE_EXTERNAL_SIGNER
std::vector<ExternalSigner> signers = {};
const std::string command = gArgs.GetArg("-signer", "");
if (command == "") return signers;
ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
return signers;
+#else
+ // This result is undistinguisable from a succesful call that returns
+ // no signers. For the current GUI this doesn't matter, because the wallet
+ // creation dialog disables the external signer checkbox in both
+ // cases. The return type could be changed to std::optional<std::vector>
+ // (or something that also includes error messages) if this distinction
+ // becomes important.
+ return {};
+#endif // ENABLE_EXTERNAL_SIGNER
}
-#endif
int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; }
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
@@ -197,26 +206,16 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return chainman().ActiveChain().Height();
}
uint256 getBestBlockHash() override
{
- const CBlockIndex* tip;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
- tip = chainman().ActiveChain().Tip();
- }
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
@@ -227,22 +226,12 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
tip = chainman().ActiveChain().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &m_context->chainman->ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
@@ -269,7 +258,6 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainman().ActiveChainstate()));
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin);
}
WalletClient& walletClient() override
@@ -476,14 +464,12 @@ public:
bool checkFinalTx(const CTransaction& tx) override
{
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return CheckFinalTx(chainman().ActiveChain().Tip(), tx);
}
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) {
return fork->nHeight;
}
@@ -493,7 +479,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
@@ -506,7 +491,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -518,9 +502,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -529,9 +511,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
@@ -542,7 +522,6 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
return GuessVerificationProgress(Params().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
@@ -555,7 +534,6 @@ public:
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
@@ -647,16 +625,7 @@ public:
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &chainman().ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
@@ -719,6 +688,12 @@ public:
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
+ bool isTaprootActive() const override
+ {
+ LOCK(::cs_main);
+ const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
+ return VersionBitsState(tip, Params().GetConsensus(), Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE;
+ }
NodeContext& m_node;
};
} // namespace
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index c189018268..b013b6d579 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -23,6 +23,8 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
result.inputs.resize(psbtx.tx->vin.size());
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs[i];
PSBTInputAnalysis& input_analysis = result.inputs[i];
@@ -61,7 +63,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
// Figure out what is missing
SignatureData outdata;
- bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
+ bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
// Things are missing
if (!complete) {
@@ -121,7 +123,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i];
Coin newcoin;
- if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
+ if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) || !psbtx.GetInputUTXO(newcoin.out, i)) {
success = false;
break;
} else {
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index a1e7a71e2c..f21b390915 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -40,7 +40,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
{ // cs_main scope
assert(node.chainman);
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp
new file mode 100644
index 0000000000..cfd0539965
--- /dev/null
+++ b/src/policy/packages.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2021 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 <consensus/validation.h>
+#include <policy/packages.h>
+#include <primitives/transaction.h>
+#include <uint256.h>
+#include <util/hasher.h>
+
+#include <numeric>
+#include <unordered_set>
+
+bool CheckPackage(const Package& txns, PackageValidationState& state)
+{
+ const unsigned int package_count = txns.size();
+
+ if (package_count > MAX_PACKAGE_COUNT) {
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
+ }
+
+ const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
+ [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
+ // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
+ if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
+ }
+
+ // Require the package to be sorted in order of dependency, i.e. parents appear before children.
+ // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
+ // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
+ // spend nonexistent coins).
+ std::unordered_set<uint256, SaltedTxidHasher> later_txids;
+ std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
+ [](const auto& tx) { return tx->GetHash(); });
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (later_txids.find(input.prevout.hash) != later_txids.end()) {
+ // The parent is a subsequent transaction in the package.
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
+ }
+ }
+ later_txids.erase(tx->GetHash());
+ }
+
+ // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
+ std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
+ // This input is also present in another tx in the package.
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
+ }
+ }
+ // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
+ // catch duplicate inputs within a single tx. This is a more severe, consensus error,
+ // and we want to report that from CheckTransaction instead.
+ std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
+ [](const auto& input) { return input.prevout; });
+ }
+ return true;
+}
diff --git a/src/policy/packages.h b/src/policy/packages.h
index 4b1463dcb3..6b7ac3e450 100644
--- a/src/policy/packages.h
+++ b/src/policy/packages.h
@@ -6,6 +6,7 @@
#define BITCOIN_POLICY_PACKAGES_H
#include <consensus/validation.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <vector>
@@ -14,6 +15,7 @@
static constexpr uint32_t MAX_PACKAGE_COUNT{25};
/** Default maximum total virtual size of transactions in a package in KvB. */
static constexpr uint32_t MAX_PACKAGE_SIZE{101};
+static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT);
/** A "reason" why a package was invalid. It may be that one or more of the included
* transactions is invalid or the package itself violates our rules.
@@ -31,4 +33,12 @@ using Package = std::vector<CTransactionRef>;
class PackageValidationState : public ValidationState<PackageValidationResult> {};
+/** Context-free package policy checks:
+ * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
+ * 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE.
+ * 3. If any dependencies exist between transactions, parents must appear before children.
+ * 4. Transactions cannot conflict, i.e., spend the same inputs.
+ */
+bool CheckPackage(const Package& txns, PackageValidationState& state);
+
#endif // BITCOIN_POLICY_PACKAGES_H
diff --git a/src/protocol.h b/src/protocol.h
index aaa9f1df40..f9248899dc 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -13,6 +13,7 @@
#include <netaddress.h>
#include <primitives/transaction.h>
#include <serialize.h>
+#include <streams.h>
#include <uint256.h>
#include <version.h>
@@ -358,6 +359,31 @@ class CAddress : public CService
{
static constexpr uint32_t TIME_INIT{100000000};
+ /** Historically, CAddress disk serialization stored the CLIENT_VERSION, optionally OR'ed with
+ * the ADDRV2_FORMAT flag to indicate V2 serialization. The first field has since been
+ * disentangled from client versioning, and now instead:
+ * - The low bits (masked by DISK_VERSION_IGNORE_MASK) store the fixed value DISK_VERSION_INIT,
+ * (in case any code exists that treats it as a client version) but are ignored on
+ * deserialization.
+ * - The high bits (masked by ~DISK_VERSION_IGNORE_MASK) store actual serialization information.
+ * Only 0 or DISK_VERSION_ADDRV2 (equal to the historical ADDRV2_FORMAT) are valid now, and
+ * any other value triggers a deserialization failure. Other values can be added later if
+ * needed.
+ *
+ * For disk deserialization, ADDRV2_FORMAT in the stream version signals that ADDRV2
+ * deserialization is permitted, but the actual format is determined by the high bits in the
+ * stored version field. For network serialization, the stream version having ADDRV2_FORMAT or
+ * not determines the actual format used (as it has no embedded version number).
+ */
+ static constexpr uint32_t DISK_VERSION_INIT{220000};
+ static constexpr uint32_t DISK_VERSION_IGNORE_MASK{0b00000000'00000111'11111111'11111111};
+ /** The version number written in disk serialized addresses to indicate V2 serializations.
+ * It must be exactly 1<<29, as that is the value that historical versions used for this
+ * (they used their internal ADDRV2_FORMAT flag here). */
+ static constexpr uint32_t DISK_VERSION_ADDRV2{1 << 29};
+ static_assert((DISK_VERSION_INIT & ~DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_INIT must be covered by DISK_VERSION_IGNORE_MASK");
+ static_assert((DISK_VERSION_ADDRV2 & DISK_VERSION_IGNORE_MASK) == 0, "DISK_VERSION_ADDRV2 must not be covered by DISK_VERSION_IGNORE_MASK");
+
public:
CAddress() : CService{} {};
CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {};
@@ -365,22 +391,48 @@ public:
SERIALIZE_METHODS(CAddress, obj)
{
- SER_READ(obj, obj.nTime = TIME_INIT);
- int nVersion = s.GetVersion();
+ // CAddress has a distinct network serialization and a disk serialization, but it should never
+ // be hashed (except through CHashWriter in addrdb.cpp, which sets SER_DISK), and it's
+ // ambiguous what that would mean. Make sure no code relying on that is introduced:
+ assert(!(s.GetType() & SER_GETHASH));
+ bool use_v2;
+ bool store_time;
if (s.GetType() & SER_DISK) {
- READWRITE(nVersion);
- }
- if ((s.GetType() & SER_DISK) ||
- (nVersion != INIT_PROTO_VERSION && !(s.GetType() & SER_GETHASH))) {
+ // In the disk serialization format, the encoding (v1 or v2) is determined by a flag version
+ // that's part of the serialization itself. ADDRV2_FORMAT in the stream version only determines
+ // whether V2 is chosen/permitted at all.
+ uint32_t stored_format_version = DISK_VERSION_INIT;
+ if (s.GetVersion() & ADDRV2_FORMAT) stored_format_version |= DISK_VERSION_ADDRV2;
+ READWRITE(stored_format_version);
+ stored_format_version &= ~DISK_VERSION_IGNORE_MASK; // ignore low bits
+ if (stored_format_version == 0) {
+ use_v2 = false;
+ } else if (stored_format_version == DISK_VERSION_ADDRV2 && (s.GetVersion() & ADDRV2_FORMAT)) {
+ // Only support v2 deserialization if ADDRV2_FORMAT is set.
+ use_v2 = true;
+ } else {
+ throw std::ios_base::failure("Unsupported CAddress disk format version");
+ }
+ store_time = true;
+ } else {
+ // In the network serialization format, the encoding (v1 or v2) is determined directly by
+ // the value of ADDRV2_FORMAT in the stream version, as no explicitly encoded version
+ // exists in the stream.
+ assert(s.GetType() & SER_NETWORK);
+ use_v2 = s.GetVersion() & ADDRV2_FORMAT;
// The only time we serialize a CAddress object without nTime is in
// the initial VERSION messages which contain two CAddress records.
// At that point, the serialization version is INIT_PROTO_VERSION.
// After the version handshake, serialization version is >=
// MIN_PEER_PROTO_VERSION and all ADDR messages are serialized with
// nTime.
- READWRITE(obj.nTime);
+ store_time = s.GetVersion() != INIT_PROTO_VERSION;
}
- if (nVersion & ADDRV2_FORMAT) {
+
+ SER_READ(obj, obj.nTime = TIME_INIT);
+ if (store_time) READWRITE(obj.nTime);
+ // nServices is serialized as CompactSize in V2; as uint64_t in V1.
+ if (use_v2) {
uint64_t services_tmp;
SER_WRITE(obj, services_tmp = obj.nServices);
READWRITE(Using<CompactSizeFormatter<false>>(services_tmp));
@@ -388,13 +440,22 @@ public:
} else {
READWRITE(Using<CustomUintFormatter<8>>(obj.nServices));
}
- READWRITEAS(CService, obj);
+ // Invoke V1/V2 serializer for CService parent object.
+ OverrideStream<Stream> os(&s, s.GetType(), use_v2 ? ADDRV2_FORMAT : 0);
+ SerReadWriteMany(os, ser_action, ReadWriteAsHelper<CService>(obj));
}
- // disk and network only
+ //! Always included in serialization, except in the network format on INIT_PROTO_VERSION.
uint32_t nTime{TIME_INIT};
-
+ //! Serialized as uint64_t in V1, and as CompactSize in V2.
ServiceFlags nServices{NODE_NONE};
+
+ friend bool operator==(const CAddress& a, const CAddress& b)
+ {
+ return a.nTime == b.nTime &&
+ a.nServices == b.nServices &&
+ static_cast<const CService&>(a) == static_cast<const CService&>(b);
+ }
};
/** getdata message type flags */
diff --git a/src/psbt.cpp b/src/psbt.cpp
index a849b2ea53..5445bc8aa1 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -59,12 +59,15 @@ bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
{
- PSBTInput input = inputs[input_index];
+ const PSBTInput& input = inputs[input_index];
uint32_t prevout_index = tx->vin[input_index].prevout.n;
if (input.non_witness_utxo) {
if (prevout_index >= input.non_witness_utxo->vout.size()) {
return false;
}
+ if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
+ return false;
+ }
utxo = input.non_witness_utxo->vout[prevout_index];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
@@ -227,7 +230,24 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
psbt_out.FromSignatureData(sigdata);
}
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
+PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
+{
+ const CMutableTransaction& tx = *psbt.tx;
+ bool have_all_spent_outputs = true;
+ std::vector<CTxOut> utxos(tx.vin.size());
+ for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
+ if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
+ }
+ PrecomputedTransactionData txdata;
+ if (have_all_spent_outputs) {
+ txdata.Init(tx, std::move(utxos), true);
+ } else {
+ txdata.Init(tx, {}, true);
+ }
+ return txdata;
+}
+
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata)
{
PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx;
@@ -267,10 +287,10 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
sigdata.witness = false;
bool sig_complete;
- if (use_dummy) {
+ if (txdata == nullptr) {
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
} else {
- MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
}
// Verify that a witness signature was produced in case one was required.
@@ -302,8 +322,9 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
// PartiallySignedTransaction did not understand them), this will combine them into a final
// script.
bool complete = true;
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL);
}
return complete;
diff --git a/src/psbt.h b/src/psbt.h
index 96ae39fdb8..f6b82b43de 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -567,11 +567,18 @@ enum class PSBTRole {
std::string PSBTRoleName(PSBTRole role);
+/** Compute a PrecomputedTransactionData object from a psbt. */
+PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt);
+
/** Checks whether a PSBTInput is already signed. */
bool PSBTInputSigned(const PSBTInput& input);
-/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
-bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
+/** Signs a PSBTInput, verifying that all provided data matches what is being signed.
+ *
+ * txdata should be the output of PrecomputePSBTData (which can be shared across
+ * multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
+ **/
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr);
/** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 51cc826b00..175a39b805 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -373,3 +373,7 @@ ECCVerifyHandle::~ECCVerifyHandle()
secp256k1_context_verify = nullptr;
}
}
+
+const secp256k1_context* GetVerifyContext() {
+ return secp256k1_context_verify;
+}
diff --git a/src/pubkey.h b/src/pubkey.h
index 152a48dd18..eec34a89c2 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -234,6 +234,10 @@ public:
* fail. */
bool IsFullyValid() const;
+ /** Test whether this is the 0 key (the result of default construction). This implies
+ * !IsFullyValid(). */
+ bool IsNull() const { return m_keydata.IsNull(); }
+
/** Construct an x-only pubkey from exactly 32 bytes. */
explicit XOnlyPubKey(Span<const unsigned char> bytes);
@@ -312,4 +316,10 @@ public:
~ECCVerifyHandle();
};
+typedef struct secp256k1_context_struct secp256k1_context;
+
+/** Access to the internal secp256k1 context used for verification. Only intended to be used
+ * by key.cpp. */
+const secp256k1_context* GetVerifyContext();
+
#endif // BITCOIN_PUBKEY_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 7024fc7654..c31f0aceea 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -114,12 +114,12 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
// Build context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
- contextMenu->addAction(tr("Copy Label"), this, &AddressBookPage::onCopyLabelAction);
- contextMenu->addAction(tr("Edit"), this, &AddressBookPage::onEditAction);
+ contextMenu->addAction(tr("&Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
+ contextMenu->addAction(tr("Copy &Label"), this, &AddressBookPage::onCopyLabelAction);
+ contextMenu->addAction(tr("&Edit"), this, &AddressBookPage::onEditAction);
if (tab == SendingTab) {
- contextMenu->addAction(tr("Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
+ contextMenu->addAction(tr("&Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
}
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 9e6cf56d31..442c813a5a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -490,7 +490,8 @@ int GuiMain(int argc, char* argv[])
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- SetupServerArgs(node_context);
+ node_context.args = &gArgs;
+ SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 50f6e739e8..3d632ec702 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -211,11 +211,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
-#ifdef ENABLE_WALLET
- if(enableWallet) {
- connect(walletFrame, &WalletFrame::requestedSyncWarningInfo, this, &BitcoinGUI::showModalOverlay);
- }
-#endif
#ifdef Q_OS_MAC
m_app_nap_inhibitor = new CAppNapInhibitor;
@@ -690,7 +685,7 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
const QString display_name = walletModel->getDisplayName();
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
- connect(wallet_view, &WalletView::outOfSyncWarningClicked, walletFrame, &WalletFrame::outOfSyncWarningClicked);
+ connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 8ae0648141..2360fa9b37 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -52,13 +52,13 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy address"), this, &CoinControlDialog::copyAddress);
- contextMenu->addAction(tr("Copy label"), this, &CoinControlDialog::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &CoinControlDialog::copyAmount);
- copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction ID"), this, &CoinControlDialog::copyTransactionHash);
+ contextMenu->addAction(tr("&Copy address"), this, &CoinControlDialog::copyAddress);
+ contextMenu->addAction(tr("Copy &label"), this, &CoinControlDialog::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &CoinControlDialog::copyAmount);
+ copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction &ID"), this, &CoinControlDialog::copyTransactionHash);
contextMenu->addSeparator();
- lockAction = contextMenu->addAction(tr("Lock unspent"), this, &CoinControlDialog::lockCoin);
- unlockAction = contextMenu->addAction(tr("Unlock unspent"), this, &CoinControlDialog::unlockCoin);
+ lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
+ unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
// clipboard actions
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
index e593697b46..dc24bbc6a6 100644
--- a/src/qt/createwalletdialog.cpp
+++ b/src/qt/createwalletdialog.cpp
@@ -31,8 +31,9 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
// Disable the disable_privkeys_checkbox and external_signer_checkbox when isEncryptWalletChecked is
// set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
+#ifdef ENABLE_EXTERNAL_SIGNER
ui->external_signer_checkbox->setEnabled(!checked);
-
+#endif
// When the disable_privkeys_checkbox is disabled, uncheck it.
if (!ui->disable_privkeys_checkbox->isEnabled()) {
ui->disable_privkeys_checkbox->setChecked(false);
@@ -112,8 +113,7 @@ CreateWalletDialog::~CreateWalletDialog()
delete ui;
}
-#ifdef ENABLE_EXTERNAL_SIGNER
-void CreateWalletDialog::setSigners(std::vector<ExternalSigner>& signers)
+void CreateWalletDialog::setSigners(const std::vector<ExternalSigner>& signers)
{
if (!signers.empty()) {
ui->external_signer_checkbox->setEnabled(true);
@@ -132,7 +132,6 @@ void CreateWalletDialog::setSigners(std::vector<ExternalSigner>& signers)
ui->external_signer_checkbox->setEnabled(false);
}
}
-#endif
QString CreateWalletDialog::walletName() const
{
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
index 585b1461f7..25ddf97585 100644
--- a/src/qt/createwalletdialog.h
+++ b/src/qt/createwalletdialog.h
@@ -7,11 +7,8 @@
#include <QDialog>
-class WalletModel;
-
-#ifdef ENABLE_EXTERNAL_SIGNER
class ExternalSigner;
-#endif
+class WalletModel;
namespace Ui {
class CreateWalletDialog;
@@ -27,9 +24,7 @@ public:
explicit CreateWalletDialog(QWidget* parent);
virtual ~CreateWalletDialog();
-#ifdef ENABLE_EXTERNAL_SIGNER
- void setSigners(std::vector<ExternalSigner>& signers);
-#endif
+ void setSigners(const std::vector<ExternalSigner>& signers);
QString walletName() const;
bool isEncryptWalletChecked() const;
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 6ad8db4348..b12fe96567 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -92,6 +92,11 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
ui->thirdPartyTxUrls->setVisible(false);
}
+#ifndef ENABLE_EXTERNAL_SIGNER
+ //: "External signing" means using devices such as hardware wallets.
+ ui->externalSignerPath->setToolTip(tr("Compiled without external signing support (required for external signing)"));
+ ui->externalSignerPath->setEnabled(false);
+#endif
/* Display elements init */
QDir translations(":translations");
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 27783bdf87..26e3dd0d60 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -166,8 +166,8 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);
- connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
- connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
+ connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
+ connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
}
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
@@ -176,11 +176,6 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
Q_EMIT transactionClicked(filter->mapToSource(index));
}
-void OverviewPage::handleOutOfSyncWarningClicks()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
-
void OverviewPage::setPrivacy(bool privacy)
{
m_privacy = privacy;
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 755a107a00..5270741c0d 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -65,7 +65,6 @@ private Q_SLOTS:
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
- void handleOutOfSyncWarningClicks();
void setMonospacedFont(bool use_embedded_font);
};
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 99318c3bc0..2adfeeaaf0 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -50,7 +50,7 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
bool complete;
size_t n_could_sign;
FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to load transaction: %1")
.arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
@@ -67,7 +67,7 @@ void PSBTOperationsDialog::signTransaction()
{
bool complete;
size_t n_signed;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to sign transaction: %1")
@@ -226,7 +226,7 @@ void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
size_t n_signed;
bool complete;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
return 0;
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index f5200bb5c0..7cdd568644 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -27,8 +27,8 @@ QRImageWidget::QRImageWidget(QWidget *parent):
QLabel(parent), contextMenu(nullptr)
{
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Save Image…"), this, &QRImageWidget::saveImage);
- contextMenu->addAction(tr("Copy Image"), this, &QRImageWidget::copyImage);
+ contextMenu->addAction(tr("&Save Image…"), this, &QRImageWidget::saveImage);
+ contextMenu->addAction(tr("&Copy Image"), this, &QRImageWidget::copyImage);
}
bool QRImageWidget::setQR(const QString& data, const QString& text)
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 3f4d7f85e6..d47ee95826 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -44,11 +44,11 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy URI"), this, &ReceiveCoinsDialog::copyURI);
- contextMenu->addAction(tr("Copy address"), this, &ReceiveCoinsDialog::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &ReceiveCoinsDialog::copyLabel);
- copyMessageAction = contextMenu->addAction(tr("Copy message"), this, &ReceiveCoinsDialog::copyMessage);
- copyAmountAction = contextMenu->addAction(tr("Copy amount"), this, &ReceiveCoinsDialog::copyAmount);
+ contextMenu->addAction(tr("Copy &URI"), this, &ReceiveCoinsDialog::copyURI);
+ contextMenu->addAction(tr("&Copy address"), this, &ReceiveCoinsDialog::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &ReceiveCoinsDialog::copyLabel);
+ copyMessageAction = contextMenu->addAction(tr("Copy &message"), this, &ReceiveCoinsDialog::copyMessage);
+ copyAmountAction = contextMenu->addAction(tr("Copy &amount"), this, &ReceiveCoinsDialog::copyAmount);
connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index abe7de8f89..41f22e9c34 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -90,7 +90,7 @@ void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
ui->wallet_content->hide();
}
- ui->btnVerify->setVisible(this->model->wallet().hasExternalSigner());
+ ui->btnVerify->setVisible(model->wallet().hasExternalSigner());
connect(ui->btnVerify, &QPushButton::clicked, [this] {
model->displayAddress(info.address.toStdString());
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 9579e6dc24..ff4bfb16f6 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -675,11 +675,11 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create peer table context menu
peersTableContextMenu = new QMenu(this);
- peersTableContextMenu->addAction(tr("Disconnect"), this, &RPCConsole::disconnectSelectedNode);
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 hour"), [this] { banSelectedNode(60 * 60); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 day"), [this] { banSelectedNode(60 * 60 * 24); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
+ peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode);
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
// peer table signal handling - update peer details when selecting new node
@@ -701,7 +701,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create ban table context menu
banTableContextMenu = new QMenu(this);
- banTableContextMenu->addAction(tr("Unban"), this, &RPCConsole::unbanSelectedNode);
+ banTableContextMenu->addAction(tr("&Unban"), this, &RPCConsole::unbanSelectedNode);
connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
// ban table signal handling - clear peer details when clicking a peer in the ban table
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index e87a2b97bc..c9bf757dfc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -200,12 +200,14 @@ void SendCoinsDialog::setModel(WalletModel *_model)
ui->optInRBF->setCheckState(Qt::Checked);
if (model->wallet().hasExternalSigner()) {
+ //: "device" usually means a hardware wallet
ui->sendButton->setText(tr("Sign on device"));
if (gArgs.GetArg("-signer", "") != "") {
ui->sendButton->setEnabled(true);
ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
} else {
ui->sendButton->setEnabled(false);
+ //: "External signer" means using devices such as hardware wallets.
ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
}
} else if (model->wallet().privateKeysDisabled()) {
@@ -414,23 +416,25 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
bool complete = false;
// Always fill without signing first. This prevents an external signer
// from being called prematurely and is not expensive.
- TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
assert(!complete);
assert(err == TransactionError::OK);
if (model->wallet().hasExternalSigner()) {
try {
- err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
} catch (const std::runtime_error& e) {
QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
send_failure = true;
return;
}
if (err == TransactionError::EXTERNAL_SIGNER_NOT_FOUND) {
+ //: "External signer" means using devices such as hardware wallets.
QMessageBox::critical(nullptr, tr("External signer not found"), "External signer not found");
send_failure = true;
return;
}
if (err == TransactionError::EXTERNAL_SIGNER_FAILED) {
+ //: "External signer" means using devices such as hardware wallets.
QMessageBox::critical(nullptr, tr("External signer failure"), "External signer failure");
send_failure = true;
return;
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index cb3dbd2267..9c31cd50df 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -85,11 +85,6 @@ void AppTests::appTests()
// Reset global state to avoid interfering with later tests.
LogInstance().DisconnectTestLogger();
AbortShutdown();
- {
- LOCK(cs_main);
- UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
- g_chainman.Reset();
- }
}
//! Entry point for BitcoinGUI tests.
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index ea35f80cf5..3e1a0e0fa9 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -146,14 +146,14 @@ void TestGUI(interfaces::Node& node)
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
- wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(105, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
}
{
WalletRescanReserver reserver(*wallet);
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ QCOMPARE(result.last_scanned_block, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
QVERIFY(result.last_failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 1e8e012dcf..4b1a546c7c 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -163,19 +163,19 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu = new QMenu(this);
contextMenu->setObjectName("contextMenu");
- copyAddressAction = contextMenu->addAction(tr("Copy address"), this, &TransactionView::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &TransactionView::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &TransactionView::copyAmount);
- contextMenu->addAction(tr("Copy transaction ID"), this, &TransactionView::copyTxID);
- contextMenu->addAction(tr("Copy raw transaction"), this, &TransactionView::copyTxHex);
- contextMenu->addAction(tr("Copy full transaction details"), this, &TransactionView::copyTxPlainText);
- contextMenu->addAction(tr("Show transaction details"), this, &TransactionView::showDetails);
+ copyAddressAction = contextMenu->addAction(tr("&Copy address"), this, &TransactionView::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &TransactionView::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &TransactionView::copyAmount);
+ contextMenu->addAction(tr("Copy transaction &ID"), this, &TransactionView::copyTxID);
+ contextMenu->addAction(tr("Copy &raw transaction"), this, &TransactionView::copyTxHex);
+ contextMenu->addAction(tr("Copy full transaction &details"), this, &TransactionView::copyTxPlainText);
+ contextMenu->addAction(tr("&Show transaction details"), this, &TransactionView::showDetails);
contextMenu->addSeparator();
- bumpFeeAction = contextMenu->addAction(tr("Increase transaction fee"));
+ bumpFeeAction = contextMenu->addAction(tr("Increase transaction &fee"));
GUIUtil::ExceptionSafeConnect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
bumpFeeAction->setObjectName("bumpFeeAction");
- abandonAction = contextMenu->addAction(tr("Abandon transaction"), this, &TransactionView::abandonTx);
- contextMenu->addAction(tr("Edit address label"), this, &TransactionView::editLabel);
+ abandonAction = contextMenu->addAction(tr("A&bandon transaction"), this, &TransactionView::abandonTx);
+ contextMenu->addAction(tr("&Edit address label"), this, &TransactionView::editLabel);
connect(dateWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseDate);
connect(typeWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseType);
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 7e5790fd87..3cceb5ca5a 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -11,6 +11,7 @@
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
+#include <external_signer.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
@@ -295,7 +296,6 @@ void CreateWalletActivity::create()
{
m_create_wallet_dialog = new CreateWalletDialog(m_parent_widget);
-#ifdef ENABLE_EXTERNAL_SIGNER
std::vector<ExternalSigner> signers;
try {
signers = node().externalSigners();
@@ -303,7 +303,6 @@ void CreateWalletActivity::create()
QMessageBox::critical(nullptr, tr("Can't list signers"), e.what());
}
m_create_wallet_dialog->setSigners(signers);
-#endif
m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
m_create_wallet_dialog->show();
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 0f2ea99685..a1f357e0db 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -242,8 +242,3 @@ WalletModel* WalletFrame::currentWalletModel() const
WalletView* wallet_view = currentWalletView();
return wallet_view ? wallet_view->getWalletModel() : nullptr;
}
-
-void WalletFrame::outOfSyncWarningClicked()
-{
- Q_EMIT requestedSyncWarningInfo();
-}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 844ed121a0..4f77bd716f 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -47,9 +47,6 @@ public:
QSize sizeHint() const override { return m_size_hint; }
Q_SIGNALS:
- /** Notify that the user has requested more information about the out-of-sync warning */
- void requestedSyncWarningInfo();
-
void createWalletButtonClicked();
private:
@@ -98,8 +95,6 @@ public Q_SLOTS:
void usedSendingAddresses();
/** Show used receiving addresses */
void usedReceivingAddresses();
- /** Pass on signal over requested out-of-sync-warning information */
- void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETFRAME_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e32b7c2807..967dd588b4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -525,7 +525,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index cc9e1502f0..3b8cf4c7ed 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -73,7 +73,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
- connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
+ connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::outOfSyncWarningClicked);
connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
// Highlight transaction after send
@@ -347,8 +347,3 @@ void WalletView::showProgress(const QString &title, int nProgress)
}
}
}
-
-void WalletView::requestedSyncWarningInfo()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 68f8a5e95b..fedf06b710 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -111,9 +111,6 @@ public Q_SLOTS:
/** Show progress dialog e.g. for rescan */
void showProgress(const QString &title, int nProgress);
- /** User has requested more information about the out of sync state */
- void requestedSyncWarningInfo();
-
Q_SIGNALS:
void setPrivacy(bool privacy);
void transactionClicked();
diff --git a/src/rest.cpp b/src/rest.cpp
index 747c7aea19..d599f381e3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -125,7 +125,7 @@ static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
__FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
return nullptr;
}
- return node_context->chainman;
+ return node_context->chainman.get();
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 83c1975d38..63897e0e05 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -84,7 +84,6 @@ ChainstateManager& EnsureChainman(const NodeContext& node)
if (!node.chainman) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
}
- WITH_LOCK(::cs_main, CHECK_NONFATAL(std::addressof(g_chainman) == std::addressof(*node.chainman)));
return *node.chainman;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 6826e6fd07..327f961196 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -114,7 +114,6 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
{
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
}
@@ -147,7 +146,6 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{ // Don't keep cs_main locked
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
nHeight = chainman.ActiveChain().Height();
nHeightEnd = nHeight+nGenerate;
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 339d711ac9..ccb3123714 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -753,7 +753,8 @@ static RPCHelpMan signrawtransactionwithkey()
},
},
},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of:\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of:\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -889,7 +890,7 @@ static RPCHelpMan testmempoolaccept()
"\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
"\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
"\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
- "\nThe maximum number of transactions allowed is 25 (MAX_PACKAGE_COUNT)\n"
+ "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
"\nThis checks if transactions violate the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
{
@@ -905,7 +906,7 @@ static RPCHelpMan testmempoolaccept()
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
"Returns results for each transaction in the same order they were passed in.\n"
- "It is possible for transactions to not be fully validated ('allowed' unset) if an earlier transaction failed.\n",
+ "It is possible for transactions to not be fully validated ('allowed' unset) if another transaction failed.\n",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -939,7 +940,6 @@ static RPCHelpMan testmempoolaccept()
UniValue::VARR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
});
-
const UniValue raw_transactions = request.params[0].get_array();
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
@@ -951,6 +951,7 @@ static RPCHelpMan testmempoolaccept()
CFeeRate(AmountFromValue(request.params[1]));
std::vector<CTransactionRef> txns;
+ txns.reserve(raw_transactions.size());
for (const auto& rawtx : raw_transactions.getValues()) {
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, rawtx.get_str())) {
@@ -971,8 +972,8 @@ static RPCHelpMan testmempoolaccept()
}();
UniValue rpc_result(UniValue::VARR);
- // We will check transaction fees we iterate through txns in order. If any transaction fee
- // exceeds maxfeerate, we will keave the rest of the validation results blank, because it
+ // We will check transaction fees while we iterate through txns in order. If any transaction fee
+ // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
// doesn't make sense to return a validation result for a transaction if its ancestor(s) would
// not be submitted.
bool exit_early{false};
@@ -1655,6 +1656,7 @@ static RPCHelpMan utxoupdatepsbt()
}
// Fill the inputs
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs.at(i);
@@ -1671,7 +1673,7 @@ static RPCHelpMan utxoupdatepsbt()
// Update script/keypath information using descriptor data.
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
// we don't actually care about those here, in fact.
- SignPSBTInput(public_provider, psbtx, i, /* sighash_type */ 1);
+ SignPSBTInput(public_provider, psbtx, i, &txdata, /* sighash_type */ 1);
}
// Update script/keypath information using descriptor data.
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 51cf8a7d62..84a8b06c5c 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -843,7 +843,9 @@ protected:
XOnlyPubKey xpk(keys[0]);
if (!xpk.IsFullyValid()) return {};
builder.Finalize(xpk);
- return Vector(GetScriptForDestination(builder.GetOutput()));
+ WitnessV1Taproot output = builder.GetOutput();
+ out.tr_spenddata[output].Merge(builder.GetSpendData());
+ return Vector(GetScriptForDestination(output));
}
bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override
{
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 3c3c3ac1a8..2dd173ee20 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1420,7 +1420,7 @@ uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
} // namespace
template <class T>
-void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
+void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs, bool force)
{
assert(!m_spent_outputs_ready);
@@ -1431,9 +1431,9 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
}
// Determine which precomputation-impacting features this transaction uses.
- bool uses_bip143_segwit = false;
- bool uses_bip341_taproot = false;
- for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
+ bool uses_bip143_segwit = force;
+ bool uses_bip341_taproot = force;
+ for (size_t inpos = 0; inpos < txTo.vin.size() && !(uses_bip143_segwit && uses_bip341_taproot); ++inpos) {
if (!txTo.vin[inpos].scriptWitness.IsNull()) {
if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
m_spent_outputs[inpos].scriptPubKey[0] == OP_1) {
@@ -1478,8 +1478,8 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
}
// explicit instantiation
-template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
-template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
+template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
+template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
@@ -1711,7 +1711,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const uns
if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
uint256 sighash;
- assert(this->txdata);
+ if (!this->txdata) return HandleMissingData(m_mdb);
if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata, m_mdb)) {
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
}
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index fa4ee83e04..ced5c28bc1 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -168,7 +168,7 @@ struct PrecomputedTransactionData
PrecomputedTransactionData() = default;
template <class T>
- void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
+ void Init(const T& tx, std::vector<CTxOut>&& spent_outputs, bool force = false);
template <class T>
explicit PrecomputedTransactionData(const T& tx);
@@ -260,6 +260,9 @@ enum class MissingDataBehavior
FAIL, //!< Just act as if the signature was invalid
};
+template<typename T>
+bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
+
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index da0092f9e3..65276f641f 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -11,13 +11,28 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
+#include <util/vector.h>
typedef std::vector<unsigned char> valtype;
-MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL) {}
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn)
+ : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn, MissingDataBehavior::FAIL),
+ m_txdata(nullptr)
+{
+}
+
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn)
+ : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn),
+ checker(txdata ? MutableTransactionSignatureChecker(txTo, nIn, amount, *txdata, MissingDataBehavior::FAIL) :
+ MutableTransactionSignatureChecker(txTo, nIn, amount, MissingDataBehavior::FAIL)),
+ m_txdata(txdata)
+{
+}
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
+ assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
+
CKey key;
if (!provider.GetKey(address, key))
return false;
@@ -26,13 +41,61 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
- // Signing for witness scripts needs the amount.
- if (sigversion == SigVersion::WITNESS_V0 && amount < 0) return false;
+ // Signing without known amount does not work in witness scripts.
+ if (sigversion == SigVersion::WITNESS_V0 && !MoneyRange(amount)) return false;
+
+ // BASE/WITNESS_V0 signatures don't support explicit SIGHASH_DEFAULT, use SIGHASH_ALL instead.
+ const int hashtype = nHashType == SIGHASH_DEFAULT ? SIGHASH_ALL : nHashType;
- uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
+ uint256 hash = SignatureHash(scriptCode, *txTo, nIn, hashtype, amount, sigversion, m_txdata);
if (!key.Sign(hash, vchSig))
return false;
- vchSig.push_back((unsigned char)nHashType);
+ vchSig.push_back((unsigned char)hashtype);
+ return true;
+}
+
+bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const
+{
+ assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
+
+ CKey key;
+ {
+ // For now, use the old full pubkey-based key derivation logic. As it indexed by
+ // Hash160(full pubkey), we need to try both a version prefixed with 0x02, and one
+ // with 0x03.
+ unsigned char b[33] = {0x02};
+ std::copy(pubkey.begin(), pubkey.end(), b + 1);
+ CPubKey fullpubkey;
+ fullpubkey.Set(b, b + 33);
+ CKeyID keyid = fullpubkey.GetID();
+ if (!provider.GetKey(keyid, key)) {
+ b[0] = 0x03;
+ fullpubkey.Set(b, b + 33);
+ CKeyID keyid = fullpubkey.GetID();
+ if (!provider.GetKey(keyid, key)) return false;
+ }
+ }
+
+ // BIP341/BIP342 signing needs lots of precomputed transaction data. While some
+ // (non-SIGHASH_DEFAULT) sighash modes exist that can work with just some subset
+ // of data present, for now, only support signing when everything is provided.
+ if (!m_txdata || !m_txdata->m_bip341_taproot_ready || !m_txdata->m_spent_outputs_ready) return false;
+
+ ScriptExecutionData execdata;
+ execdata.m_annex_init = true;
+ execdata.m_annex_present = false; // Only support annex-less signing for now.
+ if (sigversion == SigVersion::TAPSCRIPT) {
+ execdata.m_codeseparator_pos_init = true;
+ execdata.m_codeseparator_pos = 0xFFFFFFFF; // Only support non-OP_CODESEPARATOR BIP342 signing for now.
+ if (!leaf_hash) return false; // BIP342 signing needs leaf hash.
+ execdata.m_tapleaf_hash_init = true;
+ execdata.m_tapleaf_hash = *leaf_hash;
+ }
+ uint256 hash;
+ if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
+ sig.resize(64);
+ if (!key.SignSchnorr(hash, sig, merkle_root, nullptr)) return false;
+ if (nHashType) sig.push_back(nHashType);
return true;
}
@@ -92,6 +155,86 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
return false;
}
+static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion)
+{
+ auto lookup_key = std::make_pair(pubkey, leaf_hash);
+ auto it = sigdata.taproot_script_sigs.find(lookup_key);
+ if (it != sigdata.taproot_script_sigs.end()) {
+ sig_out = it->second;
+ }
+ if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
+ sigdata.taproot_script_sigs[lookup_key] = sig_out;
+ return true;
+ }
+ return false;
+}
+
+static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, const CScript& script, std::vector<valtype>& result)
+{
+ // Only BIP342 tapscript signing is supported for now.
+ if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false;
+ SigVersion sigversion = SigVersion::TAPSCRIPT;
+
+ uint256 leaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(leaf_version) << script).GetSHA256();
+
+ // <xonly pubkey> OP_CHECKSIG
+ if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
+ XOnlyPubKey pubkey(MakeSpan(script).subspan(1, 32));
+ std::vector<unsigned char> sig;
+ if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) {
+ result = Vector(std::move(sig));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result)
+{
+ TaprootSpendData spenddata;
+
+ // Gather information about this output.
+ if (provider.GetTaprootSpendData(output, spenddata)) {
+ sigdata.tr_spenddata.Merge(spenddata);
+ }
+
+ // Try key path spending.
+ {
+ std::vector<unsigned char> sig;
+ if (sigdata.taproot_key_path_sig.size() == 0) {
+ if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) {
+ sigdata.taproot_key_path_sig = sig;
+ }
+ }
+ if (sigdata.taproot_key_path_sig.size()) {
+ result = Vector(sigdata.taproot_key_path_sig);
+ return true;
+ }
+ }
+
+ // Try script path spending.
+ std::vector<std::vector<unsigned char>> smallest_result_stack;
+ for (const auto& [key, control_blocks] : sigdata.tr_spenddata.scripts) {
+ const auto& [script, leaf_ver] = key;
+ std::vector<std::vector<unsigned char>> result_stack;
+ if (SignTaprootScript(provider, creator, sigdata, leaf_ver, script, result_stack)) {
+ result_stack.emplace_back(std::begin(script), std::end(script)); // Push the script
+ result_stack.push_back(*control_blocks.begin()); // Push the smallest control block
+ if (smallest_result_stack.size() == 0 ||
+ GetSerializeSize(result_stack, PROTOCOL_VERSION) < GetSerializeSize(smallest_result_stack, PROTOCOL_VERSION)) {
+ smallest_result_stack = std::move(result_stack);
+ }
+ }
+ }
+ if (smallest_result_stack.size() != 0) {
+ result = std::move(smallest_result_stack);
+ return true;
+ }
+
+ return false;
+}
+
/**
* Sign scriptPubKey using signature made with creator.
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
@@ -113,7 +256,6 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
- case TxoutType::WITNESS_V1_TAPROOT:
return false;
case TxoutType::PUBKEY:
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
@@ -175,6 +317,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
// Could not find witnessScript, add to missing
sigdata.missing_witness_script = uint256(vSolutions[0]);
return false;
+
+ case TxoutType::WITNESS_V1_TAPROOT:
+ return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret);
} // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -205,7 +350,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
- sigdata.scriptWitness.stack.clear();
if (solved && whichType == TxoutType::SCRIPTHASH)
{
@@ -238,10 +382,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
+ } else if (whichType == TxoutType::WITNESS_V1_TAPROOT && !P2SH) {
+ sigdata.witness = true;
+ if (solved) {
+ sigdata.scriptWitness.stack = std::move(result);
+ }
+ result.clear();
} else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
sigdata.witness = true;
}
+ if (!sigdata.witness) sigdata.scriptWitness.stack.clear();
if (P2SH) {
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
}
@@ -402,6 +553,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
public:
DummySignatureChecker() {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
};
const DummySignatureChecker DUMMY_CHECKER;
@@ -427,6 +579,11 @@ public:
vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL;
return true;
}
+ bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* tweak, SigVersion sigversion) const override
+ {
+ sig.assign(64, '\000');
+ return true;
+ }
};
}
@@ -476,6 +633,26 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mtx);
+
+ PrecomputedTransactionData txdata;
+ std::vector<CTxOut> spent_outputs;
+ spent_outputs.resize(mtx.vin.size());
+ bool have_all_spent_outputs = true;
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ have_all_spent_outputs = false;
+ } else {
+ spent_outputs[i] = CTxOut(coin->second.out.nValue, coin->second.out.scriptPubKey);
+ }
+ }
+ if (have_all_spent_outputs) {
+ txdata.Init(txConst, std::move(spent_outputs), true);
+ } else {
+ txdata.Init(txConst, {}, true);
+ }
+
// Sign what we can:
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
CTxIn& txin = mtx.vin[i];
@@ -490,7 +667,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
@@ -502,7 +679,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
}
ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, MissingDataBehavior::FAIL), &serror)) {
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
diff --git a/src/script/sign.h b/src/script/sign.h
index a1cfe1574d..b4e7318892 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -11,13 +11,13 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/keyorigin.h>
+#include <script/standard.h>
#include <span.h>
#include <streams.h>
class CKey;
class CKeyID;
class CScript;
-class CScriptID;
class CTransaction;
class SigningProvider;
@@ -31,6 +31,7 @@ public:
/** Create a singular (non-script) signature. */
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
+ virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
};
/** A signature creator for transactions. */
@@ -40,11 +41,14 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
int nHashType;
CAmount amount;
const MutableTransactionSignatureChecker checker;
+ const PrecomputedTransactionData* m_txdata;
public:
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
+ MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
+ bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
};
/** A signature creator that just produces 71-byte empty signatures. */
@@ -64,8 +68,11 @@ struct SignatureData {
CScript redeem_script; ///< The redeemScript (if any) for the input
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
+ TaprootSpendData tr_spenddata; ///< Taproot spending data.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
+ std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending
+ std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash.
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
index 9781ec32af..b80fbe22ce 100644
--- a/src/script/signingprovider.cpp
+++ b/src/script/signingprovider.cpp
@@ -44,6 +44,11 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
return m_provider->GetKeyOrigin(keyid, info);
}
+bool HidingSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
+{
+ return m_provider->GetTaprootSpendData(output_key, spenddata);
+}
+
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
@@ -54,6 +59,10 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info)
return ret;
}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
+bool FlatSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
+{
+ return LookupHelper(tr_spenddata, output_key, spenddata);
+}
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
{
@@ -66,6 +75,10 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
ret.keys.insert(b.keys.begin(), b.keys.end());
ret.origins = a.origins;
ret.origins.insert(b.origins.begin(), b.origins.end());
+ ret.tr_spenddata = a.tr_spenddata;
+ for (const auto& [output_key, spenddata] : b.tr_spenddata) {
+ ret.tr_spenddata[output_key].Merge(spenddata);
+ }
return ret;
}
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index 76f31d2f6f..939ae10622 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -25,6 +25,7 @@ public:
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
virtual bool HaveKey(const CKeyID &address) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
+ virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
@@ -42,6 +43,7 @@ public:
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+ bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
};
struct FlatSigningProvider final : public SigningProvider
@@ -50,11 +52,13 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
+ std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata; /** Map from output key to spend data. */
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
+ bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
};
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index a4b11cc0a9..748f00dda5 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -377,6 +377,16 @@ bool IsValidDestination(const CTxDestination& dest) {
/*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b)
{
NodeInfo ret;
+ /* Iterate over all tracked leaves in a, add b's hash to their Merkle branch, and move them to ret. */
+ for (auto& leaf : a.leaves) {
+ leaf.merkle_branch.push_back(b.hash);
+ ret.leaves.emplace_back(std::move(leaf));
+ }
+ /* Iterate over all tracked leaves in b, add a's hash to their Merkle branch, and move them to ret. */
+ for (auto& leaf : b.leaves) {
+ leaf.merkle_branch.push_back(a.hash);
+ ret.leaves.emplace_back(std::move(leaf));
+ }
/* Lexicographically sort a and b's hash, and compute parent hash. */
if (a.hash < b.hash) {
ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256();
@@ -386,6 +396,21 @@ bool IsValidDestination(const CTxDestination& dest) {
return ret;
}
+void TaprootSpendData::Merge(TaprootSpendData other)
+{
+ // TODO: figure out how to better deal with conflicting information
+ // being merged.
+ if (internal_key.IsNull() && !other.internal_key.IsNull()) {
+ internal_key = other.internal_key;
+ }
+ if (merkle_root.IsNull() && !other.merkle_root.IsNull()) {
+ merkle_root = other.merkle_root;
+ }
+ for (auto& [key, control_blocks] : other.scripts) {
+ scripts[key].merge(std::move(control_blocks));
+ }
+}
+
void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
{
assert(depth >= 0 && (size_t)depth <= TAPROOT_CONTROL_MAX_NODE_COUNT);
@@ -435,13 +460,14 @@ void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
return branch.size() == 0 || (branch.size() == 1 && branch[0]);
}
-TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version)
+TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version, bool track)
{
assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
if (!IsValid()) return *this;
- /* Construct NodeInfo object with leaf hash. */
+ /* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */
NodeInfo node;
node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
+ if (track) node.leaves.emplace_back(LeafInfo{script, leaf_version, {}});
/* Insert into the branch. */
Insert(std::move(node), depth);
return *this;
@@ -464,8 +490,33 @@ TaprootBuilder& TaprootBuilder::Finalize(const XOnlyPubKey& internal_key)
m_internal_key = internal_key;
auto ret = m_internal_key.CreateTapTweak(m_branch.size() == 0 ? nullptr : &m_branch[0]->hash);
assert(ret.has_value());
- std::tie(m_output_key, std::ignore) = *ret;
+ std::tie(m_output_key, m_parity) = *ret;
return *this;
}
WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_key}; }
+
+TaprootSpendData TaprootBuilder::GetSpendData() const
+{
+ TaprootSpendData spd;
+ spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
+ spd.internal_key = m_internal_key;
+ if (m_branch.size()) {
+ // If any script paths exist, they have been combined into the root m_branch[0]
+ // by now. Compute the control block for each of its tracked leaves, and put them in
+ // spd.scripts.
+ for (const auto& leaf : m_branch[0]->leaves) {
+ std::vector<unsigned char> control_block;
+ control_block.resize(TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * leaf.merkle_branch.size());
+ control_block[0] = leaf.leaf_version | (m_parity ? 1 : 0);
+ std::copy(m_internal_key.begin(), m_internal_key.end(), control_block.begin() + 1);
+ if (leaf.merkle_branch.size()) {
+ std::copy(leaf.merkle_branch[0].begin(),
+ leaf.merkle_branch[0].begin() + TAPROOT_CONTROL_NODE_SIZE * leaf.merkle_branch.size(),
+ control_block.begin() + TAPROOT_CONTROL_BASE_SIZE);
+ }
+ spd.scripts[{leaf.script, leaf.leaf_version}].insert(std::move(control_block));
+ }
+ }
+ return spd;
+}
diff --git a/src/script/standard.h b/src/script/standard.h
index d7ea5cef27..285dd4c116 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -11,6 +11,7 @@
#include <uint256.h>
#include <util/hash_type.h>
+#include <map>
#include <string>
#include <variant>
@@ -209,15 +210,50 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
+struct ShortestVectorFirstComparator
+{
+ bool operator()(const std::vector<unsigned char>& a, const std::vector<unsigned char>& b) const
+ {
+ if (a.size() < b.size()) return true;
+ if (a.size() > b.size()) return false;
+ return a < b;
+ }
+};
+
+struct TaprootSpendData
+{
+ /** The BIP341 internal key. */
+ XOnlyPubKey internal_key;
+ /** The Merkle root of the script tree (0 if no scripts). */
+ uint256 merkle_root;
+ /** Map from (script, leaf_version) to (sets of) control blocks.
+ * The control blocks are sorted by size, so that the signing logic can
+ * easily prefer the cheapest one. */
+ std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
+ /** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
+ void Merge(TaprootSpendData other);
+};
+
/** Utility class to construct Taproot outputs from internal key and script tree. */
class TaprootBuilder
{
private:
+ /** Information about a tracked leaf in the Merkle tree. */
+ struct LeafInfo
+ {
+ CScript script; //!< The script.
+ int leaf_version; //!< The leaf version for that script.
+ std::vector<uint256> merkle_branch; //!< The hashing partners above this leaf.
+ };
+
/** Information associated with a node in the Merkle tree. */
struct NodeInfo
{
/** Merkle hash of this node. */
uint256 hash;
+ /** Tracked leaves underneath this node (either from the node itself, or its children).
+ * The merkle_branch field for each is the partners to get to *this* node. */
+ std::vector<LeafInfo> leaves;
};
/** Whether the builder is in a valid state so far. */
bool m_valid = true;
@@ -260,7 +296,8 @@ private:
std::vector<std::optional<NodeInfo>> m_branch;
XOnlyPubKey m_internal_key; //!< The internal key, set when finalizing.
- XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */
+ XOnlyPubKey m_output_key; //!< The output key, computed when finalizing.
+ bool m_parity; //!< The tweak parity, computed when finalizing.
/** Combine information about a parent Merkle tree node from its child nodes. */
static NodeInfo Combine(NodeInfo&& a, NodeInfo&& b);
@@ -269,8 +306,9 @@ private:
public:
/** Add a new script at a certain depth in the tree. Add() operations must be called
- * in depth-first traversal order of binary tree. */
- TaprootBuilder& Add(int depth, const CScript& script, int leaf_version);
+ * in depth-first traversal order of binary tree. If track is true, it will be included in
+ * the GetSpendData() output. */
+ TaprootBuilder& Add(int depth, const CScript& script, int leaf_version, bool track = true);
/** Like Add(), but for a Merkle node with a given hash to the tree. */
TaprootBuilder& AddOmitted(int depth, const uint256& hash);
/** Finalize the construction. Can only be called when IsComplete() is true.
@@ -285,6 +323,8 @@ public:
WitnessV1Taproot GetOutput();
/** Check if a list of depths is legal (will lead to IsComplete()). */
static bool ValidDepths(const std::vector<int>& depths);
+ /** Compute spending data (after Finalize()). */
+ TaprootSpendData GetSpendData() const;
};
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 49b40924e0..eb5c37b34d 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -74,9 +74,9 @@ public:
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(const CService& addr)
{
- LOCK(cs);
int64_t nLastSuccess = 1;
- Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
+ // Set last good connection in the deep past.
+ Good(addr, true, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime()-61;
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 2f532ef598..2eb653e9ec 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(::ChainstateActive(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
@@ -117,9 +117,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
std::vector<BlockFilter> filters;
std::vector<uint256> filter_hashes;
- for (const CBlockIndex* block_index = ::ChainActive().Genesis();
+ for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
@@ -131,7 +131,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// BlockUntilSyncedToCurrentChain should return false before index is started.
BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(filter_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(filter_index.Start(m_node.chainman->ActiveChainstate()));
// Allow filter index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
@@ -145,9 +145,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
const CBlockIndex* block_index;
- for (block_index = ::ChainActive().Genesis();
+ for (block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
CheckFilterLookups(filter_index, block_index, last_header);
}
}
@@ -156,7 +156,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* tip;
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
CKey coinbase_key_A, coinbase_key_B;
coinbase_key_A.MakeNewKey(true);
@@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -210,7 +210,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -231,14 +231,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainA_last_header);
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainB_last_header);
@@ -250,7 +250,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 106fcd2a33..597d7a7340 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -22,7 +22,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = ChainActive().Tip();
+ block_index = m_node.chainman->ActiveChain().Tip();
}
// CoinStatsIndex should not be found before it is started.
@@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// is started.
BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(coin_stats_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(coin_stats_index.Start(m_node.chainman->ActiveChainstate()));
// Allow the CoinStatsIndex to catch up with the block index that is syncing
// in a background thread.
@@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* genesis_block_index;
{
LOCK(cs_main);
- genesis_block_index = ChainActive().Genesis();
+ genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
@@ -64,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* new_block_index;
{
LOCK(cs_main);
- new_block_index = ChainActive().Tip();
+ new_block_index = m_node.chainman->ActiveChain().Tip();
}
coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index a56ce51acb..57178d015d 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -14,36 +14,19 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
#include <txorphanage.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
-#include <test/util/setup_common.h>
-
#include <array>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
-struct CConnmanTest : public CConnman {
- using CConnman::CConnman;
- void AddNode(CNode& node)
- {
- LOCK(cs_vNodes);
- vNodes.push_back(&node);
- }
- void ClearNodes()
- {
- LOCK(cs_vNodes);
- for (CNode* node : vNodes) {
- delete node;
- }
- vNodes.clear();
- }
-};
-
static CService ip(uint32_t i)
{
struct in_addr s;
@@ -83,8 +66,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// This test requires that we have a chain with non-zero work.
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip() != nullptr);
- BOOST_CHECK(::ChainActive().Tip()->nChainWork > 0);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip() != nullptr);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->nChainWork > 0);
}
// Test starts here
@@ -120,7 +103,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1);
}
-static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
+static void AddRandomOutboundPeer(std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
@@ -130,13 +113,13 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee
peerLogic.InitializeNode(&node);
node.fSuccessfullyConnected = true;
- connman->AddNode(node);
+ connman.AddTestNode(node);
}
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
const CChainParams& chainparams = Params();
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -150,8 +133,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
std::vector<CNode *> vNodes;
// Mock some outbound peers
- for (int i=0; i<max_outbound_full_relay; ++i) {
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ for (int i = 0; i < max_outbound_full_relay; ++i) {
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
}
peerLogic->CheckForStaleTipAndEvictPeers();
@@ -176,7 +159,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// If we add one more peer, something should get marked for eviction
// on the next check (since we're mocking the time to be in the future, the
// required time connected check should be satisfied).
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay; ++i) {
@@ -202,14 +185,14 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -233,7 +216,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[0]);
nodes[0]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[0]);
+ connman->AddTestNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(nodes[0]->cs_sendProcessing);
@@ -249,7 +232,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[1]);
nodes[1]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[1]);
+ connman->AddTestNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(nodes[1]->cs_sendProcessing);
@@ -280,7 +263,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[2]);
nodes[2]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[2]);
+ connman->AddTestNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(nodes[2]->cs_sendProcessing);
@@ -296,7 +279,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
for (CNode* node : nodes) {
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 98ae32a8d0..db0b461873 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -57,15 +57,6 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
(void)addr_man.SelectTriedCollision();
},
[&] {
- (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
- },
- [&] {
- (void)addr_man.GetAddr(
- /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* network */ std::nullopt);
- },
- [&] {
const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
if (opt_address && opt_net_addr) {
@@ -109,12 +100,15 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
if (opt_service) {
addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
}
- },
- [&] {
- (void)addr_man.Check();
});
}
- (void)addr_man.size();
+ const CAddrMan& const_addr_man{addr_man};
+ (void)/*const_*/addr_man.GetAddr(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* network */ std::nullopt);
+ (void)/*const_*/addr_man.Select(fuzzed_data_provider.ConsumeBool());
+ (void)const_addr_man.size();
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
- data_stream << addr_man;
+ data_stream << const_addr_man;
}
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index b509ee0b26..42f19d16c6 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -27,6 +27,7 @@
#include <vector>
namespace {
+const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
bool operator==(const Coin& a, const Coin& b)
@@ -39,6 +40,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
@@ -268,7 +270,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index eeeac18968..f83747e424 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -19,6 +19,10 @@
FUZZ_TARGET(crypto)
{
+ // Hashing is expensive with sanitizers enabled, so limit the number of
+ // calls
+ int limit_max_ops{30};
+
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
if (data.empty()) {
@@ -36,7 +40,7 @@ FUZZ_TARGET(crypto)
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
- while (fuzzed_data_provider.ConsumeBool()) {
+ while (--limit_max_ops >= 0 && fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
[&] {
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 1290c78712..721e4360d0 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -53,9 +53,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};
template <typename T>
-CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION)
+CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
- CDataStream ds(SER_NETWORK, version);
+ CDataStream ds(ser_type, version);
ds << obj;
return ds;
}
@@ -69,9 +69,9 @@ T Deserialize(CDataStream ds)
}
template <typename T>
-void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt)
+void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optional<int> protocol_version = std::nullopt, const int ser_type = SER_NETWORK)
{
- CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
+ CDataStream ds(buffer, ser_type, INIT_PROTO_VERSION);
if (protocol_version) {
ds.SetVersion(*protocol_version);
} else {
@@ -92,9 +92,9 @@ void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const std::optio
}
template <typename T>
-void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
+void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION, const int ser_type = SER_NETWORK)
{
- assert(Deserialize<T>(Serialize(obj, version)) == obj);
+ assert(Deserialize<T>(Serialize(obj, version, ser_type)) == obj);
}
} // namespace
@@ -136,8 +136,7 @@ FUZZ_TARGET_DESERIALIZE(partial_merkle_tree_deserialize, {
FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, {
CPubKey pub_key;
DeserializeFromFuzzingInput(buffer, pub_key);
- // TODO: The following equivalence should hold for CPubKey? Fix.
- // AssertEqualAfterSerializeDeserialize(pub_key);
+ AssertEqualAfterSerializeDeserialize(pub_key);
})
FUZZ_TARGET_DESERIALIZE(script_deserialize, {
CScript script;
@@ -251,9 +250,37 @@ FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, {
DeserializeFromFuzzingInput(buffer, mh);
(void)mh.IsCommandValid();
})
-FUZZ_TARGET_DESERIALIZE(address_deserialize, {
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_notime, {
CAddress a;
- DeserializeFromFuzzingInput(buffer, a);
+ DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION);
+ // A CAddress without nTime (as is expected under INIT_PROTO_VERSION) will roundtrip
+ // in all 5 formats (with/without nTime, v1/v2, network/disk)
+ AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+})
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_withtime, {
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION);
+ // A CAddress in V1 mode will roundtrip in all 4 formats that have nTime.
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+})
+FUZZ_TARGET_DESERIALIZE(address_deserialize_v2, {
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ // A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
+ // with time if it's V1 compatible.
+ if (a.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ }
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
})
FUZZ_TARGET_DESERIALIZE(inv_deserialize, {
CInv i;
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index dbd0c76d42..a7770c90e8 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -13,9 +13,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_load_external_block_file()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
@@ -27,5 +32,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
return;
}
FlatFilePos flat_file_pos;
- ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
+ g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
}
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
index 70ffc6bf37..a3f71426fa 100644
--- a/src/test/fuzz/node_eviction.cpp
+++ b/src/test/fuzz/node_eviction.cpp
@@ -31,7 +31,7 @@ FUZZ_TARGET(node_eviction)
/* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
/* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
/* m_is_local */ fuzzed_data_provider.ConsumeBool(),
- /* m_is_onion */ fuzzed_data_provider.ConsumeBool(),
+ /* m_network */ fuzzed_data_provider.PickValueInArray(ALL_NETWORKS),
});
}
// Make a copy since eviction_candidates may be in some valid but otherwise
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 7b99193ad0..c4e4d4c785 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -58,7 +58,19 @@ void initialize_process_message()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
+
+ // Temporary debug for https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=35027
+ {
+ LOCK(::cs_main);
+ assert(CheckDiskSpace(gArgs.GetDataDirNet()));
+ assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
+ }
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
+ {
+ LOCK(::cs_main);
+ assert(CheckDiskSpace(gArgs.GetDataDirNet()));
+ assert(CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * g_setup->m_node.chainman->ActiveChainstate().CoinsTip().GetCacheSize()));
+ }
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index e1a21b6c53..c2aaf486c5 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -14,9 +14,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_validation_load_mempool()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
@@ -29,6 +34,6 @@ FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
auto fuzzed_fopen = [&](const fs::path&, const char*) {
return fuzzed_file_provider.open();
};
- (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen);
+ (void)LoadMempool(pool, g_setup->m_node.chainman->ActiveChainstate(), fuzzed_fopen);
(void)DumpMempool(pool, fuzzed_fopen, true);
}
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index 73463b071e..42a7c7798c 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
auto* orig_tip = active.Tip();
for (int i = 0; i < 10; ++i) {
BlockValidationState state;
- ChainstateActive().InvalidateBlock(state, Params(), active.Tip());
+ m_node.chainman->ActiveChainstate().InvalidateBlock(state, Params(), active.Tip());
}
BOOST_CHECK_EQUAL(active.Height(), orig_tip->nHeight - 10);
coinbaseKey.MakeNewKey(true);
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index cb66d5164e..b915982d98 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -300,6 +300,48 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
auto sig = ParseHex(test.first[2]);
BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second);
}
+
+ static const std::vector<std::array<std::string, 5>> SIGN_VECTORS = {
+ {{"0000000000000000000000000000000000000000000000000000000000000003", "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}},
+ {{"B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "0000000000000000000000000000000000000000000000000000000000000001", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}},
+ {{"C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}},
+ {{"0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}},
+ };
+
+ for (const auto& [sec_hex, pub_hex, aux_hex, msg_hex, sig_hex] : SIGN_VECTORS) {
+ auto sec = ParseHex(sec_hex);
+ auto pub = ParseHex(pub_hex);
+ uint256 aux256(ParseHex(aux_hex));
+ uint256 msg256(ParseHex(msg_hex));
+ auto sig = ParseHex(sig_hex);
+ unsigned char sig64[64];
+
+ // Run the untweaked test vectors above, comparing with exact expected signature.
+ CKey key;
+ key.Set(sec.begin(), sec.end(), true);
+ XOnlyPubKey pubkey(key.GetPubKey());
+ BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end()));
+ bool ok = key.SignSchnorr(msg256, sig64, nullptr, &aux256);
+ BOOST_CHECK(ok);
+ BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
+ // Verify those signatures for good measure.
+ BOOST_CHECK(pubkey.VerifySchnorr(msg256, sig64));
+
+ // Do 10 iterations where we sign with a random Merkle root to tweak,
+ // and compare against the resulting tweaked keys, with random aux.
+ // In iteration i=0 we tweak with empty Merkle tree.
+ for (int i = 0; i < 10; ++i) {
+ uint256 merkle_root;
+ if (i) merkle_root = InsecureRand256();
+ auto tweaked = pubkey.CreateTapTweak(i ? &merkle_root : nullptr);
+ BOOST_CHECK(tweaked);
+ XOnlyPubKey tweaked_key = tweaked->first;
+ aux256 = InsecureRand256();
+ bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, &aux256);
+ BOOST_CHECK(ok);
+ BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
+ }
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index c47d0eae1e..e20c5e4e8f 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -28,8 +28,8 @@ struct MinerTestingSetup : public TestingSetup {
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
{
- CCoinsViewMemPool viewMempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
- return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), viewMempool, tx, flags);
+ CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
+ return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), view_mempool, tx, flags);
}
BlockAssembler AssemblerForTest(const CChainParams& params);
};
@@ -45,7 +45,7 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler(::ChainstateActive(), *m_node.mempool, params, options);
+ return BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, params, options);
}
constexpr static struct {
@@ -82,11 +82,11 @@ constexpr static struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CBlockIndex index;
index.nHeight = nHeight;
- index.pprev = ::ChainActive().Tip();
+ index.pprev = active_chain_tip;
return index;
}
@@ -228,17 +228,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
LOCK(cs_main);
pblock->nVersion = 1;
- pblock->nTime = ::ChainActive().Tip()->GetMedianTimePast()+1;
+ pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(bi.extranonce);
- txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
+ txCoinbase.vin[0].scriptSig.push_back(m_node.chainman->ActiveChain().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
- baseheight = ::ChainActive().Height();
+ baseheight = m_node.chainman->ActiveChain().Height();
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -364,29 +364,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// subsidy changing
- int nHeight = ::ChainActive().Height();
+ int nHeight = m_node.chainman->ActiveChain().Height();
// Create an actual 209999-long block chain (without valid blocks).
- while (::ChainActive().Tip()->nHeight < 209999) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
- while (::ChainActive().Tip()->nHeight < 210000) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -409,16 +409,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// Delete the dummy blocks again.
- while (::ChainActive().Tip()->nHeight > nHeight) {
- CBlockIndex* del = ::ChainActive().Tip();
- ::ChainActive().SetTip(del->pprev);
- ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
+ while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = m_node.chainman->ActiveChain().Tip();
+ m_node.chainman->ActiveChain().SetTip(del->pprev);
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast()+1);
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
@@ -430,7 +430,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
+ tx.vin[0].nSequence = m_node.chainman->ActiveChain().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
@@ -438,53 +438,62 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = 0;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
+ }
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
+ tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
+ }
+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
- tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
- tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
- prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
+ prevheights[0] = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -502,14 +511,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- ::ChainActive().Tip()->nHeight++;
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ m_node.chainman->ActiveChain().Tip()->nHeight++;
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
- ::ChainActive().Tip()->nHeight--;
+ m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
m_node.mempool->clear();
diff --git a/src/test/net_peer_eviction_tests.cpp b/src/test/net_peer_eviction_tests.cpp
index 31d391bf7d..4bfd487b86 100644
--- a/src/test/net_peer_eviction_tests.cpp
+++ b/src/test/net_peer_eviction_tests.cpp
@@ -2,7 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <netaddress.h>
#include <net.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -15,11 +17,6 @@
BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
-namespace {
-constexpr int NODE_EVICTION_TEST_ROUNDS{10};
-constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
-} // namespace
-
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
{
std::vector<NodeEvictionCandidate> candidates;
@@ -36,7 +33,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
/* nKeyedNetGroup */ random_context.randrange(100),
/* prefer_evict */ random_context.randbool(),
/* m_is_local */ random_context.randbool(),
- /* m_is_onion */ random_context.randbool(),
+ /* m_network */ ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
});
}
return candidates;
@@ -94,7 +91,8 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
/* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11},
@@ -104,129 +102,359 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [num_peers](NodeEvictionCandidate& c) {
c.nTimeConnected = num_peers - c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
/* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5},
random_context));
- // Test protection of onion and localhost peers...
+ // Test protection of onion, localhost, and I2P peers...
// Expect 1/4 onion peers to be protected from eviction,
- // independently of other characteristics.
+ // if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = (c.id == 3 || c.id == 8 || c.id == 9);
+ c.m_is_local = false;
+ c.m_network = (c.id == 3 || c.id == 8 || c.id == 9) ? NET_ONION : NET_IPV4;
},
/* protected_peer_ids */ {3, 8, 9},
/* unprotected_peer_ids */ {},
random_context));
- // Expect 1/4 onion peers and 1/4 of the others to be protected
- // from eviction, sorted by longest uptime (lowest nTimeConnected).
+ // Expect 1/4 onion peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
c.m_is_local = false;
- c.m_is_onion = (c.id == 3 || c.id > 7);
+ c.m_network = (c.id == 3 || c.id > 7) ? NET_ONION : NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 3, 8, 9},
/* unprotected_peer_ids */ {4, 5, 6, 7, 10, 11},
random_context));
// Expect 1/4 localhost peers to be protected from eviction,
- // if no onion peers.
+ // if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = false;
c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {1, 9, 11},
/* unprotected_peer_ids */ {},
random_context));
// Expect 1/4 localhost peers and 1/4 of the other peers to be protected,
- // sorted by longest uptime (lowest nTimeConnected), if no onion peers.
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = false;
c.m_is_local = (c.id > 6);
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
/* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11},
random_context));
- // Combined test: expect 1/4 onion and 2 localhost peers to be protected
- // from eviction, sorted by longest uptime.
+ // Expect 1/4 I2P peers to be protected from eviction,
+ // if no onion or localhost peers.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
+ c.m_is_local = false;
+ c.m_network = (c.id == 2 || c.id == 7 || c.id == 10) ? NET_I2P : NET_IPV4;
+ },
+ /* protected_peer_ids */ {2, 7, 10},
+ /* unprotected_peer_ids */ {},
+ random_context));
+
+ // Expect 1/4 I2P peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or localhost peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 0 || c.id == 5 || c.id == 10);
- c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_is_local = false;
+ c.m_network = (c.id == 4 || c.id > 8) ? NET_I2P : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 2, 5, 9, 10},
- /* unprotected_peer_ids */ {3, 4, 6, 7, 8, 11},
+ /* protected_peer_ids */ {0, 1, 2, 4, 9, 10},
+ /* unprotected_peer_ids */ {3, 5, 6, 7, 8, 11},
random_context));
- // Combined test: expect having only 1 onion to allow allocating the
- // remaining 2 of the 1/4 to localhost peers, sorted by longest uptime.
+ // Tests with 2 networks...
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 4 to
+ // protect 1 localhost, 0 onion and 1 other peer, sorted by longest uptime;
+ // stable sort breaks tie with array order of localhost first.
BOOST_CHECK(IsProtected(
- num_peers + 4, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 15);
- c.m_is_local = (c.id > 6 && c.id < 11);
+ c.m_is_local = (c.id == 4);
+ c.m_network = (c.id == 3) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
- /* unprotected_peer_ids */ {4, 5, 6, 10, 11, 12, 13, 14},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 7 to
+ // protect 1 localhost, 0 onion, and 2 other peers (3 total), sorted by
+ // uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 8 to
+ // protect protect 1 localhost, 1 onion and 2 other peers (4 total), sorted
+ // by uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
random_context));
- // Combined test: expect 2 onions (< 1/4) to allow allocating the minimum 2
- // localhost peers, sorted by longest uptime.
+ // Combined test: expect having 3 localhost and 3 onion peers out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest
+ // uptime; stable sort breaks ties with the array order of localhost first.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 7 || c.id == 9);
- c.m_is_local = (c.id == 6 || c.id == 11);
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11);
+ c.m_network = (c.id == 7 || c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 6, 7, 9, 11},
- /* unprotected_peer_ids */ {2, 3, 4, 5, 8, 10},
+ /* protected_peer_ids */ {0, 1, 2, 6, 7, 9},
+ /* unprotected_peer_ids */ {3, 4, 5, 8, 10, 11},
random_context));
- // Combined test: when > 1/4, expect max 1/4 onion and 2 localhost peers
- // to be protected from eviction, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 1 onion peer out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 3 && c.id < 8);
- c.m_is_local = (c.id > 7);
+ c.m_is_local = (c.id > 4 && c.id < 9);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 4, 5, 6, 8, 9},
- /* unprotected_peer_ids */ {1, 2, 3, 7, 10, 11},
+ /* protected_peer_ids */ {0, 1, 2, 5, 6, 10},
+ /* unprotected_peer_ids */ {3, 4, 7, 8, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 8 peers: expect 2 onion and 2
- // localhost peers (1/4 + 2) to be protected, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 2 onion peers out of 16 to
+ // protect 2 localhost and 2 onions, plus 4 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
- 8, [](NodeEvictionCandidate& c) {
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12);
+ c.m_network = (c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 8, 9, 10},
+ /* unprotected_peer_ids */ {4, 5, 7, 11, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 5 localhost and 1 onion peer out of 16 to
+ // protect 3 localhost (recovering the unused onion slot), 1 onion, and 4
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 10);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 10, 11, 12, 13},
+ /* unprotected_peer_ids */ {4, 5, 6, 7, 8, 9, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 4 onion peers out of 16 to
+ // protect 1 localhost and 3 onions (recovering the unused localhost slot),
+ // plus 4 others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ c.m_network = (c.id > 6 && c.id < 11) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
+ /* unprotected_peer_ids */ {5, 6, 10, 11, 12, 13, 14},
+ random_context));
+
+ // Combined test: expect having 2 onion and 4 I2P out of 12 peers to protect
+ // 2 onion (prioritized for having fewer candidates) and 1 I2P, plus 3
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 1 && c.id < 5);
- c.m_is_local = (c.id > 4);
+ c.m_is_local = false;
+ if (c.id == 8 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else if (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12) {
+ c.m_network = NET_I2P;
+ } else {
+ c.m_network = NET_IPV4;
+ }
},
- /* protected_peer_ids */ {2, 3, 5, 6},
- /* unprotected_peer_ids */ {0, 1, 4, 7},
+ /* protected_peer_ids */ {0, 1, 2, 6, 8, 10},
+ /* unprotected_peer_ids */ {3, 4, 5, 7, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 6 peers: expect 1 onion peer and no
- // localhost peers (1/4 + 0) to be protected, sorted by longest uptime.
+ // Tests with 3 networks...
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 4
+ // to protect 1 I2P, 0 localhost, 0 onion and 1 other peer (2 total), sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
BOOST_CHECK(IsProtected(
- 6, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 4 || c.id == 5);
c.m_is_local = (c.id == 3);
+ if (c.id == 4) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 2) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
},
- /* protected_peer_ids */ {0, 1, 4},
- /* unprotected_peer_ids */ {2, 3, 5},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 7
+ // to protect 1 I2P, 0 localhost, 0 onion and 2 other peers (3 total) sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 4);
+ if (c.id == 6) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 5) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 8
+ // to protect 1 I2P, 1 localhost, 0 onion and 2 other peers (4 total) sorted
+ // by uptime; stable sort breaks tie with array order of I2P then localhost.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ if (c.id == 5) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 4) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
+ random_context));
+
+ // Combined test: expect having 4 localhost, 2 I2P, and 2 onion peers out of
+ // 16 to protect 1 localhost, 2 I2P, and 1 onion (4/16 total), plus 4 others
+ // for 8 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id > 11);
+ if (c.id == 7 || c.id == 11) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 9 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 7, 9, 11},
+ /* unprotected_peer_ids */ {4, 5, 8, 10, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 8 I2P and 1 onion peer out of
+ // 24 to protect 1, 4, and 1 (6 total), plus 6 others for 12/24 total,
+ // sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 12);
+ if (c.id > 14 && c.id < 23) { // 4 protected instead of usual 2
+ c.m_network = NET_I2P;
+ } else if (c.id == 23) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 15, 16, 17, 18, 23},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 14, 19, 20, 21, 22},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 3 I2P and 6 onion peers out of
+ // 24 to protect 1, 3, and 2 (6 total, I2P has fewer candidates and so gets the
+ // unused localhost slot), plus 6 others for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ if (c.id == 12 || c.id == 14 || c.id == 17) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 17) { // 4 protected instead of usual 2
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 14, 15, 17, 18, 19},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 16, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 7 I2P and 4 onion peers out of
+ // 24 to protect 1 localhost, 2 I2P, and 3 onions (6 total), plus 6 others
+ // for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 13);
+ if (c.id > 16) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 12 || c.id == 14 || c.id == 15 || c.id == 16) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 17, 18},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 16, 19, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 8 localhost, 4 I2P, and 3 onion peers out of
+ // 24 to protect 2 of each (6 total), plus 6 others for 12/24 total, sorted
+ // by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 15);
+ if (c.id > 10 && c.id < 15) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 6 && c.id < 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 7, 8, 11, 12, 16, 17},
+ /* unprotected_peer_ids */ {6, 9, 10, 13, 14, 15, 18, 19, 20, 21, 22, 23},
random_context));
}
@@ -257,91 +485,89 @@ BOOST_AUTO_TEST_CASE(peer_eviction_test)
{
FastRandomContext random_context{true};
- for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) {
- for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) {
- // Four nodes with the highest keyed netgroup values should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Eight nodes with the lowest minimum ping time should be protected
- // from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four nodes that most recently sent us novel transactions accepted
- // into our mempool should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastTXTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Up to eight non-tx-relay peers that most recently sent us novel
- // blocks should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four peers that most recently sent us novel blocks should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Combination of the previous two tests.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
-
- // Combination of all tests above.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
- candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
- candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
-
- // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
- // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
- // peers by last novel block time, and four more peers by last novel block time.
- if (number_of_nodes >= 29) {
- BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
-
- // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
- // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
- // novel block time.
- if (number_of_nodes <= 20) {
- BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
+ for (int number_of_nodes = 0; number_of_nodes < 200; ++number_of_nodes) {
+ // Four nodes with the highest keyed netgroup values should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Eight nodes with the lowest minimum ping time should be protected
+ // from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [](NodeEvictionCandidate& candidate) {
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four nodes that most recently sent us novel transactions accepted
+ // into our mempool should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastTXTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Up to eight non-tx-relay peers that most recently sent us novel
+ // blocks should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four peers that most recently sent us novel blocks should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Combination of the previous two tests.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
+
+ // Combination of all tests above.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
+ candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
+
+ // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
+ // peers by last novel block time, and four more peers by last novel block time.
+ if (number_of_nodes >= 29) {
+ BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
+ }
- // Cases left to test:
- // * "If any remaining peers are preferred for eviction consider only them. [...]"
- // * "Identify the network group with the most connections and youngest member. [...]"
+ // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
+ // novel block time.
+ if (number_of_nodes <= 20) {
+ BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
}
+
+ // Cases left to test:
+ // * "If any remaining peers are preferred for eviction consider only them. [...]"
+ // * "Identify the network group with the most connections and youngest member. [...]"
}
}
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index d47c54fd6e..3ce7ecb5f2 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// BlockUntilSyncedToCurrentChain should return false before txindex is started.
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(txindex.Start(::ChainstateActive()));
+ BOOST_REQUIRE(txindex.Start(m_node.chainman->ActiveChainstate()));
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 95ad85d0f8..ade9e210f2 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -37,7 +37,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
true /* bypass_limits */);
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 3244b58082..23195c0a26 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(tx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx),
true /* bypass_limits */);
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
@@ -63,7 +63,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
// Test 2: ... and should be rejected if spend1 is in the memory pool
@@ -71,7 +71,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -80,7 +80,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -91,7 +91,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
}
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
@@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata;
// If we add many more flags, this loop can get too expensive, but we can
@@ -126,7 +126,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputScripts should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -136,13 +136,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -205,20 +205,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData ptd_spend_tx;
- BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -227,8 +227,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
- BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChainstate().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
@@ -244,7 +244,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
// Test CHECKLOCKTIMEVERIFY
@@ -267,13 +267,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -295,13 +295,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -324,11 +324,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
{
@@ -353,7 +353,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -363,12 +363,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData txdata;
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 1204873828..f6a11bc02e 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -74,12 +74,12 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = std::make_shared<CBlock>(
- BlockAssembler{::ChainstateActive(), *Assert(node.mempool), Params()}
+ BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool), Params()}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
LOCK(cs_main);
- block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
+ block->nTime = Assert(node.chainman)->ActiveChain().Tip()->GetMedianTimePast() + 1;
block->hashMerkleRoot = BlockMerkleRoot(*block);
return block;
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 71685d437a..1b49a671bd 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -6,9 +6,11 @@
#define BITCOIN_TEST_UTIL_NET_H
#include <compat.h>
+#include <netaddress.h>
#include <net.h>
#include <util/sock.h>
+#include <array>
#include <cassert>
#include <cstring>
#include <string>
@@ -67,6 +69,16 @@ constexpr ConnectionType ALL_CONNECTION_TYPES[]{
ConnectionType::ADDR_FETCH,
};
+constexpr auto ALL_NETWORKS = std::array{
+ Network::NET_UNROUTABLE,
+ Network::NET_IPV4,
+ Network::NET_IPV6,
+ Network::NET_ONION,
+ Network::NET_I2P,
+ Network::NET_CJDNS,
+ Network::NET_INTERNAL,
+};
+
/**
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
* and ignores all writes. The data to be returned is given to the constructor and when it is
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 863c3ab565..e105e85e47 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -76,6 +76,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
m_args{}
{
+ m_node.args = &gArgs;
const std::vector<const char*> arguments = Cat(
{
"dummy",
@@ -94,7 +95,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
gArgs.ForceSetArg("-datadir", m_path_root.string());
gArgs.ClearPathCache();
{
- SetupServerArgs(m_node);
+ SetupServerArgs(*m_node.args);
std::string error;
const bool success{m_node.args->ParseParameters(arguments.size(), arguments.data(), error)};
assert(success);
@@ -145,7 +146,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
- m_node.chainman = &::g_chainman;
+ m_node.chainman = std::make_unique<ChainstateManager>();
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
constexpr int script_check_threads = 2;
@@ -167,7 +168,7 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
- m_node.chainman = nullptr;
+ m_node.chainman.reset();
pblocktree.reset();
}
@@ -180,17 +181,17 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
RegisterAllCoreRPCCommands(tableRPC);
m_node.chainman->InitializeChainstate(*m_node.mempool);
- ::ChainstateActive().InitCoinsDB(
+ m_node.chainman->ActiveChainstate().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
- assert(!::ChainstateActive().CanFlushToDisk());
- ::ChainstateActive().InitCoinsCache(1 << 23);
- assert(::ChainstateActive().CanFlushToDisk());
- if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ m_node.chainman->ActiveChainstate().InitCoinsCache(1 << 23);
+ assert(m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ if (!m_node.chainman->ActiveChainstate().LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, chainparams)) {
+ if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state, chainparams)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
@@ -240,7 +241,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(::ChainstateActive(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -299,7 +300,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
// If submit=true, add transaction to the mempool.
if (submit) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 552be0a2da..e0bc10d660 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -84,8 +84,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
- LOCK(cs_main); // For g_chainman.m_blockman.LookupBlockIndex
- GenerateCoinbaseCommitment(*pblock, g_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
+ LOCK(cs_main); // For m_node.chainman->m_blockman.LookupBlockIndex
+ GenerateCoinbaseCommitment(*pblock, m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
- initial_tip = ::ChainActive().Tip();
+ initial_tip = m_node.chainman->ActiveChain().Tip();
}
auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
RegisterSharedValidationInterface(sub);
@@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
UnregisterSharedValidationInterface(sub);
LOCK(cs_main);
- BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(sub->m_expected_tip, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
/**
@@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Run the test multiple times
for (int test_runs = 3; test_runs > 0; --test_runs) {
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// Later on split from here
const uint256 split_hash{last_mined->hashPrevBlock};
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
LOCK(cs_main);
for (const auto& tx : txs) {
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, tx, false /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
@@ -306,7 +306,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
}
LOCK(cs_main);
// We are done with the reorg, so the tip must have changed
- assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
+ assert(tip_init != m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}};
// Submit the reorg in this thread to invalidate and remove the txs from the tx pool
@@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
ProcessBlock(b);
}
// Check that the reorg was eventually successful
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// We can join the other thread, which returns when the reorg was successful
rpc_thread.join();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 4413da7ea7..c5a4bbf1b0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -513,11 +513,10 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
- CCoinsViewMemPool viewMempool(&active_chainstate.CoinsTip(), *this);
+ CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
- || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), viewMempool, tx, flags, &lp, validLP)) {
+ || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
@@ -638,10 +637,8 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t innerUsage = 0;
CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
std::list<const CTxMemPoolEntry*> waitingOnDependants;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
diff --git a/src/txmempool.h b/src/txmempool.h
index 46b89049bb..ae4b16d377 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -874,7 +874,8 @@ protected:
public:
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
- /** Add the coins created by this transaction. */
+ /** Add the coins created by this transaction. These coins are only temporarily stored in
+ * m_temp_added and cannot be flushed to the back end. Only used for package validation. */
void PackageAddTransaction(const CTransactionRef& tx);
};
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 13ccf7463e..5ea0139275 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1243,9 +1243,9 @@ void runCommand(const std::string& strCommand)
}
#endif
-#ifdef ENABLE_EXTERNAL_SIGNER
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in)
{
+#ifdef ENABLE_EXTERNAL_SIGNER
namespace bp = boost::process;
UniValue result_json;
@@ -1277,8 +1277,10 @@ UniValue RunCommandParseJSON(const std::string& str_command, const std::string&
if (!result_json.read(result)) throw std::runtime_error("Unable to parse JSON: " + result);
return result_json;
-}
+#else
+ throw std::runtime_error("Compiled without external signing support (required for external signing).");
#endif // ENABLE_EXTERNAL_SIGNER
+}
void SetupEnvironment()
{
diff --git a/src/util/system.h b/src/util/system.h
index c4317c62d0..ea9870a343 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -102,7 +102,6 @@ std::string ShellEscape(const std::string& arg);
#if HAVE_SYSTEM
void runCommand(const std::string& strCommand);
#endif
-#ifdef ENABLE_EXTERNAL_SIGNER
/**
* Execute a command which returns JSON, and parse the result.
*
@@ -111,7 +110,6 @@ void runCommand(const std::string& strCommand);
* @return parsed JSON
*/
UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in="");
-#endif // ENABLE_EXTERNAL_SIGNER
/**
* Most paths passed as configuration arguments are treated as relative to
diff --git a/src/validation.cpp b/src/validation.cpp
index 5e3d429c2e..b48e49a10b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -101,21 +101,6 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false;
}
-ChainstateManager g_chainman;
-
-CChainState& ChainstateActive()
-{
- LOCK(::cs_main);
- assert(g_chainman.m_active_chainstate);
- return *g_chainman.m_active_chainstate;
-}
-
-CChain& ChainActive()
-{
- LOCK(::cs_main);
- return ::ChainstateActive().m_chain;
-}
-
/**
* Mutex to guard access to validation specific variables, such as reading
* or changing the chainstate.
@@ -161,7 +146,6 @@ void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index));
BlockMap::const_iterator it = m_block_index.find(hash);
return it == m_block_index.end() ? nullptr : it->second;
}
@@ -170,7 +154,6 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
// Find the latest block common to locator and chain - we expect that
// locator.vHave is sorted descending by height.
for (const uint256& hash : locator.vHave) {
@@ -198,7 +181,6 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
{
AssertLockHeld(cs_main);
assert(active_chain_tip); // TODO: Make active_chain_tip a reference
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
// By convention a negative value for flags indicates that the
// current network-enforced consensus rules should be used. In
@@ -237,7 +219,6 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
}
@@ -331,7 +312,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache));
for (const COutPoint& removed : vNoSpendsRemaining)
coins_cache.Uncache(removed);
}
@@ -339,7 +319,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (active_chainstate.IsInitialBlockDownload())
return false;
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
@@ -366,7 +345,6 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
{
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -433,7 +411,6 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip));
const Coin& coinFromUTXOSet = coins_tip.AccessCoin(txin.prevout);
assert(!coinFromUTXOSet.IsSpent());
assert(coinFromUTXOSet.out == coin.out);
@@ -454,7 +431,6 @@ public:
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) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -472,8 +448,10 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
- /** Disable BIP125 RBFing; disallow all conflicts with mempool transactions. */
- const bool disallow_mempool_conflicts;
+ /** Whether we allow transactions to replace mempool transactions by BIP125 rules. If false,
+ * any transaction spending the same inputs as a transaction in the mempool is considered
+ * a conflict. */
+ const bool m_allow_bip125_replacement{true};
};
// Single transaction acceptance
@@ -482,7 +460,7 @@ public:
/**
* Multiple transaction acceptance. Transactions may or may not be interdependent,
* but must not conflict with each other. Parents must come before children if any
- * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned.
+ * dependencies exist.
*/
PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -605,7 +583,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Only accept nLockTime-using 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.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
@@ -619,6 +596,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
{
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
+ if (!args.m_allow_bip125_replacement) {
+ // Transaction conflicts with a mempool tx, but we're not allowing replacements.
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
+ }
if (!setConflicts.count(ptxConflicting->GetHash()))
{
// Allow opt-out of transaction replacement by setting
@@ -645,7 +626,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
break;
}
}
- if (fReplacementOptOut || args.disallow_mempool_conflicts) {
+ if (fReplacementOptOut) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
}
@@ -657,7 +638,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
LockPoints lp;
m_view.SetBackend(m_viewmempool);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
@@ -695,18 +675,15 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// be mined yet.
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
// backend was removed, it no longer pulls coins from the mempool.
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
const auto& params = args.m_chainparams.GetConsensus();
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
auto taproot_state = VersionBitsState(m_active_chainstate.m_chain.Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_state == ThresholdState::ACTIVE)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
@@ -733,7 +710,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
fSpendsCoinbase, nSigOpsCost, lp));
unsigned int nSize = entry->GetTxSize();
@@ -986,9 +962,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, P
// 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.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), state.ToString());
@@ -1029,7 +1003,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// - 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
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
// Store transaction in memory
@@ -1037,7 +1010,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
@@ -1080,65 +1052,15 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
{
AssertLockHeld(cs_main);
+ // These context-free package limits can be done before taking the mempool lock.
PackageValidationState package_state;
- const unsigned int package_count = txns.size();
-
- // These context-free package limits can be checked before taking the mempool lock.
- if (package_count > MAX_PACKAGE_COUNT) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
- return PackageMempoolAcceptResult(package_state, {});
- }
+ if (!CheckPackage(txns, package_state)) return PackageMempoolAcceptResult(package_state, {});
- const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
- [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
- // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
- if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
- return PackageMempoolAcceptResult(package_state, {});
- }
-
- // Construct workspaces and check package policies.
std::vector<Workspace> workspaces{};
- workspaces.reserve(package_count);
- {
- std::unordered_set<uint256, SaltedTxidHasher> later_txids;
- std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
- [](const auto& tx) { return tx->GetHash(); });
- // Require the package to be sorted in order of dependency, i.e. parents appear before children.
- // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
- // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
- // spend nonexistent coins).
- for (const auto& tx : txns) {
- for (const auto& input : tx->vin) {
- if (later_txids.find(input.prevout.hash) != later_txids.end()) {
- // The parent is a subsequent transaction in the package.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
- return PackageMempoolAcceptResult(package_state, {});
- }
- }
- later_txids.erase(tx->GetHash());
- workspaces.emplace_back(Workspace(tx));
- }
- }
+ workspaces.reserve(txns.size());
+ std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces),
+ [](const auto& tx) { return Workspace(tx); });
std::map<const uint256, const MempoolAcceptResult> results;
- {
- // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
- std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
- for (const auto& tx : txns) {
- for (const auto& input : tx->vin) {
- if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
- // This input is also present in another tx in the package.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
- return PackageMempoolAcceptResult(package_state, {});
- }
- }
- // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
- // catch duplicate inputs within a single tx. This is a more severe, consensus error,
- // and we want to report that from CheckTransaction instead.
- std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
- [](const auto& input) { return input.prevout; });
- }
- }
LOCK(m_pool.cs);
@@ -1151,10 +1073,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
return PackageMempoolAcceptResult(package_state, std::move(results));
}
// Make the coins created by this transaction available for subsequent transactions in the
- // package to spend. Since we already checked conflicts in the package and RBFs are
- // impossible, we don't need to track the coins spent. Note that this logic will need to be
- // updated if RBFs in packages are allowed in the future.
- assert(args.disallow_mempool_conflicts);
+ // package to spend. Since we already checked conflicts in the package and we don't allow
+ // replacements, we don't need to track the coins spent. Note that this logic will need to be
+ // updated if package replace-by-fee is allowed in the future.
+ assert(!args.m_allow_bip125_replacement);
m_viewmempool.PackageAddTransaction(ws.m_ptx);
}
@@ -1188,9 +1110,8 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
{
std::vector<COutPoint> coins_to_uncache;
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache,
- test_accept, /* disallow_mempool_conflicts */ false };
+ test_accept, /* m_allow_bip125_replacement */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// Remove coins that were not present in the coins cache before calling
@@ -1210,7 +1131,6 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
@@ -1225,12 +1145,10 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
std::vector<COutPoint> coins_to_uncache;
const CChainParams& chainparams = Params();
MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
- test_accept, /* disallow_mempool_conflicts */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ test_accept, /* m_allow_bip125_replacement */ false };
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
// Uncache coins pertaining to transactions that were not submitted to the mempool.
- // Ensure the cache is still within its size limits.
for (const COutPoint& hashTx : coins_to_uncache) {
active_chainstate.CoinsTip().Uncache(hashTx);
}
@@ -1366,7 +1284,6 @@ static void AlertNotify(const std::string& strMessage)
void CChainState::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
@@ -1385,7 +1302,6 @@ void CChainState::CheckForkWarningConditions()
// Called both upon regular invalid block discovery *and* InvalidateBlock
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
@@ -1446,7 +1362,6 @@ bool CScriptCheck::operator()() {
int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs)
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock());
return pindexPrev->nHeight + 1;
}
@@ -2330,14 +2245,13 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
DoWarning(warning);
} else {
@@ -2346,7 +2260,6 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
@@ -2606,7 +2519,6 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
{
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
const CBlockIndex* pindexOldTip = m_chain.Tip();
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2706,7 +2618,6 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
fInitialBlockDownload = chainstate.IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
@@ -2917,7 +2828,6 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -3248,7 +3158,6 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
{
const uint256& hash = i.second;
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
return pindex;
@@ -3281,7 +3190,6 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// BlockIndex().
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
@@ -3485,7 +3393,6 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
// Exposed wrapper for AcceptBlockHeader
bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
@@ -3576,7 +3483,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
if (blockPos.IsNull()) {
@@ -3598,7 +3504,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
AssertLockNotHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
{
CBlockIndex *pindex = nullptr;
@@ -3643,7 +3548,6 @@ bool TestBlockValidity(BlockValidationState& state,
bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip());
CCoinsViewCache viewNew(&chainstate.CoinsTip());
uint256 block_hash(block.GetHash());
@@ -3653,7 +3557,6 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman));
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
@@ -3733,7 +3636,6 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
{
BlockValidationState state;
const CChainParams& chainparams = Params();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
@@ -3887,7 +3789,6 @@ void BlockManager::Unload() {
bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree,
setBlockIndexCandidates)) {
@@ -3944,7 +3845,6 @@ bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
void CChainState::LoadMempool(const ArgsManager& args)
{
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
::LoadMempool(m_mempool, *this);
}
m_mempool.SetIsLoaded(!ShutdownRequested());
@@ -3996,7 +3896,6 @@ bool CVerifyDB::VerifyDB(
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr)
return true;
@@ -4265,7 +4164,6 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
return true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
const CBlock& block = chainparams.GenesisBlock();
FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
@@ -4329,7 +4227,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
{
LOCK(cs_main);
// detect out of order blocks, and store them for later
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
@@ -4339,11 +4236,9 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
// process in case the block isn't known yet
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
@@ -4358,13 +4253,11 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
// Activate the genesis block so normal node progress can continue
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!ActivateBestChain(state, chainparams, nullptr)) {
break;
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
// Recursively process earlier encountered successors of this block
@@ -4383,7 +4276,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
@@ -4392,7 +4284,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
range.first++;
mapBlocksUnknownParent.erase(it);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
}
}
@@ -4673,7 +4564,6 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
}
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime, false /* bypass_limits */,
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
diff --git a/src/validation.h b/src/validation.h
index 359a6c779f..5720ba8071 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -234,11 +234,13 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
-* Atomically test acceptance of a package. If the package only contains one tx, package rules still apply.
+* Atomically test acceptance of a package. If the package only contains one tx, package rules still
+* apply. Package validation does not allow BIP125 replacements, so the transaction(s) cannot spend
+* the same inputs as any transaction in the mempool.
* @param[in] txns Group of transactions which may be independent or contain
-* parent-child dependencies. The transactions must not conflict, i.e.
-* must not spend the same inputs, even if it would be a valid BIP125
-* replace-by-fee. Parents must appear before children.
+* parent-child dependencies. The transactions must not conflict
+* with each other, i.e., must not spend the same inputs. If any
+* dependencies exist, parents must appear before children.
* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction.
* If a transaction fails, validation will exit early and some results may be missing.
*/
@@ -269,9 +271,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
* @param[in] tip Chain tip to check tx sequence locks against. For example,
* the tip of the current active chain.
- * @param[in] coins_view Any CCoinsView that provides access to the relevant coins
- * for checking sequence locks. Any CCoinsView can be passed in;
- * it is assumed to be consistent with the tip.
+ * @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
+ * checking sequence locks. For example, it can be a CCoinsViewCache
+ * that isn't connected to anything but contains all the relevant
+ * coins, or a CCoinsViewMemPool that is connected to the
+ * mempool and chainstate UTXO set. In the latter case, the caller is
+ * responsible for holding the appropriate locks to ensure that
+ * calls to GetCoin() return correct coins.
* Simulates calling SequenceLocks() with data from the tip passed in.
* Optionally stores in LockPoints the resulting height and time calculated and the hash
* of the block needed for calculation or skips the calculation and uses the LockPoints
@@ -885,10 +891,6 @@ private:
CAutoFile& coins_file,
const SnapshotMetadata& metadata);
- // For access to m_active_chainstate.
- friend CChainState& ChainstateActive();
- friend CChain& ChainActive();
-
public:
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
@@ -1011,16 +1013,13 @@ public:
//! Check to see if caches are out of balance and if so, call
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-};
-
-/** DEPRECATED! Please use node.chainman instead. May only be used in validation.cpp internally */
-extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
-/** Please prefer the identical ChainstateManager::ActiveChainstate */
-CChainState& ChainstateActive();
-
-/** Please prefer the identical ChainstateManager::ActiveChain */
-CChain& ChainActive();
+ ~ChainstateManager() {
+ LOCK(::cs_main);
+ UnloadBlockIndex(/* mempool */ nullptr, *this);
+ Reset();
+ }
+};
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp
index fe2c810afa..efef1ec754 100644
--- a/src/wallet/external_signer_scriptpubkeyman.cpp
+++ b/src/wallet/external_signer_scriptpubkeyman.cpp
@@ -13,8 +13,6 @@
#include <utility>
#include <vector>
-#ifdef ENABLE_EXTERNAL_SIGNER
-
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc)
{
LOCK(cs_desc_man);
@@ -62,10 +60,10 @@ bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, c
}
// If sign is true, transaction must previously have been filled
-TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (!sign) {
- return DescriptorScriptPubKeyMan::FillPSBT(psbt, sighash_type, false, bip32derivs, n_signed);
+ return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed);
}
// Already complete if every input is now signed
@@ -84,5 +82,3 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact
FinalizePSBT(psbt); // This won't work in a multisig setup
return TransactionError::OK;
}
-
-#endif
diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h
index 1786958912..8eed947b7b 100644
--- a/src/wallet/external_signer_scriptpubkeyman.h
+++ b/src/wallet/external_signer_scriptpubkeyman.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
#define BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
-#ifdef ENABLE_EXTERNAL_SIGNER
#include <wallet/scriptpubkeyman.h>
#include <memory>
@@ -29,8 +28,6 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
};
-#endif
-
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index ee92316b89..5a832d020b 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -349,9 +349,9 @@ public:
TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) override
+ bool& complete) override
{
return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 726b13beac..4e9ba83ead 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1530,6 +1530,18 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
}
+ // Taproot descriptors cannot be imported if Taproot is not yet active.
+ // Check if this is a Taproot descriptor
+ CTxDestination dest;
+ ExtractDestination(scripts[0], dest);
+ if (std::holds_alternative<WitnessV1Taproot>(dest)) {
+ // Check if Taproot is active
+ if (!wallet.chain().isTaprootActive()) {
+ // Taproot is not active, raise an error
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
+ }
+ }
+
// If private keys are enabled, check some things.
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 534c974178..ab34af2329 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3320,7 +3320,8 @@ RPCHelpMan signrawtransactionwithwallet()
},
},
},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -3542,7 +3543,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
} else {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false /* sign */, true /* bip32derivs */);
CHECK_NONFATAL(err == TransactionError::OK);
CHECK_NONFATAL(!complete);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -4175,8 +4176,8 @@ static RPCHelpMan send()
// First fill transaction with our data without signing,
// so external signers are not asked sign more than once.
bool complete;
- pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, false, true);
- const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
+ pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, false, true);
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, true, false);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4291,7 +4292,8 @@ static RPCHelpMan walletprocesspsbt()
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
{"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
- {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
+ " \"DEFAULT\"\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 2eb9ca5c6d..c8baa0665e 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -13,7 +13,6 @@
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
-#include <external_signer.h>
#include <wallet/scriptpubkeyman.h>
#include <optional>
@@ -597,7 +596,7 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
return SigningResult::SIGNING_FAILED;
}
-TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (n_signed) {
*n_signed = 0;
@@ -626,7 +625,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
}
SignatureData sigdata;
input.FillSignatureData(sigdata);
- SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
+ SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
bool signed_one = PSBTInputSigned(input);
if (n_signed && (signed_one || !sign)) {
@@ -2083,7 +2082,7 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::OK;
}
-TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
+TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
if (n_signed) {
*n_signed = 0;
@@ -2133,7 +2132,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
}
}
- SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
+ SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
bool signed_one = PSBTInputSigned(input);
if (n_signed && (signed_one || !sign)) {
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index b8e34fbac3..3c4603608c 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -235,7 +235,7 @@ public:
/** Sign a message with the given script */
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
- virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
+ virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
virtual uint256 GetID() const { return uint256(); }
@@ -394,7 +394,7 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
@@ -605,7 +605,7 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index ce7e661b67..1cefa386b7 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
- BOOST_CHECK(spk_man->FillPSBT(psbtx, SIGHASH_ALL, true, true) != TransactionError::OK);
+ BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 6a791748b4..a0070b8dd3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -83,17 +83,17 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -112,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -163,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -180,10 +180,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Prune the older block file.
{
@@ -242,7 +242,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
- const int64_t BLOCK_TIME = ::ChainActive().Tip()->GetBlockTimeMax() + 5;
+ const int64_t BLOCK_TIME = m_node.chainman->ActiveChain().Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
@@ -265,7 +265,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
JSONRPCRequest request;
request.params.setArray();
@@ -286,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
RemoveWallet(wallet, std::nullopt);
@@ -313,9 +313,9 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
CWalletTx wtx(&wallet, m_coinbase_txns.back());
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 0);
wtx.m_confirm = confirm;
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -483,16 +483,16 @@ public:
wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
wallet->LoadWallet();
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(*wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, m_node.chainman->ActiveChain().Height());
BOOST_CHECK(result.last_failed_block.IsNull());
}
@@ -521,10 +521,10 @@ public:
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
LOCK(wallet->cs_wallet);
- wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
return it->second;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4b6630de3c..256faf2b23 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -8,6 +8,7 @@
#include <chain.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
+#include <external_signer.h>
#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
@@ -1807,7 +1808,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
}
std::map<int, std::string> input_errors;
- return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
+ return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors);
}
bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
@@ -1830,6 +1831,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
if (n_signed) {
*n_signed = 0;
}
+ const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
LOCK(cs_wallet);
// Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -1856,7 +1858,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
// Fill in information from ScriptPubKeyMans
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
int n_signed_this_spkm = 0;
- TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
+ TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
if (res != TransactionError::OK) {
return res;
}
@@ -2215,7 +2217,6 @@ void ReserveDestination::ReturnDestination()
bool CWallet::DisplayAddress(const CTxDestination& dest)
{
-#ifdef ENABLE_EXTERNAL_SIGNER
CScript scriptPubKey = GetScriptForDestination(dest);
const auto spk_man = GetScriptPubKeyMan(scriptPubKey);
if (spk_man == nullptr) {
@@ -2227,9 +2228,6 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
}
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
-#else
- return false;
-#endif
}
void CWallet::LockCoin(const COutPoint& output)
@@ -3063,12 +3061,8 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
{
if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
-#ifdef ENABLE_EXTERNAL_SIGNER
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc));
m_spk_managers[id] = std::move(spk_manager);
-#else
- throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
-#endif
} else {
auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
m_spk_managers[id] = std::move(spk_manager);
@@ -3108,7 +3102,6 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
}
}
} else {
-#ifdef ENABLE_EXTERNAL_SIGNER
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
// TODO: add account parameter
@@ -3135,9 +3128,6 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
AddActiveScriptPubKeyMan(id, t, internal);
}
}
-#else
- throw std::runtime_error(std::string(__func__) + ": Compiled without external signing support (required for external signing)");
-#endif // ENABLE_EXTERNAL_SIGNER
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index d0e26c416c..66f39edb4d 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -21,7 +21,6 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
-#include <external_signer.h>
#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h>
#include <wallet/spend.h>