aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am5
-rw-r--r--src/Makefile.crc32c.include1
-rw-r--r--src/Makefile.test.include4
-rw-r--r--src/Makefile.test_util.include1
-rw-r--r--src/addrdb.cpp12
-rw-r--r--src/addrdb.h8
-rw-r--r--src/addrman.cpp557
-rw-r--r--src/addrman.h382
-rw-r--r--src/addrman_impl.h271
-rw-r--r--src/bech32.cpp34
-rw-r--r--src/bench/addrman.cpp52
-rw-r--r--src/bench/bech32.cpp1
-rw-r--r--src/bench/bench.cpp17
-rw-r--r--src/bench/bench.h3
-rw-r--r--src/bench/bench_bitcoin.cpp64
-rw-r--r--src/bench/crypto_hash.cpp8
-rw-r--r--src/bench/nanobench.h43
-rw-r--r--src/bench/peer_eviction.cpp10
-rw-r--r--src/bench/rollingbloom.cpp16
-rw-r--r--src/bitcoin-cli.cpp30
-rw-r--r--src/bitcoin-tx.cpp3
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/bloom.cpp59
-rw-r--r--src/bloom.h20
-rw-r--r--src/chain.h24
-rw-r--r--src/chainparams.cpp49
-rw-r--r--src/chainparamsbase.cpp2
-rw-r--r--src/checkqueue.h2
-rw-r--r--src/consensus/amount.h (renamed from src/amount.h)12
-rw-r--r--src/consensus/tx_check.cpp1
-rw-r--r--src/consensus/tx_verify.cpp1
-rw-r--r--src/consensus/tx_verify.h2
-rw-r--r--src/core_io.h8
-rw-r--r--src/core_read.cpp53
-rw-r--r--src/core_write.cpp47
-rw-r--r--src/crc32c/.travis.yml18
-rw-r--r--src/crc32c/.ycm_extra_conf.py4
-rw-r--r--src/crc32c/README.md2
-rw-r--r--src/crc32c/src/crc32c_arm64_check.h10
-rw-r--r--src/dbwrapper.cpp8
-rw-r--r--src/dummywallet.cpp2
-rw-r--r--src/hash.cpp6
-rw-r--r--src/httpserver.cpp11
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/init.cpp94
-rw-r--r--src/interfaces/chain.h2
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/interfaces/wallet.h6
-rw-r--r--src/key.cpp5
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/mapport.cpp2
-rw-r--r--src/miner.cpp8
-rw-r--r--src/net.cpp45
-rw-r--r--src/net.h6
-rw-r--r--src/net_processing.cpp29
-rw-r--r--src/net_processing.h4
-rw-r--r--src/netaddress.cpp2
-rw-r--r--src/netaddress.h2
-rw-r--r--src/node/blockstorage.cpp4
-rw-r--r--src/node/coinstats.h2
-rw-r--r--src/node/context.h4
-rw-r--r--src/node/interfaces.cpp18
-rw-r--r--src/node/psbt.cpp2
-rw-r--r--src/policy/feerate.h2
-rw-r--r--src/policy/fees.h2
-rw-r--r--src/policy/rbf.cpp45
-rw-r--r--src/policy/rbf.h11
-rw-r--r--src/primitives/transaction.cpp1
-rw-r--r--src/primitives/transaction.h2
-rw-r--r--src/pubkey.cpp5
-rw-r--r--src/qt/addressbookpage.cpp8
-rw-r--r--src/qt/bitcoin.cpp22
-rw-r--r--src/qt/bitcoin.h5
-rw-r--r--src/qt/bitcoinamountfield.h2
-rw-r--r--src/qt/bitcoingui.cpp29
-rw-r--r--src/qt/bitcoingui.h3
-rw-r--r--src/qt/bitcoinunits.cpp2
-rw-r--r--src/qt/bitcoinunits.h2
-rw-r--r--src/qt/coincontroldialog.cpp18
-rw-r--r--src/qt/coincontroldialog.h6
-rw-r--r--src/qt/forms/optionsdialog.ui20
-rw-r--r--src/qt/guiutil.cpp28
-rw-r--r--src/qt/guiutil.h13
-rw-r--r--src/qt/initexecutor.cpp49
-rw-r--r--src/qt/initexecutor.h1
-rw-r--r--src/qt/intro.cpp4
-rw-r--r--src/qt/optionsdialog.cpp5
-rw-r--r--src/qt/optionsdialog.h1
-rw-r--r--src/qt/optionsmodel.cpp15
-rw-r--r--src/qt/optionsmodel.h2
-rw-r--r--src/qt/psbtoperationsdialog.cpp7
-rw-r--r--src/qt/rpcconsole.cpp23
-rw-r--r--src/qt/sendcoinsdialog.cpp13
-rw-r--r--src/qt/sendcoinsrecipient.h2
-rw-r--r--src/qt/test/test_main.cpp1
-rw-r--r--src/qt/transactionfilterproxy.h2
-rw-r--r--src/qt/transactionrecord.h2
-rw-r--r--src/qt/transactionview.cpp28
-rw-r--r--src/qt/walletcontroller.cpp86
-rw-r--r--src/qt/walletcontroller.h19
-rw-r--r--src/qt/walletframe.cpp5
-rw-r--r--src/qt/walletmodel.cpp7
-rw-r--r--src/qt/walletmodeltransaction.h2
-rw-r--r--src/qt/walletview.cpp24
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/randomenv.cpp6
-rw-r--r--src/rest.cpp12
-rw-r--r--src/rpc/blockchain.cpp105
-rw-r--r--src/rpc/blockchain.h6
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/external_signer.cpp7
-rw-r--r--src/rpc/mining.cpp20
-rw-r--r--src/rpc/misc.cpp35
-rw-r--r--src/rpc/net.cpp26
-rw-r--r--src/rpc/rawtransaction.cpp101
-rw-r--r--src/rpc/rawtransaction_util.cpp1
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/server.h1
-rw-r--r--src/rpc/util.cpp1
-rw-r--r--src/scheduler.cpp2
-rw-r--r--src/script/sigcache.cpp2
-rw-r--r--src/script/sign.cpp1
-rw-r--r--src/script/standard.cpp41
-rw-r--r--src/script/standard.h18
-rw-r--r--src/sync.cpp19
-rw-r--r--src/test/README.md26
-rw-r--r--src/test/addrman_tests.cpp211
-rw-r--r--src/test/amount_tests.cpp2
-rw-r--r--src/test/bloom_tests.cpp2
-rw-r--r--src/test/data/README.md2
-rw-r--r--src/test/fuzz/addrman.cpp222
-rw-r--r--src/test/fuzz/coins_view.cpp2
-rw-r--r--src/test/fuzz/connman.cpp2
-rw-r--r--src/test/fuzz/data_stream.cpp30
-rw-r--r--src/test/fuzz/deserialize.cpp5
-rw-r--r--src/test/fuzz/fee_rate.cpp2
-rw-r--r--src/test/fuzz/fees.cpp2
-rw-r--r--src/test/fuzz/integer.cpp2
-rw-r--r--src/test/fuzz/locale.cpp6
-rw-r--r--src/test/fuzz/muhash.cpp73
-rw-r--r--src/test/fuzz/parse_numbers.cpp7
-rw-r--r--src/test/fuzz/script.cpp62
-rw-r--r--src/test/fuzz/script_flags.cpp1
-rw-r--r--src/test/fuzz/string.cpp159
-rw-r--r--src/test/fuzz/system.cpp12
-rw-r--r--src/test/fuzz/transaction.cpp4
-rw-r--r--src/test/fuzz/util.cpp1
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp1
-rw-r--r--src/test/getarg_tests.cpp18
-rw-r--r--src/test/policy_fee_tests.cpp2
-rw-r--r--src/test/script_parse_tests.cpp55
-rw-r--r--src/test/script_standard_tests.cpp61
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/txvalidationcache_tests.cpp9
-rw-r--r--src/test/util/chainstate.h54
-rw-r--r--src/test/util/setup_common.cpp31
-rw-r--r--src/test/util/setup_common.h19
-rw-r--r--src/test/util_tests.cpp214
-rw-r--r--src/test/validation_chainstate_tests.cpp77
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp66
-rw-r--r--src/test/validation_flush_tests.cpp4
-rw-r--r--src/test/validation_tests.cpp1
-rw-r--r--src/timedata.cpp10
-rw-r--r--src/torcontrol.cpp4
-rw-r--r--src/txdb.cpp4
-rw-r--r--src/txmempool.cpp40
-rw-r--r--src/txmempool.h31
-rw-r--r--src/util/moneystr.cpp5
-rw-r--r--src/util/moneystr.h2
-rw-r--r--src/util/rbf.h6
-rw-r--r--src/util/strencodings.cpp132
-rw-r--r--src/util/strencodings.h59
-rw-r--r--src/util/syscall_sandbox.cpp918
-rw-r--r--src/util/syscall_sandbox.h57
-rw-r--r--src/util/system.cpp22
-rw-r--r--src/util/system.h2
-rw-r--r--src/validation.cpp204
-rw-r--r--src/validation.h48
-rw-r--r--src/wallet/bdb.cpp2
-rw-r--r--src/wallet/coincontrol.h29
-rw-r--r--src/wallet/coinselection.cpp33
-rw-r--r--src/wallet/coinselection.h29
-rw-r--r--src/wallet/fees.h2
-rw-r--r--src/wallet/init.cpp1
-rw-r--r--src/wallet/interfaces.cpp12
-rw-r--r--src/wallet/receive.cpp1
-rw-r--r--src/wallet/receive.h2
-rw-r--r--src/wallet/rpcdump.cpp6
-rw-r--r--src/wallet/rpcwallet.cpp264
-rw-r--r--src/wallet/salvage.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.cpp6
-rw-r--r--src/wallet/spend.cpp93
-rw-r--r--src/wallet/spend.h6
-rw-r--r--src/wallet/sqlite.cpp4
-rw-r--r--src/wallet/test/coinselector_tests.cpp21
-rw-r--r--src/wallet/test/spend_tests.cpp1
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/transaction.h31
-rw-r--r--src/wallet/wallet.cpp153
-rw-r--r--src/wallet/wallet.h34
-rw-r--r--src/wallet/walletdb.cpp39
-rw-r--r--src/wallet/walletdb.h8
-rw-r--r--src/wallet/wallettool.cpp4
-rw-r--r--src/zmq/zmqnotificationinterface.cpp2
206 files changed, 4451 insertions, 2372 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 52c8b85357..12fdc9ad75 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -117,6 +117,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
+ addrman_impl.h \
attributes.h \
banman.h \
base58.h \
@@ -261,6 +262,7 @@ BITCOIN_CORE_H = \
util/sock.h \
util/spanparsing.h \
util/string.h \
+ util/syscall_sandbox.h \
util/system.h \
util/thread.h \
util/threadnames.h \
@@ -498,9 +500,9 @@ crypto_libbitcoin_crypto_shani_a_SOURCES = crypto/sha256_shani.cpp
libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_consensus_a_SOURCES = \
- amount.h \
arith_uint256.cpp \
arith_uint256.h \
+ consensus/amount.h \
consensus/merkle.cpp \
consensus/merkle.h \
consensus/params.h \
@@ -611,6 +613,7 @@ libbitcoin_util_a_SOURCES = \
util/spanparsing.cpp \
util/strencodings.cpp \
util/string.cpp \
+ util/syscall_sandbox.cpp \
util/time.cpp \
util/tokenpipe.cpp \
$(BITCOIN_CORE_H)
diff --git a/src/Makefile.crc32c.include b/src/Makefile.crc32c.include
index 113272e65e..3cbe71792c 100644
--- a/src/Makefile.crc32c.include
+++ b/src/Makefile.crc32c.include
@@ -14,7 +14,6 @@ CRC32C_CPPFLAGS_INT += -I$(srcdir)/crc32c/include
CRC32C_CPPFLAGS_INT += -DHAVE_BUILTIN_PREFETCH=@HAVE_BUILTIN_PREFETCH@
CRC32C_CPPFLAGS_INT += -DHAVE_MM_PREFETCH=@HAVE_MM_PREFETCH@
CRC32C_CPPFLAGS_INT += -DHAVE_STRONG_GETAUXVAL=@HAVE_STRONG_GETAUXVAL@
-CRC32C_CPPFLAGS_INT += -DHAVE_WEAK_GETAUXVAL=@HAVE_WEAK_GETAUXVAL@
CRC32C_CPPFLAGS_INT += -DCRC32C_TESTS_BUILT_WITH_GLOG=0
if ENABLE_SSE42
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a85a359960..d70793ffa9 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -119,8 +119,9 @@ BITCOIN_TESTS =\
test/sanity_tests.cpp \
test/scheduler_tests.cpp \
test/script_p2sh_tests.cpp \
- test/script_tests.cpp \
+ test/script_parse_tests.cpp \
test/script_standard_tests.cpp \
+ test/script_tests.cpp \
test/scriptnum_tests.cpp \
test/serfloat_tests.cpp \
test/serialize_tests.cpp \
@@ -232,7 +233,6 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \
test/fuzz/crypto_poly1305.cpp \
test/fuzz/cuckoocache.cpp \
- test/fuzz/data_stream.cpp \
test/fuzz/decode_tx.cpp \
test/fuzz/descriptor_parse.cpp \
test/fuzz/deserialize.cpp \
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 85e50ebf70..0a3b99e7d2 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -9,6 +9,7 @@ EXTRA_LIBRARIES += \
TEST_UTIL_H = \
test/util/blockfilter.h \
+ test/util/chainstate.h \
test/util/logging.h \
test/util/mining.h \
test/util/net.h \
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 1e73750ce3..50fd09101e 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -170,21 +170,21 @@ bool CBanDB::Read(banmap_t& banSet)
return true;
}
-bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
+bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
{
const auto pathAddr = args.GetDataDirNet() / "peers.dat";
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
-void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
+void ReadFromStream(AddrMan& addr, CDataStream& ssPeers)
{
DeserializeDB(ssPeers, addr, false);
}
-std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman)
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman)
{
- auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
- addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+ auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
+ addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
int64_t nStart = GetTimeMillis();
const auto path_addr{args.GetDataDirNet() / "peers.dat"};
@@ -193,7 +193,7 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
} catch (const DbNotFoundError&) {
// Addrman can be in an inconsistent state after failure, reset it
- addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+ addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
DumpPeerAddresses(args, *addrman);
} catch (const std::exception& e) {
diff --git a/src/addrdb.h b/src/addrdb.h
index 33cc1f9204..19be4b5bb4 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -14,14 +14,14 @@
#include <vector>
class ArgsManager;
-class CAddrMan;
+class AddrMan;
class CAddress;
class CDataStream;
struct bilingual_str;
-bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
+bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr);
/** Only used by tests. */
-void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
+void ReadFromStream(AddrMan& addr, CDataStream& ssPeers);
/** Access to the banlist database (banlist.json) */
class CBanDB
@@ -48,7 +48,7 @@ public:
};
/** Returns an error string on failure */
-std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman);
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<AddrMan>& addrman);
/**
* Dump the anchor IP address database (anchors.dat)
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 772c34ae77..c364a7710b 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -4,24 +4,27 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
+#include <addrman_impl.h>
-#include <clientversion.h>
#include <hash.h>
-#include <logging.h>
#include <netaddress.h>
+#include <protocol.h>
+#include <random.h>
#include <serialize.h>
#include <streams.h>
+#include <timedata.h>
+#include <tinyformat.h>
+#include <uint256.h>
+#include <util/check.h>
#include <cmath>
#include <optional>
-#include <unordered_map>
-#include <unordered_set>
/** Over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread */
static constexpr uint32_t ADDRMAN_TRIED_BUCKETS_PER_GROUP{8};
/** Over how many buckets entries with new addresses originating from a single group are spread */
static constexpr uint32_t ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP{64};
-/** Maximum number of times an address can be added to the new table */
+/** Maximum number of times an address can occur in the new table */
static constexpr int32_t ADDRMAN_NEW_BUCKETS_PER_ADDRESS{8};
/** How old addresses can maximally be */
static constexpr int64_t ADDRMAN_HORIZON_DAYS{30};
@@ -38,7 +41,7 @@ static constexpr size_t ADDRMAN_SET_TRIED_COLLISION_SIZE{10};
/** The maximum time we'll spend trying to resolve a tried table collision, in seconds */
static constexpr int64_t ADDRMAN_TEST_WINDOW{40*60}; // 40 minutes
-int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
+int AddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool>& asmap) const
{
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
@@ -48,7 +51,7 @@ int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asma
return tried_bucket;
}
-int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool> &asmap) const
+int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool>& asmap) const
{
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
@@ -59,13 +62,13 @@ int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std:
return new_bucket;
}
-int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
+int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int nBucket) const
{
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << nBucket << GetKey()).GetCheapHash();
return hash1 % ADDRMAN_BUCKET_SIZE;
}
-bool CAddrInfo::IsTerrible(int64_t nNow) const
+bool AddrInfo::IsTerrible(int64_t nNow) const
{
if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
return false;
@@ -85,7 +88,7 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const
return false;
}
-double CAddrInfo::GetChance(int64_t nNow) const
+double AddrInfo::GetChance(int64_t nNow) const
{
double fChance = 1.0;
int64_t nSinceLastTry = std::max<int64_t>(nNow - nLastTry, 0);
@@ -100,7 +103,7 @@ double CAddrInfo::GetChance(int64_t nNow) const
return fChance;
}
-CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio)
+AddrManImpl::AddrManImpl(std::vector<bool>&& asmap, bool deterministic, int32_t consistency_check_ratio)
: insecure_rand{deterministic}
, nKey{deterministic ? uint256{1} : insecure_rand.rand256()}
, m_consistency_check_ratio{consistency_check_ratio}
@@ -118,8 +121,13 @@ CAddrMan::CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consiste
}
}
+AddrManImpl::~AddrManImpl()
+{
+ nKey.SetNull();
+}
+
template <typename Stream>
-void CAddrMan::Serialize(Stream& s_) const
+void AddrManImpl::Serialize(Stream& s_) const
{
LOCK(cs);
@@ -182,7 +190,7 @@ void CAddrMan::Serialize(Stream& s_) const
int nIds = 0;
for (const auto& entry : mapInfo) {
mapUnkIds[entry.first] = nIds;
- const CAddrInfo &info = entry.second;
+ const AddrInfo& info = entry.second;
if (info.nRefCount) {
assert(nIds != nNew); // this means nNew was wrong, oh ow
s << info;
@@ -191,7 +199,7 @@ void CAddrMan::Serialize(Stream& s_) const
}
nIds = 0;
for (const auto& entry : mapInfo) {
- const CAddrInfo &info = entry.second;
+ const AddrInfo& info = entry.second;
if (info.fInTried) {
assert(nIds != nTried); // this means nTried was wrong, oh ow
s << info;
@@ -222,7 +230,7 @@ void CAddrMan::Serialize(Stream& s_) const
}
template <typename Stream>
-void CAddrMan::Unserialize(Stream& s_)
+void AddrManImpl::Unserialize(Stream& s_)
{
LOCK(cs);
@@ -261,21 +269,21 @@ void CAddrMan::Unserialize(Stream& s_)
if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nNew < 0) {
throw std::ios_base::failure(
- strprintf("Corrupt CAddrMan serialization: nNew=%d, should be in [0, %d]",
+ strprintf("Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",
nNew,
ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
}
if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0) {
throw std::ios_base::failure(
- strprintf("Corrupt CAddrMan serialization: nTried=%d, should be in [0, %d]",
+ strprintf("Corrupt AddrMan serialization: nTried=%d, should be in [0, %d]",
nTried,
ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
}
// Deserialize entries from the new table.
for (int n = 0; n < nNew; n++) {
- CAddrInfo &info = mapInfo[n];
+ AddrInfo& info = mapInfo[n];
s >> info;
mapAddr[info] = n;
info.nRandomPos = vRandom.size();
@@ -286,7 +294,7 @@ void CAddrMan::Unserialize(Stream& s_)
// Deserialize entries from the tried table.
int nLost = 0;
for (int n = 0; n < nTried; n++) {
- CAddrInfo info;
+ AddrInfo info;
s >> info;
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
@@ -343,7 +351,7 @@ void CAddrMan::Unserialize(Stream& s_)
for (auto bucket_entry : bucket_entries) {
int bucket{bucket_entry.first};
const int entry_index{bucket_entry.second};
- CAddrInfo& info = mapInfo[entry_index];
+ AddrInfo& info = mapInfo[entry_index];
// Don't store the entry in the new bucket if it's not a valid address for our addrman
if (!info.IsValid()) continue;
@@ -385,19 +393,15 @@ void CAddrMan::Unserialize(Stream& s_)
LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
}
- Check();
+ const int check_code{ForceCheckAddrman()};
+ if (check_code != 0) {
+ throw std::ios_base::failure(strprintf(
+ "Corrupt data. Consistency check failed with code %s",
+ check_code));
+ }
}
-// explicit instantiation
-template void CAddrMan::Serialize(CHashWriter& s) const;
-template void CAddrMan::Serialize(CAutoFile& s) const;
-template void CAddrMan::Serialize(CDataStream& s) const;
-template void CAddrMan::Unserialize(CAutoFile& s);
-template void CAddrMan::Unserialize(CHashVerifier<CAutoFile>& s);
-template void CAddrMan::Unserialize(CDataStream& s);
-template void CAddrMan::Unserialize(CHashVerifier<CDataStream>& s);
-
-CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
+AddrInfo* AddrManImpl::Find(const CNetAddr& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -412,12 +416,12 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
return nullptr;
}
-CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
+AddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
AssertLockHeld(cs);
int nId = nIdCount++;
- mapInfo[nId] = CAddrInfo(addr, addrSource);
+ mapInfo[nId] = AddrInfo(addr, addrSource);
mapAddr[addr] = nId;
mapInfo[nId].nRandomPos = vRandom.size();
vRandom.push_back(nId);
@@ -426,7 +430,7 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
return &mapInfo[nId];
}
-void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
+void AddrManImpl::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
{
AssertLockHeld(cs);
@@ -450,12 +454,12 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
vRandom[nRndPos2] = nId1;
}
-void CAddrMan::Delete(int nId)
+void AddrManImpl::Delete(int nId)
{
AssertLockHeld(cs);
assert(mapInfo.count(nId) != 0);
- CAddrInfo& info = mapInfo[nId];
+ AddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
assert(info.nRefCount == 0);
@@ -466,14 +470,14 @@ void CAddrMan::Delete(int nId)
nNew--;
}
-void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
+void AddrManImpl::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];
- CAddrInfo& infoDelete = mapInfo[nIdDelete];
+ AddrInfo& infoDelete = mapInfo[nIdDelete];
assert(infoDelete.nRefCount > 0);
infoDelete.nRefCount--;
vvNew[nUBucket][nUBucketPos] = -1;
@@ -483,16 +487,19 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
}
}
-void CAddrMan::MakeTried(CAddrInfo& info, int nId)
+void AddrManImpl::MakeTried(AddrInfo& 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);
+ const int start_bucket{info.GetNewBucket(nKey, m_asmap)};
+ for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
+ const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
+ const int pos{info.GetBucketPosition(nKey, true, bucket)};
if (vvNew[bucket][pos] == nId) {
vvNew[bucket][pos] = -1;
info.nRefCount--;
+ if (info.nRefCount == 0) break;
}
}
nNew--;
@@ -508,7 +515,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
// find an item to evict
int nIdEvict = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nIdEvict) == 1);
- CAddrInfo& infoOld = mapInfo[nIdEvict];
+ AddrInfo& infoOld = mapInfo[nIdEvict];
// Remove the to-be-evicted item from the tried set.
infoOld.fInTried = false;
@@ -533,7 +540,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
info.fInTried = true;
}
-void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
+void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
AssertLockHeld(cs);
@@ -541,13 +548,13 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
nLastGood = nTime;
- CAddrInfo* pinfo = Find(addr, &nId);
+ AddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -564,22 +571,10 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
if (info.fInTried)
return;
- // find a bucket it is in now
- int nRnd = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
- int nUBucket = -1;
- for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
- int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
- int nBpos = info.GetBucketPosition(nKey, true, nB);
- if (vvNew[nB][nBpos] == nId) {
- nUBucket = nB;
- break;
- }
- }
-
- // if no bucket is found, something bad happened;
- // TODO: maybe re-add the node, but for now, just bail out
- if (nUBucket == -1)
+ // if it is not in new, something bad happened
+ if (!Assume(info.nRefCount > 0)) {
return;
+ }
// which tried bucket to move the entry to
int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
@@ -601,7 +596,7 @@ 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)
+bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
AssertLockHeld(cs);
@@ -610,7 +605,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
bool fNew = false;
int nId;
- CAddrInfo* pinfo = Find(addr, &nId);
+ AddrInfo* pinfo = Find(addr, &nId);
// Do not set a penalty for a source's self-announcement
if (addr == source) {
@@ -657,7 +652,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
if (vvNew[nUBucket][nUBucketPos] != nId) {
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (!fInsert) {
- CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
+ AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
// Overwrite the existing new table entry.
fInsert = true;
@@ -676,17 +671,17 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
return fNew;
}
-void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
+void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
{
AssertLockHeld(cs);
- CAddrInfo* pinfo = Find(addr);
+ AddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -700,15 +695,13 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
}
}
-CAddrInfo CAddrMan::Select_(bool newOnly) const
+std::pair<CAddress, int64_t> AddrManImpl::Select_(bool newOnly) const
{
AssertLockHeld(cs);
- if (vRandom.empty())
- return CAddrInfo();
+ if (vRandom.empty()) return {};
- if (newOnly && nNew == 0)
- return CAddrInfo();
+ if (newOnly && nNew == 0) return {};
// Use a 50% chance for choosing between tried and new table entries.
if (!newOnly &&
@@ -725,9 +718,10 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
int nId = vvTried[nKBucket][nKBucketPos];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
- const CAddrInfo& info{it_found->second};
- if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
- return info;
+ const AddrInfo& info{it_found->second};
+ if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
+ return {info, info.nLastTry};
+ }
fChanceFactor *= 1.2;
}
} else {
@@ -743,107 +737,16 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
int nId = vvNew[nUBucket][nUBucketPos];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
- const CAddrInfo& info{it_found->second};
- if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
- return info;
- fChanceFactor *= 1.2;
- }
- }
-}
-
-int CAddrMan::Check_() const
-{
- AssertLockHeld(cs);
-
- // Run consistency checks 1 in m_consistency_check_ratio times if enabled
- if (m_consistency_check_ratio == 0) return 0;
- if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return 0;
-
- LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
-
- std::unordered_set<int> setTried;
- std::unordered_map<int, int> mapNew;
-
- if (vRandom.size() != (size_t)(nTried + nNew))
- return -7;
-
- for (const auto& entry : mapInfo) {
- int n = entry.first;
- const CAddrInfo& info = entry.second;
- if (info.fInTried) {
- if (!info.nLastSuccess)
- return -1;
- if (info.nRefCount)
- return -2;
- setTried.insert(n);
- } else {
- if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
- return -3;
- if (!info.nRefCount)
- return -4;
- mapNew[n] = info.nRefCount;
- }
- const auto it{mapAddr.find(info)};
- if (it == mapAddr.end() || it->second != n) {
- return -5;
- }
- if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
- return -14;
- if (info.nLastTry < 0)
- return -6;
- if (info.nLastSuccess < 0)
- return -8;
- }
-
- if (setTried.size() != (size_t)nTried)
- return -9;
- if (mapNew.size() != (size_t)nNew)
- return -10;
-
- for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
- for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
- if (vvTried[n][i] != -1) {
- if (!setTried.count(vvTried[n][i]))
- return -11;
- const auto it{mapInfo.find(vvTried[n][i])};
- if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_asmap) != n) {
- return -17;
- }
- if (it->second.GetBucketPosition(nKey, false, n) != i) {
- return -18;
- }
- setTried.erase(vvTried[n][i]);
- }
- }
- }
-
- for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
- for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
- if (vvNew[n][i] != -1) {
- if (!mapNew.count(vvNew[n][i]))
- return -12;
- const auto it{mapInfo.find(vvNew[n][i])};
- if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
- return -19;
- }
- if (--mapNew[vvNew[n][i]] == 0)
- mapNew.erase(vvNew[n][i]);
+ const AddrInfo& info{it_found->second};
+ if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
+ return {info, info.nLastTry};
}
+ fChanceFactor *= 1.2;
}
}
-
- if (setTried.size())
- return -13;
- if (mapNew.size())
- return -15;
- if (nKey.IsNull())
- return -16;
-
- LogPrint(BCLog::ADDRMAN, "Addrman checks completed successfully\n");
- return 0;
}
-void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
{
AssertLockHeld(cs);
@@ -857,8 +760,9 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
// gather a list of random nodes, skipping those of low quality
const int64_t now{GetAdjustedTime()};
+ std::vector<CAddress> addresses;
for (unsigned int n = 0; n < vRandom.size(); n++) {
- if (vAddr.size() >= nNodes)
+ if (addresses.size() >= nNodes)
break;
int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
@@ -866,7 +770,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
const auto it{mapInfo.find(vRandom[n])};
assert(it != mapInfo.end());
- const CAddrInfo& ai{it->second};
+ const AddrInfo& ai{it->second};
// Filter by network (optional)
if (network != std::nullopt && ai.GetNetClass() != network) continue;
@@ -874,21 +778,23 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
// Filter for quality
if (ai.IsTerrible(now)) continue;
- vAddr.push_back(ai);
+ addresses.push_back(ai);
}
+
+ return addresses;
}
-void CAddrMan::Connected_(const CService& addr, int64_t nTime)
+void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
{
AssertLockHeld(cs);
- CAddrInfo* pinfo = Find(addr);
+ AddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -900,17 +806,17 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
info.nTime = nTime;
}
-void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
+void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
{
AssertLockHeld(cs);
- CAddrInfo* pinfo = Find(addr);
+ AddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
- CAddrInfo& info = *pinfo;
+ AddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
@@ -920,7 +826,7 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
info.nServices = nServices;
}
-void CAddrMan::ResolveCollisions_()
+void AddrManImpl::ResolveCollisions_()
{
AssertLockHeld(cs);
@@ -933,7 +839,7 @@ void CAddrMan::ResolveCollisions_()
if (mapInfo.count(id_new) != 1) {
erase_collision = true;
} else {
- CAddrInfo& info_new = mapInfo[id_new];
+ AddrInfo& info_new = mapInfo[id_new];
// Which tried bucket to move the entry to.
int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap);
@@ -944,7 +850,7 @@ void CAddrMan::ResolveCollisions_()
// Get the to-be-evicted address that is being tested
int id_old = vvTried[tried_bucket][tried_bucket_pos];
- CAddrInfo& info_old = mapInfo[id_old];
+ AddrInfo& info_old = mapInfo[id_old];
// Has successfully connected in last X hours
if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
@@ -981,11 +887,11 @@ void CAddrMan::ResolveCollisions_()
}
}
-CAddrInfo CAddrMan::SelectTriedCollision_()
+std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision_()
{
AssertLockHeld(cs);
- if (m_tried_collisions.size() == 0) return CAddrInfo();
+ if (m_tried_collisions.size() == 0) return {};
std::set<int>::iterator it = m_tried_collisions.begin();
@@ -996,16 +902,291 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
// If id_new not found in mapInfo remove it from m_tried_collisions
if (mapInfo.count(id_new) != 1) {
m_tried_collisions.erase(it);
- return CAddrInfo();
+ return {};
}
- const CAddrInfo& newInfo = mapInfo[id_new];
+ const AddrInfo& newInfo = mapInfo[id_new];
// which tried bucket to move the entry to
int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap);
int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
- int id_old = vvTried[tried_bucket][tried_bucket_pos];
+ const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
+ return {info_old, info_old.nLastTry};
+}
+
+void AddrManImpl::Check() const
+{
+ AssertLockHeld(cs);
+
+ // Run consistency checks 1 in m_consistency_check_ratio times if enabled
+ if (m_consistency_check_ratio == 0) return;
+ if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
+
+ const int err{ForceCheckAddrman()};
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ assert(false);
+ }
+}
+
+int AddrManImpl::ForceCheckAddrman() const
+{
+ AssertLockHeld(cs);
+
+ LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
+
+ std::unordered_set<int> setTried;
+ std::unordered_map<int, int> mapNew;
+
+ if (vRandom.size() != (size_t)(nTried + nNew))
+ return -7;
+
+ for (const auto& entry : mapInfo) {
+ int n = entry.first;
+ const AddrInfo& info = entry.second;
+ if (info.fInTried) {
+ if (!info.nLastSuccess)
+ return -1;
+ if (info.nRefCount)
+ return -2;
+ setTried.insert(n);
+ } else {
+ if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
+ return -3;
+ if (!info.nRefCount)
+ return -4;
+ mapNew[n] = info.nRefCount;
+ }
+ const auto it{mapAddr.find(info)};
+ if (it == mapAddr.end() || it->second != n) {
+ return -5;
+ }
+ if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
+ return -14;
+ if (info.nLastTry < 0)
+ return -6;
+ if (info.nLastSuccess < 0)
+ return -8;
+ }
- return mapInfo[id_old];
+ if (setTried.size() != (size_t)nTried)
+ return -9;
+ if (mapNew.size() != (size_t)nNew)
+ return -10;
+
+ for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
+ for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
+ if (vvTried[n][i] != -1) {
+ if (!setTried.count(vvTried[n][i]))
+ return -11;
+ const auto it{mapInfo.find(vvTried[n][i])};
+ if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_asmap) != n) {
+ return -17;
+ }
+ if (it->second.GetBucketPosition(nKey, false, n) != i) {
+ return -18;
+ }
+ setTried.erase(vvTried[n][i]);
+ }
+ }
+ }
+
+ for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
+ for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
+ if (vvNew[n][i] != -1) {
+ if (!mapNew.count(vvNew[n][i]))
+ return -12;
+ const auto it{mapInfo.find(vvNew[n][i])};
+ if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
+ return -19;
+ }
+ if (--mapNew[vvNew[n][i]] == 0)
+ mapNew.erase(vvNew[n][i]);
+ }
+ }
+ }
+
+ if (setTried.size())
+ return -13;
+ if (mapNew.size())
+ return -15;
+ if (nKey.IsNull())
+ return -16;
+
+ LogPrint(BCLog::ADDRMAN, "Addrman checks completed successfully\n");
+ return 0;
+}
+
+size_t AddrManImpl::size() const
+{
+ LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
+ return vRandom.size();
+}
+
+bool AddrManImpl::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ LOCK(cs);
+ int nAdd = 0;
+ Check();
+ for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
+ nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
+ Check();
+ if (nAdd) {
+ LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
+ }
+ return nAdd > 0;
+}
+
+void AddrManImpl::Good(const CService& addr, int64_t nTime)
+{
+ LOCK(cs);
+ Check();
+ Good_(addr, /* test_before_evict */ true, nTime);
+ Check();
+}
+
+void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, int64_t nTime)
+{
+ LOCK(cs);
+ Check();
+ Attempt_(addr, fCountFailure, nTime);
+ Check();
+}
+
+void AddrManImpl::ResolveCollisions()
+{
+ LOCK(cs);
+ Check();
+ ResolveCollisions_();
+ Check();
+}
+
+std::pair<CAddress, int64_t> AddrManImpl::SelectTriedCollision()
+{
+ LOCK(cs);
+ Check();
+ const auto ret = SelectTriedCollision_();
+ Check();
+ return ret;
+}
+
+std::pair<CAddress, int64_t> AddrManImpl::Select(bool newOnly) const
+{
+ LOCK(cs);
+ Check();
+ const auto addrRet = Select_(newOnly);
+ Check();
+ return addrRet;
+}
+
+std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+{
+ LOCK(cs);
+ Check();
+ const auto addresses = GetAddr_(max_addresses, max_pct, network);
+ Check();
+ return addresses;
+}
+
+void AddrManImpl::Connected(const CService& addr, int64_t nTime)
+{
+ LOCK(cs);
+ Check();
+ Connected_(addr, nTime);
+ Check();
+}
+
+void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
+{
+ LOCK(cs);
+ Check();
+ SetServices_(addr, nServices);
+ Check();
+}
+
+const std::vector<bool>& AddrManImpl::GetAsmap() const
+{
+ return m_asmap;
+}
+
+AddrMan::AddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio)
+ : m_impl(std::make_unique<AddrManImpl>(std::move(asmap), deterministic, consistency_check_ratio)) {}
+
+AddrMan::~AddrMan() = default;
+
+template <typename Stream>
+void AddrMan::Serialize(Stream& s_) const
+{
+ m_impl->Serialize<Stream>(s_);
+}
+
+template <typename Stream>
+void AddrMan::Unserialize(Stream& s_)
+{
+ m_impl->Unserialize<Stream>(s_);
+}
+
+// explicit instantiation
+template void AddrMan::Serialize(CHashWriter& s) const;
+template void AddrMan::Serialize(CAutoFile& s) const;
+template void AddrMan::Serialize(CDataStream& s) const;
+template void AddrMan::Unserialize(CAutoFile& s);
+template void AddrMan::Unserialize(CHashVerifier<CAutoFile>& s);
+template void AddrMan::Unserialize(CDataStream& s);
+template void AddrMan::Unserialize(CHashVerifier<CDataStream>& s);
+
+size_t AddrMan::size() const
+{
+ return m_impl->size();
+}
+
+bool AddrMan::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ return m_impl->Add(vAddr, source, nTimePenalty);
+}
+
+void AddrMan::Good(const CService& addr, int64_t nTime)
+{
+ m_impl->Good(addr, nTime);
+}
+
+void AddrMan::Attempt(const CService& addr, bool fCountFailure, int64_t nTime)
+{
+ m_impl->Attempt(addr, fCountFailure, nTime);
+}
+
+void AddrMan::ResolveCollisions()
+{
+ m_impl->ResolveCollisions();
+}
+
+std::pair<CAddress, int64_t> AddrMan::SelectTriedCollision()
+{
+ return m_impl->SelectTriedCollision();
+}
+
+std::pair<CAddress, int64_t> AddrMan::Select(bool newOnly) const
+{
+ return m_impl->Select(newOnly);
+}
+
+std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+{
+ return m_impl->GetAddr(max_addresses, max_pct, network);
+}
+
+void AddrMan::Connected(const CService& addr, int64_t nTime)
+{
+ m_impl->Connected(addr, nTime);
+}
+
+void AddrMan::SetServices(const CService& addr, ServiceFlags nServices)
+{
+ m_impl->SetServices(addr, nServices);
+}
+
+const std::vector<bool>& AddrMan::GetAsmap() const
+{
+ return m_impl->GetAsmap();
}
diff --git a/src/addrman.h b/src/addrman.h
index 0885231ebc..174ab4f811 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -6,94 +6,22 @@
#ifndef BITCOIN_ADDRMAN_H
#define BITCOIN_ADDRMAN_H
-#include <fs.h>
-#include <logging.h>
#include <netaddress.h>
#include <protocol.h>
-#include <sync.h>
+#include <streams.h>
#include <timedata.h>
#include <cstdint>
+#include <memory>
#include <optional>
-#include <set>
-#include <unordered_map>
+#include <utility>
#include <vector>
+class AddrManImpl;
+
/** Default for -checkaddrman */
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
-/**
- * Extended statistics about a CAddress
- */
-class CAddrInfo : public CAddress
-{
-public:
- //! last try whatsoever by us (memory only)
- int64_t nLastTry{0};
-
- //! last counted attempt (memory only)
- int64_t nLastCountAttempt{0};
-
-private:
- //! where knowledge about this address first came from
- CNetAddr source;
-
- //! last successful connection by us
- int64_t nLastSuccess{0};
-
- //! connection attempts since last successful attempt
- int nAttempts{0};
-
- //! reference count in new sets (memory only)
- int nRefCount{0};
-
- //! in tried set? (memory only)
- bool fInTried{false};
-
- //! position in vRandom
- mutable int nRandomPos{-1};
-
- friend class CAddrMan;
- friend class CAddrManDeterministic;
-
-public:
-
- SERIALIZE_METHODS(CAddrInfo, obj)
- {
- READWRITEAS(CAddress, obj);
- READWRITE(obj.source, obj.nLastSuccess, obj.nAttempts);
- }
-
- CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
- {
- }
-
- CAddrInfo() : CAddress(), source()
- {
- }
-
- //! Calculate in which "tried" bucket this entry belongs
- int GetTriedBucket(const uint256 &nKey, const std::vector<bool> &asmap) const;
-
- //! Calculate in which "new" bucket this entry belongs, given a certain source
- int GetNewBucket(const uint256 &nKey, const CNetAddr& src, const std::vector<bool> &asmap) const;
-
- //! Calculate in which "new" bucket this entry belongs, using its default source
- int GetNewBucket(const uint256 &nKey, const std::vector<bool> &asmap) const
- {
- return GetNewBucket(nKey, source, asmap);
- }
-
- //! Calculate in which position of a bucket to store this entry.
- int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
-
- //! Determine whether the statistics about this entry are bad enough so that it can just be deleted
- bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
-
- //! Calculate the relative chance this entry should be given when selecting nodes to connect to
- double GetChance(int64_t nNow = GetAdjustedTime()) const;
-};
-
/** Stochastic address manager
*
* Design goals:
@@ -123,298 +51,64 @@ public:
* * Several indexes are kept for high performance. Setting m_consistency_check_ratio with the -checkaddrman
* configuration option will introduce (expensive) consistency checks for the entire data structure.
*/
+class AddrMan
+{
+ const std::unique_ptr<AddrManImpl> m_impl;
-/** Total number of buckets for tried addresses */
-static constexpr int32_t ADDRMAN_TRIED_BUCKET_COUNT_LOG2{8};
-static constexpr int ADDRMAN_TRIED_BUCKET_COUNT{1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2};
-
-/** Total number of buckets for new addresses */
-static constexpr int32_t ADDRMAN_NEW_BUCKET_COUNT_LOG2{10};
-static constexpr int ADDRMAN_NEW_BUCKET_COUNT{1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2};
+public:
+ explicit AddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio);
-/** Maximum allowed number of entries in buckets for new and tried addresses */
-static constexpr int32_t ADDRMAN_BUCKET_SIZE_LOG2{6};
-static constexpr int ADDRMAN_BUCKET_SIZE{1 << ADDRMAN_BUCKET_SIZE_LOG2};
+ ~AddrMan();
-/**
- * Stochastical (IP) address manager
- */
-class CAddrMan
-{
-public:
template <typename Stream>
- void Serialize(Stream& s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+ void Serialize(Stream& s_) const;
template <typename Stream>
- void Unserialize(Stream& s_) EXCLUSIVE_LOCKS_REQUIRED(!cs);
-
- explicit CAddrMan(std::vector<bool> asmap, bool deterministic, int32_t consistency_check_ratio);
-
- ~CAddrMan()
- {
- nKey.SetNull();
- }
+ void Unserialize(Stream& s_);
//! 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();
- }
+ size_t size() const;
//! Add addresses to addrman's new table.
- bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
- EXCLUSIVE_LOCKS_REQUIRED(!cs)
- {
- LOCK(cs);
- int nAdd = 0;
- Check();
- for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
- nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
- Check();
- if (nAdd) {
- LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
- }
- return nAdd > 0;
- }
+ bool Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty = 0);
- //! Mark an entry as accessible.
- void Good(const CService &addr, int64_t nTime = GetAdjustedTime())
- EXCLUSIVE_LOCKS_REQUIRED(!cs)
- {
- LOCK(cs);
- Check();
- Good_(addr, /* test_before_evict */ true, nTime);
- Check();
- }
+ //! Mark an entry as accessible, possibly moving it from "new" to "tried".
+ void Good(const CService& addr, int64_t nTime = GetAdjustedTime());
//! 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();
- Attempt_(addr, fCountFailure, nTime);
- Check();
- }
+ void Attempt(const CService& addr, bool fCountFailure, int64_t nTime = GetAdjustedTime());
//! 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();
- ResolveCollisions_();
- Check();
- }
-
- //! Randomly select an address in tried that another address is attempting to evict.
- CAddrInfo SelectTriedCollision()
- EXCLUSIVE_LOCKS_REQUIRED(!cs)
- {
- LOCK(cs);
- Check();
- const CAddrInfo ret = SelectTriedCollision_();
- Check();
- return ret;
- }
+ void ResolveCollisions();
/**
- * Choose an address to connect to.
+ * Randomly select an address in the tried table that another address is
+ * attempting to evict.
+ *
+ * @return CAddress The record for the selected tried peer.
+ * int64_t The last time we attempted to connect to that peer.
*/
- CAddrInfo Select(bool newOnly = false) const
- EXCLUSIVE_LOCKS_REQUIRED(!cs)
- {
- LOCK(cs);
- Check();
- const CAddrInfo addrRet = Select_(newOnly);
- Check();
- return addrRet;
- }
+ std::pair<CAddress, int64_t> SelectTriedCollision();
/**
- * Return all or many randomly selected addresses, optionally by network.
+ * Choose an address to connect to.
*
- * @param[in] max_addresses Maximum number of addresses to return (0 = all).
- * @param[in] max_pct Maximum percentage of addresses to return (0 = all).
- * @param[in] network Select only addresses of this network (nullopt = all).
+ * @param[in] newOnly Whether to only select addresses from the new table.
+ * @return CAddress The record for the selected peer.
+ * int64_t The last time we attempted to connect to that peer.
*/
- std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
- EXCLUSIVE_LOCKS_REQUIRED(!cs)
- {
- LOCK(cs);
- Check();
- std::vector<CAddress> vAddr;
- 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();
- Connected_(addr, nTime);
- Check();
- }
-
- void SetServices(const CService &addr, ServiceFlags nServices)
- EXCLUSIVE_LOCKS_REQUIRED(!cs)
- {
- LOCK(cs);
- Check();
- SetServices_(addr, nServices);
- Check();
- }
-
- const std::vector<bool>& GetAsmap() const { return m_asmap; }
-
-private:
- //! A mutex to protect the inner data structures.
- mutable Mutex cs;
-
- //! Source of random numbers for randomization in inner loops
- mutable FastRandomContext insecure_rand GUARDED_BY(cs);
-
- //! secret key to randomize bucket select with
- uint256 nKey;
-
- //! Serialization versions.
- enum Format : uint8_t {
- V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
- V1_DETERMINISTIC = 1, //!< for pre-asmap files
- V2_ASMAP = 2, //!< for files including asmap version
- V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
- };
-
- //! The maximum format this software knows it can unserialize. Also, we always serialize
- //! in this format.
- //! The format (first byte in the serialized stream) can be higher than this and
- //! still this software may be able to unserialize the file - if the second byte
- //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
- static constexpr Format FILE_FORMAT = Format::V3_BIP155;
-
- //! The initial value of a field that is incremented every time an incompatible format
- //! change is made (such that old software versions would not be able to parse and
- //! understand the new file format). This is 32 because we overtook the "key size"
- //! field which was 32 historically.
- //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
- static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
-
- //! last used nId
- int nIdCount GUARDED_BY(cs){0};
-
- //! table with information about all nIds
- std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
-
- //! find an nId based on its network address
- std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
-
- //! randomly-ordered vector of all nIds
- //! This is mutable because it is unobservable outside the class, so any
- //! changes to it (even in const methods) are also unobservable.
- mutable std::vector<int> vRandom GUARDED_BY(cs);
-
- // number of "tried" entries
- int nTried GUARDED_BY(cs){0};
-
- //! list of "tried" buckets
- int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
-
- //! number of (unique) "new" entries
- int nNew GUARDED_BY(cs){0};
-
- //! list of "new" buckets
- int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
-
- //! last time Good was called (memory only). Initially set to 1 so that "never" is strictly worse.
- int64_t nLastGood GUARDED_BY(cs){1};
-
- //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
- std::set<int> m_tried_collisions;
-
- /** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
- const int32_t m_consistency_check_ratio;
-
- // Compressed IP->ASN mapping, loaded from a file when a node starts.
- // Should be always empty if no file was provided.
- // This mapping is then used for bucketing nodes in Addrman.
- //
- // If asmap is provided, nodes will be bucketed by
- // AS they belong to, in order to make impossible for a node
- // to connect to several nodes hosted in a single AS.
- // This is done in response to Erebus attack, but also to generally
- // diversify the connections every node creates,
- // especially useful when a large fraction of nodes
- // operate under a couple of cloud providers.
- //
- // If a new asmap was provided, the existing records
- // would be re-bucketed accordingly.
- const std::vector<bool> m_asmap;
-
- //! Find an entry.
- CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
- CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Swap two elements in vRandom.
- void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Move an entry from the "new" table(s) to the "tried" table
- void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Delete an entry. It must not be in tried, and have refcount 0.
- void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
- void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Mark an entry "good", possibly moving it from "new" to "tried".
- void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Add an entry to the "new" table.
- bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Mark an entry as attempted to connect.
- void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
- CAddrInfo Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
- void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Return a random to-be-evicted tried table address.
- CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
- //! Consistency check
- void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
- {
- AssertLockHeld(cs);
-
- const int err = Check_();
- if (err) {
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- assert(false);
- }
- }
-
- //! Perform consistency check. Returns an error code or zero.
- int Check_() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ std::pair<CAddress, int64_t> Select(bool newOnly = false) const;
/**
* Return all or many randomly selected addresses, optionally by network.
*
- * @param[out] vAddr Vector of randomly selected addresses from vRandom.
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
+ *
+ * @return A vector of randomly selected addresses from vRandom.
*/
- void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
/** We have successfully connected to this peer. Calling this function
* updates the CAddress's nTime, which is used in our IsTerrible()
@@ -427,13 +121,15 @@ private:
* @param[in] addr The address of the peer we were connected to
* @param[in] nTime The time that we were last connected to this peer
*/
- void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void Connected(const CService& addr, int64_t nTime = GetAdjustedTime());
//! Update an entry's service bits.
- void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void SetServices(const CService& addr, ServiceFlags nServices);
+
+ const std::vector<bool>& GetAsmap() const;
- friend class CAddrManTest;
- friend class CAddrManDeterministic;
+ friend class AddrManTest;
+ friend class AddrManDeterministic;
};
#endif // BITCOIN_ADDRMAN_H
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
new file mode 100644
index 0000000000..1dc7f25f9c
--- /dev/null
+++ b/src/addrman_impl.h
@@ -0,0 +1,271 @@
+// 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.
+
+#ifndef BITCOIN_ADDRMAN_IMPL_H
+#define BITCOIN_ADDRMAN_IMPL_H
+
+#include <logging.h>
+#include <netaddress.h>
+#include <protocol.h>
+#include <serialize.h>
+#include <sync.h>
+#include <uint256.h>
+
+#include <cstdint>
+#include <optional>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+/** Total number of buckets for tried addresses */
+static constexpr int32_t ADDRMAN_TRIED_BUCKET_COUNT_LOG2{8};
+static constexpr int ADDRMAN_TRIED_BUCKET_COUNT{1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2};
+/** Total number of buckets for new addresses */
+static constexpr int32_t ADDRMAN_NEW_BUCKET_COUNT_LOG2{10};
+static constexpr int ADDRMAN_NEW_BUCKET_COUNT{1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2};
+/** Maximum allowed number of entries in buckets for new and tried addresses */
+static constexpr int32_t ADDRMAN_BUCKET_SIZE_LOG2{6};
+static constexpr int ADDRMAN_BUCKET_SIZE{1 << ADDRMAN_BUCKET_SIZE_LOG2};
+
+/**
+ * Extended statistics about a CAddress
+ */
+class AddrInfo : public CAddress
+{
+public:
+ //! last try whatsoever by us (memory only)
+ int64_t nLastTry{0};
+
+ //! last counted attempt (memory only)
+ int64_t nLastCountAttempt{0};
+
+ //! where knowledge about this address first came from
+ CNetAddr source;
+
+ //! last successful connection by us
+ int64_t nLastSuccess{0};
+
+ //! connection attempts since last successful attempt
+ int nAttempts{0};
+
+ //! reference count in new sets (memory only)
+ int nRefCount{0};
+
+ //! in tried set? (memory only)
+ bool fInTried{false};
+
+ //! position in vRandom
+ mutable int nRandomPos{-1};
+
+ SERIALIZE_METHODS(AddrInfo, obj)
+ {
+ READWRITEAS(CAddress, obj);
+ READWRITE(obj.source, obj.nLastSuccess, obj.nAttempts);
+ }
+
+ AddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
+ {
+ }
+
+ AddrInfo() : CAddress(), source()
+ {
+ }
+
+ //! Calculate in which "tried" bucket this entry belongs
+ int GetTriedBucket(const uint256 &nKey, const std::vector<bool> &asmap) const;
+
+ //! Calculate in which "new" bucket this entry belongs, given a certain source
+ int GetNewBucket(const uint256 &nKey, const CNetAddr& src, const std::vector<bool> &asmap) const;
+
+ //! Calculate in which "new" bucket this entry belongs, using its default source
+ int GetNewBucket(const uint256 &nKey, const std::vector<bool> &asmap) const
+ {
+ return GetNewBucket(nKey, source, asmap);
+ }
+
+ //! Calculate in which position of a bucket to store this entry.
+ int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
+
+ //! Determine whether the statistics about this entry are bad enough so that it can just be deleted
+ bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
+
+ //! Calculate the relative chance this entry should be given when selecting nodes to connect to
+ double GetChance(int64_t nNow = GetAdjustedTime()) const;
+};
+
+class AddrManImpl
+{
+public:
+ AddrManImpl(std::vector<bool>&& asmap, bool deterministic, int32_t consistency_check_ratio);
+
+ ~AddrManImpl();
+
+ template <typename Stream>
+ void Serialize(Stream& s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ template <typename Stream>
+ void Unserialize(Stream& s_) EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ size_t size() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ bool Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void Good(const CService& addr, int64_t nTime)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void Attempt(const CService& addr, bool fCountFailure, int64_t nTime)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ std::pair<CAddress, int64_t> SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ std::pair<CAddress, int64_t> Select(bool newOnly) const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void Connected(const CService& addr, int64_t nTime)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ void SetServices(const CService& addr, ServiceFlags nServices)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs);
+
+ const std::vector<bool>& GetAsmap() const;
+
+ friend class AddrManTest;
+ friend class AddrManDeterministic;
+
+private:
+ //! A mutex to protect the inner data structures.
+ mutable Mutex cs;
+
+ //! Source of random numbers for randomization in inner loops
+ mutable FastRandomContext insecure_rand GUARDED_BY(cs);
+
+ //! secret key to randomize bucket select with
+ uint256 nKey;
+
+ //! Serialization versions.
+ enum Format : uint8_t {
+ V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
+ V1_DETERMINISTIC = 1, //!< for pre-asmap files
+ V2_ASMAP = 2, //!< for files including asmap version
+ V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
+ };
+
+ //! The maximum format this software knows it can unserialize. Also, we always serialize
+ //! in this format.
+ //! The format (first byte in the serialized stream) can be higher than this and
+ //! still this software may be able to unserialize the file - if the second byte
+ //! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
+ static constexpr Format FILE_FORMAT = Format::V3_BIP155;
+
+ //! The initial value of a field that is incremented every time an incompatible format
+ //! change is made (such that old software versions would not be able to parse and
+ //! understand the new file format). This is 32 because we overtook the "key size"
+ //! field which was 32 historically.
+ //! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
+ static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
+
+ //! last used nId
+ int nIdCount GUARDED_BY(cs){0};
+
+ //! table with information about all nIds
+ std::unordered_map<int, AddrInfo> mapInfo GUARDED_BY(cs);
+
+ //! find an nId based on its network address
+ std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
+
+ //! randomly-ordered vector of all nIds
+ //! This is mutable because it is unobservable outside the class, so any
+ //! changes to it (even in const methods) are also unobservable.
+ mutable std::vector<int> vRandom GUARDED_BY(cs);
+
+ // number of "tried" entries
+ int nTried GUARDED_BY(cs){0};
+
+ //! list of "tried" buckets
+ int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
+
+ //! number of (unique) "new" entries
+ int nNew GUARDED_BY(cs){0};
+
+ //! list of "new" buckets
+ int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
+
+ //! last time Good was called (memory only). Initially set to 1 so that "never" is strictly worse.
+ int64_t nLastGood GUARDED_BY(cs){1};
+
+ //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
+ std::set<int> m_tried_collisions;
+
+ /** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
+ const int32_t m_consistency_check_ratio;
+
+ // Compressed IP->ASN mapping, loaded from a file when a node starts.
+ // Should be always empty if no file was provided.
+ // This mapping is then used for bucketing nodes in Addrman.
+ //
+ // If asmap is provided, nodes will be bucketed by
+ // AS they belong to, in order to make impossible for a node
+ // to connect to several nodes hosted in a single AS.
+ // This is done in response to Erebus attack, but also to generally
+ // diversify the connections every node creates,
+ // especially useful when a large fraction of nodes
+ // operate under a couple of cloud providers.
+ //
+ // If a new asmap was provided, the existing records
+ // would be re-bucketed accordingly.
+ const std::vector<bool> m_asmap;
+
+ //! Find an entry.
+ AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
+ AddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Swap two elements in vRandom.
+ void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Delete an entry. It must not be in tried, and have refcount 0.
+ void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Clear a position in a "new" table. This is the only place where entries are actually deleted.
+ void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Move an entry from the "new" table(s) to the "tried" table
+ void MakeTried(AddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ void Good_(const CService& addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ bool Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ void Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ std::pair<CAddress, int64_t> Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ void SetServices_(const CService& addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ std::pair<CAddress, int64_t> SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Consistency check, taking into account m_consistency_check_ratio. Will std::abort if an inconsistency is detected.
+ void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ //! Perform consistency check, regardless of m_consistency_check_ratio.
+ //! @returns an error code or zero.
+ int ForceCheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+};
+
+#endif // BITCOIN_ADDRMAN_IMPL_H
diff --git a/src/bech32.cpp b/src/bech32.cpp
index 288b14e023..9da2488ef2 100644
--- a/src/bech32.cpp
+++ b/src/bech32.cpp
@@ -66,6 +66,26 @@ uint32_t PolyMod(const data& v)
// the above example, `c` initially corresponds to 1 mod g(x), and after processing 2 inputs of
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
// for `c`.
+
+ // The following Sage code constructs the generator used:
+ //
+ // B = GF(2) # Binary field
+ // BP.<b> = B[] # Polynomials over the binary field
+ // F_mod = b**5 + b**3 + 1
+ // F.<f> = GF(32, modulus=F_mod, repr='int') # GF(32) definition
+ // FP.<x> = F[] # Polynomials over GF(32)
+ // E_mod = x**2 + F.fetch_int(9)*x + F.fetch_int(23)
+ // E.<e> = F.extension(E_mod) # GF(1024) extension field definition
+ // for p in divisors(E.order() - 1): # Verify e has order 1023.
+ // assert((e**p == 1) == (p % 1023 == 0))
+ // G = lcm([(e**i).minpoly() for i in range(997,1000)])
+ // print(G) # Print out the generator
+ //
+ // It demonstrates that g(x) is the least common multiple of the minimal polynomials
+ // of 3 consecutive powers (997,998,999) of a primitive element (e) of GF(1024).
+ // That guarantees it is, in fact, the generator of a primitive BCH code with cycle
+ // length 1023 and distance 4. See https://en.wikipedia.org/wiki/BCH_code for more details.
+
uint32_t c = 1;
for (const auto v_i : v) {
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
@@ -88,12 +108,21 @@ uint32_t PolyMod(const data& v)
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
c = ((c & 0x1ffffff) << 5) ^ v_i;
- // Finally, for each set bit n in c0, conditionally add {2^n}k(x):
+ // Finally, for each set bit n in c0, conditionally add {2^n}k(x). These constants can be
+ // computed using the following Sage code (continuing the code above):
+ //
+ // for i in [1,2,4,8,16]: # Print out {1,2,4,8,16}*(g(x) mod x^6), packed in hex integers.
+ // v = 0
+ // for coef in reversed((F.fetch_int(i)*(G % x**6)).coefficients(sparse=True)):
+ // v = v*32 + coef.integer_representation()
+ // print("0x%x" % v)
+ //
if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
+
}
return c;
}
@@ -125,7 +154,8 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
// list of values would result in a new valid list. For that reason, Bech32 requires the
- // resulting checksum to be 1 instead. In Bech32m, this constant was amended.
+ // resulting checksum to be 1 instead. In Bech32m, this constant was amended. See
+ // https://gist.github.com/sipa/14c248c288c3880a3b191f978a34508e for details.
const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values));
if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32;
if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M;
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index e5dd571a4c..d6834a239b 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -53,14 +53,14 @@ static void CreateAddresses()
}
}
-static void AddAddressesToAddrMan(CAddrMan& addrman)
+static void AddAddressesToAddrMan(AddrMan& addrman)
{
for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
addrman.Add(g_addresses[source_i], g_sources[source_i]);
}
}
-static void FillAddrMan(CAddrMan& addrman)
+static void FillAddrMan(AddrMan& addrman)
{
CreateAddresses();
@@ -74,26 +74,26 @@ static void AddrManAdd(benchmark::Bench& bench)
CreateAddresses();
bench.run([&] {
- CAddrMan addrman{/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0};
+ AddrMan addrman{/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0};
AddAddressesToAddrMan(addrman);
});
}
static void AddrManSelect(benchmark::Bench& bench)
{
- CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
FillAddrMan(addrman);
bench.run([&] {
const auto& address = addrman.Select();
- assert(address.GetPort() > 0);
+ assert(address.first.GetPort() > 0);
});
}
static void AddrManGetAddr(benchmark::Bench& bench)
{
- CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
FillAddrMan(addrman);
@@ -103,41 +103,33 @@ static void AddrManGetAddr(benchmark::Bench& bench)
});
}
-static void AddrManGood(benchmark::Bench& bench)
+static void AddrManAddThenGood(benchmark::Bench& bench)
{
- /* Create many CAddrMan objects - one to be modified at each loop iteration.
- * This is necessary because the CAddrMan::Good() method modifies the
- * object, affecting the timing of subsequent calls to the same method and
- * we want to do the same amount of work in every loop iteration. */
-
- bench.epochs(5).epochIterations(1);
- const uint64_t addrman_count{bench.epochs() * bench.epochIterations()};
- Assert(addrman_count == 5U);
-
- std::vector<std::unique_ptr<CAddrMan>> addrmans(addrman_count);
- for (size_t i{0}; i < addrman_count; ++i) {
- addrmans[i] = std::make_unique<CAddrMan>(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- FillAddrMan(*addrmans[i]);
- }
-
- auto markSomeAsGood = [](CAddrMan& addrman) {
+ auto markSomeAsGood = [](AddrMan& addrman) {
for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
- if (addr_i % 32 == 0) {
- addrman.Good(g_addresses[source_i][addr_i]);
- }
+ addrman.Good(g_addresses[source_i][addr_i]);
}
}
};
- uint64_t i = 0;
+ CreateAddresses();
+
bench.run([&] {
- markSomeAsGood(*addrmans.at(i));
- ++i;
+ // To make the benchmark independent of the number of evaluations, we always prepare a new addrman.
+ // This is necessary because AddrMan::Good() method modifies the object, affecting the timing of subsequent calls
+ // to the same method and we want to do the same amount of work in every loop iteration.
+ //
+ // This has some overhead (exactly the result of AddrManAdd benchmark), but that overhead is constant so improvements in
+ // AddrMan::Good() will still be noticeable.
+ AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddAddressesToAddrMan(addrman);
+
+ markSomeAsGood(addrman);
});
}
BENCHMARK(AddrManAdd);
BENCHMARK(AddrManSelect);
BENCHMARK(AddrManGetAddr);
-BENCHMARK(AddrManGood);
+BENCHMARK(AddrManAddThenGood);
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index 8e10862a37..bc3685818e 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <bench/nanobench.h>
#include <bech32.h>
#include <util/strencodings.h>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 012057e792..030bc43396 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -4,11 +4,18 @@
#include <bench/bench.h>
-#include <chainparams.h>
#include <test/util/setup_common.h>
-#include <validation.h>
+#include <chrono>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <map>
#include <regex>
+#include <string>
+#include <vector>
+
+using namespace std::chrono_literals;
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
@@ -61,6 +68,12 @@ void benchmark::BenchRunner::RunAll(const Args& args)
Bench bench;
bench.name(p.first);
+ if (args.min_time > 0ms) {
+ // convert to nanos before dividing to reduce rounding errors
+ std::chrono::nanoseconds min_time_ns = args.min_time;
+ bench.minEpochTime(min_time_ns / bench.epochs());
+ }
+
if (args.asymptote.empty()) {
p.second(bench);
} else {
diff --git a/src/bench/bench.h b/src/bench/bench.h
index c4fcd80e33..b0e4006ee3 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -41,11 +41,12 @@ using ankerl::nanobench::Bench;
typedef std::function<void(Bench&)> BenchFunction;
struct Args {
- std::string regex_filter;
bool is_list_only;
+ std::chrono::milliseconds min_time;
std::vector<double> asymptote;
std::string output_csv;
std::string output_json;
+ std::string regex_filter;
};
class BenchRunner
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index aab777cac1..0b43ea1fd5 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -4,21 +4,28 @@
#include <bench/bench.h>
+#include <clientversion.h>
#include <crypto/sha256.h>
#include <util/strencodings.h>
#include <util/system.h>
-#include <memory>
+#include <chrono>
+#include <cstdint>
+#include <iostream>
+#include <sstream>
+#include <vector>
static const char* DEFAULT_BENCH_FILTER = ".*";
+static constexpr int64_t DEFAULT_MIN_TIME_MS{10};
static void SetupBenchArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
- argsman.AddArg("-asymptote=n1,n2,n3,...", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
@@ -48,17 +55,62 @@ int main(int argc, char** argv)
}
if (HelpRequested(argsman)) {
- std::cout << argsman.GetHelpMessage();
+ std::cout << "Usage: bench_bitcoin [options]\n"
+ "\n"
+ << argsman.GetHelpMessage()
+ << "Description:\n"
+ "\n"
+ " bench_bitcoin executes microbenchmarks. The quality of the benchmark results\n"
+ " highly depend on the stability of the machine. It can sometimes be difficult\n"
+ " to get stable, repeatable results, so here are a few tips:\n"
+ "\n"
+ " * Use pyperf [1] to disable frequency scaling, turbo boost etc. For best\n"
+ " results, use CPU pinning and CPU isolation (see [2]).\n"
+ "\n"
+ " * Each call of run() should do exactly the same work. E.g. inserting into\n"
+ " a std::vector doesn't do that as it will reallocate on certain calls. Make\n"
+ " sure each run has exactly the same preconditions.\n"
+ "\n"
+ " * If results are still not reliable, increase runtime with e.g.\n"
+ " -min_time=5000 to let a benchmark run for at least 5 seconds.\n"
+ "\n"
+ " * bench_bitcoin uses nanobench [3] for which there is extensive\n"
+ " documentation available online.\n"
+ "\n"
+ "Environment Variables:\n"
+ "\n"
+ " To attach a profiler you can run a benchmark in endless mode. This can be\n"
+ " done with the environment variable NANOBENCH_ENDLESS. E.g. like so:\n"
+ "\n"
+ " NANOBENCH_ENDLESS=MuHash ./bench_bitcoin -filter=MuHash\n"
+ "\n"
+ " In rare cases it can be useful to suppress stability warnings. This can be\n"
+ " done with the environment variable NANOBENCH_SUPPRESS_WARNINGS, e.g:\n"
+ "\n"
+ " NANOBENCH_SUPPRESS_WARNINGS=1 ./bench_bitcoin\n"
+ "\n"
+ "Notes:\n"
+ "\n"
+ " 1. pyperf\n"
+ " https://github.com/psf/pyperf\n"
+ "\n"
+ " 2. CPU pinning & isolation\n"
+ " https://pyperf.readthedocs.io/en/latest/system.html\n"
+ "\n"
+ " 3. nanobench\n"
+ " https://github.com/martinus/nanobench\n"
+ "\n";
return EXIT_SUCCESS;
}
benchmark::Args args;
- args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
- args.is_list_only = argsman.GetBoolArg("-list", false);
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
+ args.is_list_only = argsman.GetBoolArg("-list", false);
+ args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min_time", DEFAULT_MIN_TIME_MS));
args.output_csv = argsman.GetArg("-output_csv", "");
args.output_json = argsman.GetArg("-output_json", "");
+ args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
benchmark::BenchRunner::RunAll(args);
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 30fe11be6b..d36e504bfc 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -110,9 +110,9 @@ static void MuHash(benchmark::Bench& bench)
{
MuHash3072 acc;
unsigned char key[32] = {0};
- int i = 0;
+ uint32_t i = 0;
bench.run([&] {
- key[0] = ++i;
+ key[0] = ++i & 0xFF;
acc *= MuHash3072(key);
});
}
@@ -134,10 +134,6 @@ static void MuHashDiv(benchmark::Bench& bench)
FastRandomContext rng(true);
MuHash3072 muhash{rng.randbytes(32)};
- for (size_t i = 0; i < bench.epochIterations(); ++i) {
- acc *= muhash;
- }
-
bench.run([&] {
acc /= muhash;
});
diff --git a/src/bench/nanobench.h b/src/bench/nanobench.h
index 030d6ebf6a..27df08fb69 100644
--- a/src/bench/nanobench.h
+++ b/src/bench/nanobench.h
@@ -33,7 +33,7 @@
// see https://semver.org/
#define ANKERL_NANOBENCH_VERSION_MAJOR 4 // incompatible API changes
#define ANKERL_NANOBENCH_VERSION_MINOR 3 // backwards-compatible changes
-#define ANKERL_NANOBENCH_VERSION_PATCH 4 // backwards-compatible bug fixes
+#define ANKERL_NANOBENCH_VERSION_PATCH 6 // backwards-compatible bug fixes
///////////////////////////////////////////////////////////////////////////////////////////////////
// public facing api - as minimal as possible
@@ -88,13 +88,15 @@
} while (0)
#endif
-#if defined(__linux__) && defined(PERF_EVENT_IOC_ID) && defined(PERF_COUNT_HW_REF_CPU_CYCLES) && defined(PERF_FLAG_FD_CLOEXEC) && \
- !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
-// only enable perf counters on kernel 3.14 which seems to have all the necessary defines. The three PERF_... defines are not in
-// kernel 2.6.32 (all others are).
-# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
-#else
-# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
+#define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
+#if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
+# include <linux/version.h>
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+// PERF_COUNT_HW_REF_CPU_CYCLES only available since kernel 3.3
+// PERF_FLAG_FD_CLOEXEC since kernel 3.14
+# undef ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS
+# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
+# endif
#endif
#if defined(__clang__)
@@ -2210,20 +2212,20 @@ struct IterationLogic::Impl {
columns.emplace_back(10, 1, "err%", "%", rErrorMedian * 100.0);
double rInsMedian = -1.0;
- if (mResult.has(Result::Measure::instructions)) {
+ if (mBench.performanceCounters() && mResult.has(Result::Measure::instructions)) {
rInsMedian = mResult.median(Result::Measure::instructions);
columns.emplace_back(18, 2, "ins/" + mBench.unit(), "", rInsMedian / mBench.batch());
}
double rCycMedian = -1.0;
- if (mResult.has(Result::Measure::cpucycles)) {
+ if (mBench.performanceCounters() && mResult.has(Result::Measure::cpucycles)) {
rCycMedian = mResult.median(Result::Measure::cpucycles);
columns.emplace_back(18, 2, "cyc/" + mBench.unit(), "", rCycMedian / mBench.batch());
}
if (rInsMedian > 0.0 && rCycMedian > 0.0) {
columns.emplace_back(9, 3, "IPC", "", rCycMedian <= 0.0 ? 0.0 : rInsMedian / rCycMedian);
}
- if (mResult.has(Result::Measure::branchinstructions)) {
+ if (mBench.performanceCounters() && mResult.has(Result::Measure::branchinstructions)) {
double rBraMedian = mResult.median(Result::Measure::branchinstructions);
columns.emplace_back(17, 2, "bra/" + mBench.unit(), "", rBraMedian / mBench.batch());
if (mResult.has(Result::Measure::branchmisses)) {
@@ -2402,6 +2404,14 @@ public:
return (a + divisor / 2) / divisor;
}
+ ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
+ static inline uint32_t mix(uint32_t x) noexcept {
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return x;
+ }
+
template <typename Op>
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
void calibrate(Op&& op) {
@@ -2441,15 +2451,10 @@ public:
uint64_t const numIters = 100000U + (std::random_device{}() & 3);
uint64_t n = numIters;
uint32_t x = 1234567;
- auto fn = [&]() {
- x ^= x << 13;
- x ^= x >> 17;
- x ^= x << 5;
- };
beginMeasure();
while (n-- > 0) {
- fn();
+ x = mix(x);
}
endMeasure();
detail::doNotOptimizeAway(x);
@@ -2459,8 +2464,8 @@ public:
beginMeasure();
while (n-- > 0) {
// we now run *twice* so we can easily calculate the overhead
- fn();
- fn();
+ x = mix(x);
+ x = mix(x);
}
endMeasure();
detail::doNotOptimizeAway(x);
diff --git a/src/bench/peer_eviction.cpp b/src/bench/peer_eviction.cpp
index 46fd9d999e..8429f18613 100644
--- a/src/bench/peer_eviction.cpp
+++ b/src/bench/peer_eviction.cpp
@@ -20,19 +20,17 @@ static void EvictionProtectionCommon(
{
using Candidates = std::vector<NodeEvictionCandidate>;
FastRandomContext random_context{true};
- bench.warmup(100).epochIterations(1100);
Candidates candidates{GetRandomNodeEvictionCandidates(num_candidates, random_context)};
for (auto& c : candidates) {
candidate_setup_fn(c);
}
- std::vector<Candidates> copies{
- static_cast<size_t>(bench.epochs() * bench.epochIterations()), candidates};
- size_t i{0};
+
bench.run([&] {
- ProtectEvictionCandidatesByRatio(copies.at(i));
- ++i;
+ // creating a copy has an overhead of about 3%, so it does not influence the benchmark results much.
+ auto copy = candidates;
+ ProtectEvictionCandidatesByRatio(copy);
});
}
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 997ab56549..28167767db 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -13,16 +13,16 @@ static void RollingBloom(benchmark::Bench& bench)
uint32_t count = 0;
bench.run([&] {
count++;
- data[0] = count;
- data[1] = count >> 8;
- data[2] = count >> 16;
- data[3] = count >> 24;
+ data[0] = count & 0xFF;
+ data[1] = (count >> 8) & 0xFF;
+ data[2] = (count >> 16) & 0xFF;
+ data[3] = (count >> 24) & 0xFF;
filter.insert(data);
- data[0] = count >> 24;
- data[1] = count >> 16;
- data[2] = count >> 8;
- data[3] = count;
+ data[0] = (count >> 24) & 0xFF;
+ data[1] = (count >> 16) & 0xFF;
+ data[2] = (count >> 8) & 0xFF;
+ data[3] = count & 0xFF;
filter.contains(data);
});
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 4ccd1f4fee..3c22ee0f67 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -337,7 +337,7 @@ public:
connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
result.pushKV("connections", connections);
- result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
+ result.pushKV("networks", batch[ID_NETWORKINFO]["result"]["networks"]);
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO]["result"].isNull()) {
@@ -702,7 +702,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
// 3. default port for chain
uint16_t port{BaseParams().RPCPort()};
SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
- port = static_cast<uint16_t>(gArgs.GetArg("-rpcport", port));
+ port = static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", port));
// Obtain event base
raii_event_base base = obtain_event_base();
@@ -712,7 +712,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
// Set connection timeout
{
- const int timeout = gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
+ const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
if (timeout > 0) {
evhttp_connection_set_timeout(evcon.get(), timeout);
} else {
@@ -822,7 +822,7 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
UniValue response(UniValue::VOBJ);
// Execute and handle connection failures with -rpcwait.
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
- const int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
+ const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
const auto deadline{GetTime<std::chrono::microseconds>() + 1s * timeout};
do {
@@ -986,8 +986,26 @@ static void ParseGetInfoResult(UniValue& result)
RESET);
result_string += strprintf("Version: %s\n", result["version"].getValStr());
result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
- const std::string proxy = result["proxy"].getValStr();
- result_string += strprintf("Proxy: %s\n", proxy.empty() ? "N/A" : proxy);
+
+ // proxies
+ std::map<std::string, std::vector<std::string>> proxy_networks;
+ std::vector<std::string> ordered_proxies;
+
+ for (const UniValue& network : result["networks"].getValues()) {
+ const std::string proxy = network["proxy"].getValStr();
+ if (proxy.empty()) continue;
+ // Add proxy to ordered_proxy if has not been processed
+ if (proxy_networks.find(proxy) == proxy_networks.end()) ordered_proxies.push_back(proxy);
+
+ proxy_networks[proxy].push_back(network["name"].getValStr());
+ }
+
+ std::vector<std::string> formatted_proxies;
+ for (const std::string& proxy : ordered_proxies) {
+ formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", ")));
+ }
+ result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", "));
+
result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
if (!result["has_wallet"].isNull()) {
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 58c51bd8e0..fc3bc6aa71 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -8,6 +8,7 @@
#include <clientversion.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <core_io.h>
#include <key_io.h>
@@ -727,7 +728,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
static void OutputTxJSON(const CTransaction& tx)
{
UniValue entry(UniValue::VOBJ);
- TxToUniv(tx, uint256(), /* include_addresses */ false, entry);
+ TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
tfm::format(std::cout, "%s\n", jsonOutput);
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 654679af27..25ec2809e9 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -19,6 +19,7 @@
#include <shutdown.h>
#include <util/check.h>
#include <util/strencodings.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/tokenpipe.h>
@@ -238,6 +239,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
daemon_ep.Close();
}
#endif
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF);
if (fRet) {
WaitForShutdown();
}
diff --git a/src/bloom.cpp b/src/bloom.cpp
index d0128a26d7..15e06389de 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -4,20 +4,22 @@
#include <bloom.h>
-#include <primitives/transaction.h>
#include <hash.h>
+#include <primitives/transaction.h>
+#include <random.h>
#include <script/script.h>
#include <script/standard.h>
-#include <random.h>
+#include <span.h>
#include <streams.h>
-#include <math.h>
-#include <stdlib.h>
-
#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <vector>
-#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
-#define LN2 0.6931471805599453094172321214581765680755001343602552
+static constexpr double LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
+static constexpr double LN2 = 0.6931471805599453094172321214581765680755001343602552;
CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweakIn, unsigned char nFlagsIn) :
/**
@@ -37,13 +39,13 @@ CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, c
{
}
-inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
+inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, Span<const unsigned char> vDataToHash) const
{
// 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}
-void CBloomFilter::insert(const std::vector<unsigned char>& vKey)
+void CBloomFilter::insert(Span<const unsigned char> vKey)
{
if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700)
return;
@@ -59,17 +61,10 @@ void CBloomFilter::insert(const COutPoint& outpoint)
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- std::vector<unsigned char> data(stream.begin(), stream.end());
- insert(data);
+ insert(stream);
}
-void CBloomFilter::insert(const uint256& hash)
-{
- std::vector<unsigned char> data(hash.begin(), hash.end());
- insert(data);
-}
-
-bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const
+bool CBloomFilter::contains(Span<const unsigned char> vKey) const
{
if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700)
return true;
@@ -87,14 +82,7 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- std::vector<unsigned char> data(stream.begin(), stream.end());
- return contains(data);
-}
-
-bool CBloomFilter::contains(const uint256& hash) const
-{
- std::vector<unsigned char> data(hash.begin(), hash.end());
- return contains(data);
+ return contains(MakeUCharSpan(stream));
}
bool CBloomFilter::IsWithinSizeConstraints() const
@@ -198,7 +186,8 @@ CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const dou
}
/* Similar to CBloomFilter::Hash */
-static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, const std::vector<unsigned char>& vDataToHash) {
+static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, Span<const unsigned char> vDataToHash)
+{
return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash);
}
@@ -210,7 +199,7 @@ static inline uint32_t FastMod(uint32_t x, size_t n) {
return ((uint64_t)x * (uint64_t)n) >> 32;
}
-void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
+void CRollingBloomFilter::insert(Span<const unsigned char> vKey)
{
if (nEntriesThisGeneration == nEntriesPerGeneration) {
nEntriesThisGeneration = 0;
@@ -241,13 +230,7 @@ void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
}
}
-void CRollingBloomFilter::insert(const uint256& hash)
-{
- std::vector<unsigned char> vData(hash.begin(), hash.end());
- insert(vData);
-}
-
-bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
+bool CRollingBloomFilter::contains(Span<const unsigned char> vKey) const
{
for (int n = 0; n < nHashFuncs; n++) {
uint32_t h = RollingBloomHash(n, nTweak, vKey);
@@ -261,12 +244,6 @@ bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
return true;
}
-bool CRollingBloomFilter::contains(const uint256& hash) const
-{
- std::vector<unsigned char> vData(hash.begin(), hash.end());
- return contains(vData);
-}
-
void CRollingBloomFilter::reset()
{
nTweak = GetRand(std::numeric_limits<unsigned int>::max());
diff --git a/src/bloom.h b/src/bloom.h
index fdaa8abfb2..422646d8b9 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -6,16 +6,16 @@
#define BITCOIN_BLOOM_H
#include <serialize.h>
+#include <span.h>
#include <vector>
class COutPoint;
class CTransaction;
-class uint256;
//! 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%
-static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes
-static const unsigned int MAX_HASH_FUNCS = 50;
+static constexpr unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes
+static constexpr unsigned int MAX_HASH_FUNCS = 50;
/**
* First two bits of nFlags control how much IsRelevantAndUpdate actually updates
@@ -49,7 +49,7 @@ private:
unsigned int nTweak;
unsigned char nFlags;
- unsigned int Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const;
+ unsigned int Hash(unsigned int nHashNum, Span<const unsigned char> vDataToHash) const;
public:
/**
@@ -66,13 +66,11 @@ public:
SERIALIZE_METHODS(CBloomFilter, obj) { READWRITE(obj.vData, obj.nHashFuncs, obj.nTweak, obj.nFlags); }
- void insert(const std::vector<unsigned char>& vKey);
+ void insert(Span<const unsigned char> vKey);
void insert(const COutPoint& outpoint);
- void insert(const uint256& hash);
- bool contains(const std::vector<unsigned char>& vKey) const;
+ bool contains(Span<const unsigned char> vKey) const;
bool contains(const COutPoint& outpoint) const;
- bool contains(const uint256& hash) const;
//! True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS
//! (catch a filter which was just deserialized which was too big)
@@ -112,10 +110,8 @@ class CRollingBloomFilter
public:
CRollingBloomFilter(const unsigned int nElements, const double nFPRate);
- void insert(const std::vector<unsigned char>& vKey);
- void insert(const uint256& hash);
- bool contains(const std::vector<unsigned char>& vKey) const;
- bool contains(const uint256& hash) const;
+ void insert(Span<const unsigned char> vKey);
+ bool contains(Span<const unsigned char> vKey) const;
void reset();
diff --git a/src/chain.h b/src/chain.h
index 84a3a4e1e7..365a7f79b6 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -126,7 +126,15 @@ enum BlockStatus: uint32_t {
BLOCK_FAILED_CHILD = 64, //!< descends from failed block
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
- BLOCK_OPT_WITNESS = 128, //!< block data in blk*.data was received with a witness-enforcing client
+ BLOCK_OPT_WITNESS = 128, //!< block data in blk*.dat was received with a witness-enforcing client
+
+ /**
+ * If set, this indicates that the block index entry is assumed-valid.
+ * Certain diagnostics will be skipped in e.g. CheckBlockIndex().
+ * It almost certainly means that the block's full validation is pending
+ * on a background chainstate. See `doc/assumeutxo.md`.
+ */
+ BLOCK_ASSUMED_VALID = 256,
};
/** The block chain is a tree shaped structure starting with the
@@ -300,14 +308,24 @@ public:
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
}
+ //! @returns true if the block is assumed-valid; this means it is queued to be
+ //! validated by a background chainstate.
+ bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
+
//! Raise the validity level of this block index entry.
//! Returns true if the validity was changed.
bool RaiseValidity(enum BlockStatus nUpTo)
{
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
- if (nStatus & BLOCK_FAILED_MASK)
- return false;
+ if (nStatus & BLOCK_FAILED_MASK) return false;
+
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
+ // If this block had been marked assumed-valid and we're raising
+ // its validity to a certain point, there is no longer an assumption.
+ if (nStatus & BLOCK_ASSUMED_VALID && nUpTo >= BLOCK_VALID_SCRIPTS) {
+ nStatus &= ~BLOCK_ASSUMED_VALID;
+ }
+
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;
return true;
}
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 4cc37560a3..b155745794 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -280,8 +280,10 @@ public:
if (!args.IsArgSet("-signetchallenge")) {
bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
+ vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl");
+
+ // Hardcoded nodes can be removed once there are more DNS seeds
vSeeds.emplace_back("178.128.221.177");
- vSeeds.emplace_back("2a01:7c8:d005:390::5");
vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333");
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000008546553c03");
@@ -390,12 +392,12 @@ public:
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
- consensus.BIP34Height = 2; // BIP34 activated on regtest (Block at height 1 not enforced for testing purposes)
+ consensus.BIP34Height = 1; // Always active unless overridden
consensus.BIP34Hash = uint256();
- consensus.BIP65Height = 111; // BIP65 activated on regtest (Block at height 110 and earlier not enforced for testing purposes)
- consensus.BIP66Height = 102; // BIP66 activated on regtest (Block at height 101 and earlier not enforced for testing purposes)
- consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
- consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
+ consensus.BIP65Height = 1; // Always active unless overridden
+ consensus.BIP66Height = 1; // Always active unless overridden
+ consensus.CSVHeight = 1; // Always active unless overridden
+ consensus.SegwitHeight = 1; // Always active unless overridden
consensus.MinBIP9WarningHeight = 0;
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
@@ -487,15 +489,38 @@ public:
void UpdateActivationParametersFromArgs(const ArgsManager& args);
};
-void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
+static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& consensus)
{
- if (args.IsArgSet("-segwitheight")) {
- int64_t height = args.GetArg("-segwitheight", consensus.SegwitHeight);
- if (height < 0 || height >= std::numeric_limits<int>::max()) {
- throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range.", height));
+ for (const std::string& arg : args.GetArgs("-testactivationheight")) {
+ const auto found{arg.find('@')};
+ if (found == std::string::npos) {
+ throw std::runtime_error(strprintf("Invalid format (%s) for -testactivationheight=name@height.", arg));
+ }
+ const auto name{arg.substr(0, found)};
+ const auto value{arg.substr(found + 1)};
+ int32_t height;
+ if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits<int>::max()) {
+ throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg));
+ }
+ if (name == "segwit") {
+ consensus.SegwitHeight = int{height};
+ } else if (name == "bip34") {
+ consensus.BIP34Height = int{height};
+ } else if (name == "dersig") {
+ consensus.BIP66Height = int{height};
+ } else if (name == "cltv") {
+ consensus.BIP65Height = int{height};
+ } else if (name == "csv") {
+ consensus.CSVHeight = int{height};
+ } else {
+ throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
}
- consensus.SegwitHeight = static_cast<int>(height);
}
+}
+
+void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
+{
+ MaybeUpdateHeights(args, consensus);
if (!args.IsArgSet("-vbparams")) return;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 79c1bc25bc..dc484f5c03 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
diff --git a/src/checkqueue.h b/src/checkqueue.h
index 4ceeb3600a..7c20e2013c 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -7,6 +7,7 @@
#include <sync.h>
#include <tinyformat.h>
+#include <util/syscall_sandbox.h>
#include <util/threadnames.h>
#include <algorithm>
@@ -151,6 +152,7 @@ public:
for (int n = 0; n < threads_num; ++n) {
m_worker_threads.emplace_back([this, n]() {
util::ThreadRename(strprintf("scriptch.%i", n));
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::VALIDATION_SCRIPT_CHECK);
Loop(false /* worker thread */);
});
}
diff --git a/src/amount.h b/src/consensus/amount.h
index 47968e80b1..8b41a2277d 100644
--- a/src/amount.h
+++ b/src/consensus/amount.h
@@ -3,15 +3,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_AMOUNT_H
-#define BITCOIN_AMOUNT_H
+#ifndef BITCOIN_CONSENSUS_AMOUNT_H
+#define BITCOIN_CONSENSUS_AMOUNT_H
-#include <stdint.h>
+#include <cstdint>
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount;
-static const CAmount COIN = 100000000;
+static constexpr CAmount COIN = 100000000;
/** No amount larger than this (in satoshi) is valid.
*
@@ -22,7 +22,7 @@ static const CAmount COIN = 100000000;
* critical; in unusual circumstances like a(nother) overflow bug that allowed
* for the creation of coins out of thin air modification could lead to a fork.
* */
-static const CAmount MAX_MONEY = 21000000 * COIN;
+static constexpr CAmount MAX_MONEY = 21000000 * COIN;
inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
-#endif // BITCOIN_AMOUNT_H
+#endif // BITCOIN_CONSENSUS_AMOUNT_H
diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp
index bb8cd10c63..de4824fadc 100644
--- a/src/consensus/tx_check.cpp
+++ b/src/consensus/tx_check.cpp
@@ -4,6 +4,7 @@
#include <consensus/tx_check.h>
+#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <consensus/validation.h>
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 0ab790ccdc..a07adae536 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -4,6 +4,7 @@
#include <consensus/tx_verify.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index 264433c33d..777556808a 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CONSENSUS_TX_VERIFY_H
#define BITCOIN_CONSENSUS_TX_VERIFY_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <stdint.h>
#include <vector>
diff --git a/src/core_io.h b/src/core_io.h
index 3b9b66574c..be93a17efe 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_CORE_IO_H
#define BITCOIN_CORE_IO_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <attributes.h>
#include <string>
@@ -44,8 +44,8 @@ UniValue ValueFromAmount(const CAmount amount);
std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
std::string SighashToStr(unsigned char sighash_type);
-void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex, bool include_addresses);
-void ScriptToUniv(const CScript& script, UniValue& out, bool include_address);
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_addresses, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
+void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include_hex, bool include_address = true);
+void ScriptToUniv(const CScript& script, UniValue& out);
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
#endif // BITCOIN_CORE_IO_H
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 6108961010..2149b428d2 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -26,20 +26,20 @@ opcodetype ParseOpCode(const std::string& s)
{
static std::map<std::string, opcodetype> mapOpNames;
- if (mapOpNames.empty())
- {
- for (unsigned int op = 0; op <= MAX_OPCODE; op++)
- {
+ if (mapOpNames.empty()) {
+ for (unsigned int op = 0; op <= MAX_OPCODE; op++) {
// Allow OP_RESERVED to get into mapOpNames
- if (op < OP_NOP && op != OP_RESERVED)
+ if (op < OP_NOP && op != OP_RESERVED) {
continue;
+ }
std::string strName = GetOpName(static_cast<opcodetype>(op));
- if (strName == "OP_UNKNOWN")
+ if (strName == "OP_UNKNOWN") {
continue;
+ }
mapOpNames[strName] = static_cast<opcodetype>(op);
// Convenience: OP_ADD and just ADD are both recognized:
- if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_"
+ if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_"
mapOpNames[strName.substr(3)] = static_cast<opcodetype>(op);
}
}
@@ -59,44 +59,35 @@ CScript ParseScript(const std::string& s)
std::vector<std::string> words;
boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on);
- for (std::vector<std::string>::const_iterator w = words.begin(); w != words.end(); ++w)
- {
- if (w->empty())
- {
+ for (const std::string& w : words) {
+ if (w.empty()) {
// Empty string, ignore. (boost::split given '' will return one word)
- }
- else if (std::all_of(w->begin(), w->end(), ::IsDigit) ||
- (w->front() == '-' && w->size() > 1 && std::all_of(w->begin()+1, w->end(), ::IsDigit)))
+ } else if (std::all_of(w.begin(), w.end(), ::IsDigit) ||
+ (w.front() == '-' && w.size() > 1 && std::all_of(w.begin() + 1, w.end(), ::IsDigit)))
{
// Number
- int64_t n = atoi64(*w);
+ const auto num{ToIntegral<int64_t>(w)};
- //limit the range of numbers ParseScript accepts in decimal
- //since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts
- if (n > int64_t{0xffffffff} || n < -1 * int64_t{0xffffffff}) {
+ // limit the range of numbers ParseScript accepts in decimal
+ // since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts
+ if (!num.has_value() || num > int64_t{0xffffffff} || num < -1 * int64_t{0xffffffff}) {
throw std::runtime_error("script parse error: decimal numeric value only allowed in the "
"range -0xFFFFFFFF...0xFFFFFFFF");
}
- result << n;
- }
- else if (w->substr(0,2) == "0x" && w->size() > 2 && IsHex(std::string(w->begin()+2, w->end())))
- {
+ result << num.value();
+ } else if (w.substr(0, 2) == "0x" && w.size() > 2 && IsHex(std::string(w.begin() + 2, w.end()))) {
// Raw hex data, inserted NOT pushed onto stack:
- std::vector<unsigned char> raw = ParseHex(std::string(w->begin()+2, w->end()));
+ std::vector<unsigned char> raw = ParseHex(std::string(w.begin() + 2, w.end()));
result.insert(result.end(), raw.begin(), raw.end());
- }
- else if (w->size() >= 2 && w->front() == '\'' && w->back() == '\'')
- {
+ } else if (w.size() >= 2 && w.front() == '\'' && w.back() == '\'') {
// Single-quoted string, pushed as data. NOTE: this is poor-man's
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
- std::vector<unsigned char> value(w->begin()+1, w->end()-1);
+ std::vector<unsigned char> value(w.begin() + 1, w.end() - 1);
result << value;
- }
- else
- {
+ } else {
// opcode, e.g. OP_ADD or ADD:
- result << ParseOpCode(*w);
+ result << ParseOpCode(w);
}
}
diff --git a/src/core_write.cpp b/src/core_write.cpp
index b35f835f42..6b13e4c586 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -4,6 +4,7 @@
#include <core_io.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <key_io.h>
@@ -141,56 +142,28 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
return HexStr(ssTx);
}
-void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
+void ScriptToUniv(const CScript& script, UniValue& out)
{
- out.pushKV("asm", ScriptToAsmStr(script));
- out.pushKV("hex", HexStr(script));
-
- std::vector<std::vector<unsigned char>> solns;
- TxoutType type = Solver(script, solns);
- out.pushKV("type", GetTxnOutputType(type));
-
- CTxDestination address;
- if (include_address && ExtractDestination(script, address) && type != TxoutType::PUBKEY) {
- out.pushKV("address", EncodeDestination(address));
- }
+ ScriptPubKeyToUniv(script, out, /* include_hex */ true, /* include_address */ false);
}
-// TODO: from v23 ("addresses" and "reqSigs" deprecated) this method should be refactored to remove the `include_addresses` option
-// this method can also be combined with `ScriptToUniv` as they will overlap
-void ScriptPubKeyToUniv(const CScript& scriptPubKey,
- UniValue& out, bool fIncludeHex, bool include_addresses)
+void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include_hex, bool include_address)
{
- TxoutType type;
CTxDestination address;
- std::vector<CTxDestination> addresses;
- int nRequired;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
- if (fIncludeHex)
- out.pushKV("hex", HexStr(scriptPubKey));
+ if (include_hex) out.pushKV("hex", HexStr(scriptPubKey));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TxoutType::PUBKEY) {
- out.pushKV("type", GetTxnOutputType(type));
- return;
- }
+ std::vector<std::vector<unsigned char>> solns;
+ const TxoutType type{Solver(scriptPubKey, solns)};
- if (ExtractDestination(scriptPubKey, address)) {
+ if (include_address && ExtractDestination(scriptPubKey, address) && type != TxoutType::PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
out.pushKV("type", GetTxnOutputType(type));
-
- if (include_addresses) {
- UniValue a(UniValue::VARR);
- for (const CTxDestination& addr : addresses) {
- a.push_back(EncodeDestination(addr));
- }
- out.pushKV("addresses", a);
- out.pushKV("reqSigs", nRequired);
- }
}
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_addresses, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
{
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
@@ -249,7 +222,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_add
out.pushKV("n", (int64_t)i);
UniValue o(UniValue::VOBJ);
- ScriptPubKeyToUniv(txout.scriptPubKey, o, true, include_addresses);
+ ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
out.pushKV("scriptPubKey", o);
vout.push_back(out);
diff --git a/src/crc32c/.travis.yml b/src/crc32c/.travis.yml
index d990a89f07..183a5fba45 100644
--- a/src/crc32c/.travis.yml
+++ b/src/crc32c/.travis.yml
@@ -4,7 +4,7 @@
language: cpp
dist: bionic
-osx_image: xcode10.3
+osx_image: xcode12.5
compiler:
- gcc
@@ -24,20 +24,20 @@ env:
addons:
apt:
sources:
- - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main'
+ - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
- sourceline: 'ppa:ubuntu-toolchain-r/test'
packages:
- - clang-9
+ - clang-12
- cmake
- - gcc-9
- - g++-9
+ - gcc-11
+ - g++-11
- ninja-build
homebrew:
packages:
- cmake
- - gcc@9
- - llvm@9
+ - gcc@11
+ - llvm@12
- ninja
update: true
@@ -48,14 +48,14 @@ install:
export PATH="$(brew --prefix llvm)/bin:$PATH";
fi
# /usr/bin/gcc points to an older compiler on both Linux and macOS.
-- if [ "$CXX" = "g++" ]; then export CXX="g++-9" CC="gcc-9"; fi
+- if [ "$CXX" = "g++" ]; then export CXX="g++-11" CC="gcc-11"; fi
# /usr/bin/clang points to an older compiler on both Linux and macOS.
#
# Homebrew's llvm package doesn't ship a versioned clang++ binary, so the values
# below don't work on macOS. Fortunately, the path change above makes the
# default values (clang and clang++) resolve to the correct compiler on macOS.
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
- if [ "$CXX" = "clang++" ]; then export CXX="clang++-9" CC="clang-9"; fi;
+ if [ "$CXX" = "clang++" ]; then export CXX="clang++-12" CC="clang-12"; fi;
fi
- echo ${CC}
- echo ${CXX}
diff --git a/src/crc32c/.ycm_extra_conf.py b/src/crc32c/.ycm_extra_conf.py
index 536aadcec8..62daa8a4ac 100644
--- a/src/crc32c/.ycm_extra_conf.py
+++ b/src/crc32c/.ycm_extra_conf.py
@@ -4,10 +4,10 @@
"""YouCompleteMe configuration that interprets a .clang_complete file.
This module implementes the YouCompleteMe configuration API documented at:
-https://github.com/Valloric/ycmd#ycm_extra_confpy-specification
+https://github.com/ycm-core/ycmd#ycm_extra_confpy-specification
The implementation loads and processes a .clang_complete file, documented at:
-https://github.com/Rip-Rip/clang_complete/blob/master/README.md
+https://github.com/xavierd/clang_complete/blob/master/README.md
"""
import os
diff --git a/src/crc32c/README.md b/src/crc32c/README.md
index 0bd69f7f09..58ba38e611 100644
--- a/src/crc32c/README.md
+++ b/src/crc32c/README.md
@@ -65,7 +65,7 @@ apm install autocomplete-clang build build-cmake clang-format language-cmake \
If you don't mind more setup in return for more speed, replace
`autocomplete-clang` and `linter-clang` with `you-complete-me`. This requires
-[setting up ycmd](https://github.com/Valloric/ycmd#building).
+[setting up ycmd](https://github.com/ycm-core/ycmd#building).
```bash
apm install autocomplete-plus build build-cmake clang-format language-cmake \
diff --git a/src/crc32c/src/crc32c_arm64_check.h b/src/crc32c/src/crc32c_arm64_check.h
index 62a07aba09..6b80f70037 100644
--- a/src/crc32c/src/crc32c_arm64_check.h
+++ b/src/crc32c/src/crc32c_arm64_check.h
@@ -40,7 +40,15 @@ inline bool CanUseArm64Crc32() {
// From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code.
constexpr unsigned long kHWCAP_PMULL = 1 << 4;
constexpr unsigned long kHWCAP_CRC32 = 1 << 7;
- unsigned long hwcap = (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
+ unsigned long hwcap =
+#if HAVE_STRONG_GETAUXVAL
+ // Some compilers warn on (&getauxval != nullptr) in the block below.
+ getauxval(AT_HWCAP);
+#elif HAVE_WEAK_GETAUXVAL
+ (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
+#else
+#error This is supposed to be nested inside a check for HAVE_*_GETAUXVAL.
+#endif // HAVE_STRONG_GETAUXVAL
return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) ==
(kHWCAP_PMULL | kHWCAP_CRC32);
#elif defined(__APPLE__)
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 3a1086bf4c..bcaf746167 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -197,13 +197,15 @@ bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
return true;
}
-size_t CDBWrapper::DynamicMemoryUsage() const {
+size_t CDBWrapper::DynamicMemoryUsage() const
+{
std::string memory;
- if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory)) {
+ std::optional<size_t> parsed;
+ if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
return 0;
}
- return stoul(memory);
+ return parsed.value();
}
// Prefixed with null character to avoid collisions with other keys
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 8caeb32627..7f6471740f 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -39,8 +39,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-maxtxfee=<amt>",
"-mintxfee=<amt>",
"-paytxfee=<amt>",
- "-rescan",
- "-salvagewallet",
"-signer=<cmd>",
"-spendzeroconfchange",
"-txconfirmtarget=<n>",
diff --git a/src/hash.cpp b/src/hash.cpp
index 3465caa3a9..0e5bd975e4 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <hash.h>
+#include <span.h>
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
@@ -74,10 +75,7 @@ unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vData
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64])
{
unsigned char num[4];
- num[0] = (nChild >> 24) & 0xFF;
- num[1] = (nChild >> 16) & 0xFF;
- num[2] = (nChild >> 8) & 0xFF;
- num[3] = (nChild >> 0) & 0xFF;
+ WriteBE32(num, nChild);
CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output);
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index fa0379f612..6e75e28596 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -12,6 +12,7 @@
#include <shutdown.h>
#include <sync.h>
#include <util/strencodings.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
@@ -279,6 +280,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
static bool ThreadHTTP(struct event_base* base)
{
util::ThreadRename("http");
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_HTTP_SERVER);
LogPrint(BCLog::HTTP, "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
@@ -289,7 +291,7 @@ static bool ThreadHTTP(struct event_base* base)
/** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp* http)
{
- uint16_t http_port{static_cast<uint16_t>(gArgs.GetArg("-rpcport", BaseParams().RPCPort()))};
+ uint16_t http_port{static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", BaseParams().RPCPort()))};
std::vector<std::pair<std::string, uint16_t>> endpoints;
// Determine what addresses to bind to
@@ -332,6 +334,7 @@ static bool HTTPBindAddresses(struct evhttp* http)
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
{
util::ThreadRename(strprintf("httpworker.%i", worker_num));
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_HTTP_SERVER_WORKER);
queue->Run();
}
@@ -374,7 +377,7 @@ bool InitHTTPServer()
return false;
}
- evhttp_set_timeout(http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
+ evhttp_set_timeout(http, gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
evhttp_set_max_body_size(http, MAX_SIZE);
evhttp_set_gencb(http, http_request_cb, nullptr);
@@ -385,7 +388,7 @@ bool InitHTTPServer()
}
LogPrint(BCLog::HTTP, "Initialized HTTP server\n");
- int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
+ int workQueueDepth = std::max((long)gArgs.GetIntArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
g_work_queue = std::make_unique<WorkQueue<HTTPClosure>>(workQueueDepth);
@@ -415,7 +418,7 @@ static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
{
LogPrint(BCLog::HTTP, "Starting HTTP server\n");
- int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
+ int rpcThreads = std::max((long)gArgs.GetIntArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
g_thread_http = std::thread(ThreadHTTP, eventBase);
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 6fd2701e2e..3ca86a310e 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -8,6 +8,7 @@
#include <node/ui_interface.h>
#include <shutdown.h>
#include <tinyformat.h>
+#include <util/syscall_sandbox.h>
#include <util/thread.h>
#include <util/translation.h>
#include <validation.h> // For g_chainman
@@ -123,6 +124,7 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain&
void BaseIndex::ThreadSync()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::TX_INDEX);
const CBlockIndex* pindex = m_best_block_index.load();
if (!m_synced) {
auto& consensus_params = Params().GetConsensus();
diff --git a/src/init.cpp b/src/init.cpp
index ff36ec805c..4495ded64d 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -10,12 +10,12 @@
#include <init.h>
#include <addrman.h>
-#include <amount.h>
#include <banman.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <compat/sanity.h>
+#include <consensus/amount.h>
#include <deploymentstatus.h>
#include <fs.h>
#include <hash.h>
@@ -60,6 +60,7 @@
#include <util/check.h>
#include <util/moneystr.h>
#include <util/string.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <util/thread.h>
#include <util/threadnames.h>
@@ -218,7 +219,7 @@ void Shutdown(NodeContext& node)
node.banman.reset();
node.addrman.reset();
- if (node.mempool && node.mempool->IsLoaded() && node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(*node.mempool);
}
@@ -397,7 +398,7 @@ void SetupServerArgs(ArgsManager& argsman)
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -coinstatsindex and -rescan. "
+ argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -coinstatsindex. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -562,6 +563,10 @@ void SetupServerArgs(ArgsManager& argsman)
hidden_args.emplace_back("-daemonwait");
#endif
+#if defined(USE_SYSCALL_SANDBOX)
+ argsman.AddArg("-sandbox=<mode>", "Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the \"log-and-abort\" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the \"abort\" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+#endif // USE_SYSCALL_SANDBOX
+
// Add the hidden options
argsman.AddHiddenArgs(hidden_args);
}
@@ -843,7 +848,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
// if using block pruning, then disallow txindex and coinstatsindex
- if (args.GetArg("-prune", 0)) {
+ if (args.GetIntArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX))
@@ -868,11 +873,9 @@ bool AppInitParameterInteraction(const ArgsManager& args)
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
- nUserMaxConnections = args.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
+ nUserMaxConnections = args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
- // Trim requested connection counts, to fit into system limitations
- // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind + NUM_FDS_MESSAGE_CAPTURE);
#ifdef USE_POLL
@@ -880,6 +883,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
#else
int fd_max = FD_SETSIZE;
#endif
+ // Trim requested connection counts, to fit into system limitations
+ // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
@@ -915,8 +920,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
// mempool limits
- int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t nMempoolSizeMin = args.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
+ int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMin = args.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
@@ -930,7 +935,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
- int64_t nPruneArg = args.GetArg("-prune", 0);
+ int64_t nPruneArg = args.GetIntArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(_("Prune cannot be configured with a negative value."));
}
@@ -947,12 +952,12 @@ bool AppInitParameterInteraction(const ArgsManager& args)
fPruneMode = true;
}
- nConnectTimeout = args.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
+ nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
- peer_connect_timeout = args.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
+ peer_connect_timeout = args.GetIntArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
}
@@ -992,32 +997,63 @@ bool AppInitParameterInteraction(const ArgsManager& args)
if (!chainparams.IsTestChain() && !fRequireStandard) {
return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
}
- nBytesPerSigOp = args.GetArg("-bytespersigop", nBytesPerSigOp);
+ nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) return false;
fIsBareMultisigStd = args.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
- nMaxDatacarrierBytes = args.GetArg("-datacarriersize", nMaxDatacarrierBytes);
+ nMaxDatacarrierBytes = args.GetIntArg("-datacarriersize", nMaxDatacarrierBytes);
// Option to startup with mocktime set (used for regression testing):
- SetMockTime(args.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
+ SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
+ if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError(Untranslated("rpcserialversion must be non-negative."));
- if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError(Untranslated("Unknown rpcserialversion requested."));
- nMaxTipAge = args.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
+ nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
}
+#if defined(USE_SYSCALL_SANDBOX)
+ if (args.IsArgSet("-sandbox") && !args.IsArgNegated("-sandbox")) {
+ const std::string sandbox_arg{args.GetArg("-sandbox", "")};
+ bool log_syscall_violation_before_terminating{false};
+ if (sandbox_arg == "log-and-abort") {
+ log_syscall_violation_before_terminating = true;
+ } else if (sandbox_arg == "abort") {
+ // log_syscall_violation_before_terminating is false by default.
+ } else {
+ return InitError(Untranslated("Unknown syscall sandbox mode (-sandbox=<mode>). Available modes are \"log-and-abort\" and \"abort\"."));
+ }
+ // execve(...) is not allowed by the syscall sandbox.
+ const std::vector<std::string> features_using_execve{
+ "-alertnotify",
+ "-blocknotify",
+ "-signer",
+ "-startupnotify",
+ "-walletnotify",
+ };
+ for (const std::string& feature_using_execve : features_using_execve) {
+ if (!args.GetArg(feature_using_execve, "").empty()) {
+ return InitError(Untranslated(strprintf("The experimental syscall sandbox feature (-sandbox=<mode>) is incompatible with %s (which uses execve).", feature_using_execve)));
+ }
+ }
+ if (!SetupSyscallSandbox(log_syscall_violation_before_terminating)) {
+ return InitError(Untranslated("Installation of the syscall sandbox failed."));
+ }
+ LogPrintf("Experimental syscall sandbox enabled (-sandbox=%s): bitcoind will terminate if an unexpected (not allowlisted) syscall is invoked.\n", sandbox_arg);
+ }
+#endif // USE_SYSCALL_SANDBOX
+
return true;
}
@@ -1101,7 +1137,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
InitSignatureCache();
InitScriptExecutionCache();
- int script_threads = args.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
+ int script_threads = args.GetIntArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script threads)
@@ -1208,7 +1244,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
assert(!node.banman);
- node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true));
@@ -1218,7 +1254,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
assert(!node.mempool);
- int check_ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
+ int check_ratio = std::min<int>(std::max<int>(args.GetIntArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio);
assert(!node.chainman);
@@ -1325,7 +1361,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
- int64_t nTotalCache = (args.GetArg("-dbcache", nDefaultDbCache) << 20);
+ int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
@@ -1343,7 +1379,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
int64_t nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
- int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
@@ -1499,7 +1535,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(_("Verifying blocks…").translated);
- if (fHavePruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
+ if (fHavePruned && args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
@@ -1516,8 +1552,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!CVerifyDB().VerifyDB(
*chainstate, chainparams, chainstate->CoinsDB(),
- args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
- args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
+ args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
failed_verification = true;
break;
@@ -1709,11 +1745,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc = node.peerman.get();
- connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
- connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+ connOptions.nSendBufferMaxSize = 1000 * args.GetIntArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
+ connOptions.nReceiveFloodSize = 1000 * args.GetIntArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
- connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
+ connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetIntArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
connOptions.m_peer_connect_timeout = peer_connect_timeout;
for (const std::string& bind_arg : args.GetArgs("-bind")) {
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index a134174078..d4ceb517dd 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -177,7 +177,7 @@ public:
std::string& err_string) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
- virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
+ virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;
//! Get the node's package limits.
//! Currently only returns the ancestor and descendant count limits, but could be enhanced to
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 770b1b8753..34fdde3774 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_INTERFACES_NODE_H
#define BITCOIN_INTERFACES_NODE_H
-#include <amount.h> // For CAmount
+#include <consensus/amount.h>
#include <external_signer.h>
#include <net.h> // For NodeId
#include <net_types.h> // For banmap_t
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a85db04b8b..490563426c 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_INTERFACES_WALLET_H
#define BITCOIN_INTERFACES_WALLET_H
-#include <amount.h> // For CAmount
+#include <consensus/amount.h>
#include <interfaces/chain.h> // For ChainClient
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/standard.h> // For CTxDestination
@@ -122,10 +122,10 @@ public:
virtual bool displayAddress(const CTxDestination& dest) = 0;
//! Lock coin.
- virtual void lockCoin(const COutPoint& output) = 0;
+ virtual bool lockCoin(const COutPoint& output, const bool write_to_db) = 0;
//! Unlock coin.
- virtual void unlockCoin(const COutPoint& output) = 0;
+ virtual bool unlockCoin(const COutPoint& output) = 0;
//! Return whether coin is locked.
virtual bool isLockedCoin(const COutPoint& output) = 0;
diff --git a/src/key.cpp b/src/key.cpp
index 40df248e02..39155e4311 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -343,8 +343,7 @@ CExtPubKey CExtKey::Neuter() const {
void CExtKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
code[0] = nDepth;
memcpy(code+1, vchFingerprint, 4);
- code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
- code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ WriteBE32(code+5, nChild);
memcpy(code+9, chaincode.begin(), 32);
code[41] = 0;
assert(key.size() == 32);
@@ -354,7 +353,7 @@ void CExtKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
void CExtKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
nDepth = code[0];
memcpy(vchFingerprint, code+1, 4);
- nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ nChild = ReadBE32(code+5);
memcpy(chaincode.begin(), code+9, 32);
key.Set(code+42, code+BIP32_EXTKEY_SIZE, true);
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || code[41] != 0) key = CKey();
diff --git a/src/logging.cpp b/src/logging.cpp
index eb2c750296..a352e106e5 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -160,6 +160,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::I2P, "i2p"},
{BCLog::IPC, "ipc"},
{BCLog::LOCK, "lock"},
+ {BCLog::UTIL, "util"},
{BCLog::ALL, "1"},
{BCLog::ALL, "all"},
};
diff --git a/src/logging.h b/src/logging.h
index 53a89d28bd..02e64a7c48 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -60,6 +60,7 @@ namespace BCLog {
I2P = (1 << 22),
IPC = (1 << 23),
LOCK = (1 << 24),
+ UTIL = (1 << 25),
ALL = ~(uint32_t)0,
};
diff --git a/src/mapport.cpp b/src/mapport.cpp
index 135efb561e..a2d06c68b4 100644
--- a/src/mapport.cpp
+++ b/src/mapport.cpp
@@ -14,6 +14,7 @@
#include <netaddress.h>
#include <netbase.h>
#include <threadinterrupt.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <util/thread.h>
@@ -222,6 +223,7 @@ static bool ProcessUpnp()
static void ThreadMapPort()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_MAP_PORT);
bool ok;
do {
ok = false;
diff --git a/src/miner.cpp b/src/miner.cpp
index 168ade5507..1ef246cd14 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -5,10 +5,10 @@
#include <miner.h>
-#include <amount.h>
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
@@ -72,7 +72,7 @@ static BlockAssembler::Options DefaultOptions()
// Block resource limits
// If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
BlockAssembler::Options options;
- options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
+ options.nBlockMaxWeight = gArgs.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
if (gArgs.IsArgSet("-blockmintxfee")) {
std::optional<CAmount> parsed = ParseMoney(gArgs.GetArg("-blockmintxfee", ""));
options.blockMinFeeRate = CFeeRate{parsed.value_or(DEFAULT_BLOCK_MIN_TX_FEE)};
@@ -125,7 +125,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
- pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);
+ pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
@@ -237,7 +237,7 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
if (fPrintPriority) {
- LogPrintf("fee %s txid %s\n",
+ LogPrintf("fee rate %s txid %s\n",
CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(),
iter->GetTx().GetHash().ToString());
}
diff --git a/src/net.cpp b/src/net.cpp
index c72cd75ba7..7271ff22b2 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -25,6 +25,7 @@
#include <scheduler.h>
#include <util/sock.h>
#include <util/strencodings.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <util/thread.h>
#include <util/trace.h>
@@ -123,7 +124,7 @@ void CConnman::AddAddrFetch(const std::string& strDest)
uint16_t GetListenPort()
{
- return static_cast<uint16_t>(gArgs.GetArg("-port", Params().GetDefaultPort()));
+ return static_cast<uint16_t>(gArgs.GetIntArg("-port", Params().GetDefaultPort()));
}
// find 'best' local address for a particular peer
@@ -192,8 +193,8 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
static int GetnScore(const CService& addr)
{
LOCK(cs_mapLocalHost);
- if (mapLocalHost.count(addr) == 0) return 0;
- return mapLocalHost[addr].nScore;
+ const auto it = mapLocalHost.find(addr);
+ return (it != mapLocalHost.end()) ? it->second.nScore : 0;
}
// Is our peer's addrLocal potentially useful as an external IP source?
@@ -245,10 +246,10 @@ bool AddLocal(const CService& addr, int nScore)
{
LOCK(cs_mapLocalHost);
- bool fAlready = mapLocalHost.count(addr) > 0;
- LocalServiceInfo &info = mapLocalHost[addr];
- if (!fAlready || nScore >= info.nScore) {
- info.nScore = nScore + (fAlready ? 1 : 0);
+ const auto [it, is_newly_added] = mapLocalHost.emplace(addr, LocalServiceInfo());
+ LocalServiceInfo &info = it->second;
+ if (is_newly_added || nScore >= info.nScore) {
+ info.nScore = nScore + (is_newly_added ? 0 : 1);
info.nPort = addr.GetPort();
}
}
@@ -290,12 +291,10 @@ bool IsReachable(const CNetAddr &addr)
/** vote for a local address */
bool SeenLocal(const CService& addr)
{
- {
- LOCK(cs_mapLocalHost);
- if (mapLocalHost.count(addr) == 0)
- return false;
- mapLocalHost[addr].nScore++;
- }
+ LOCK(cs_mapLocalHost);
+ const auto it = mapLocalHost.find(addr);
+ if (it == mapLocalHost.end()) return false;
+ ++it->second.nScore;
return true;
}
@@ -1617,6 +1616,7 @@ void CConnman::SocketHandler()
void CConnman::ThreadSocketHandler()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET);
while (!interruptNet)
{
DisconnectNodes();
@@ -1636,6 +1636,7 @@ void CConnman::WakeMessageHandler()
void CConnman::ThreadDNSAddressSeed()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_DNS_SEED);
FastRandomContext rng;
std::vector<std::string> seeds = Params().DNSSeeds();
Shuffle(seeds.begin(), seeds.end(), rng);
@@ -1818,6 +1819,7 @@ int CConnman::GetExtraBlockRelayCount() const
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION);
// Connect to specific addresses
if (!connect.empty())
{
@@ -2008,17 +2010,18 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (nTries > 100)
break;
- CAddrInfo addr;
+ CAddress addr;
+ int64_t addr_last_try{0};
if (fFeeler) {
// First, try to get a tried table collision address. This returns
// an empty (invalid) address if there are no collisions to try.
- addr = addrman.SelectTriedCollision();
+ std::tie(addr, addr_last_try) = addrman.SelectTriedCollision();
if (!addr.IsValid()) {
// No tried table collisions. Select a new table address
// for our feeler.
- addr = addrman.Select(true);
+ std::tie(addr, addr_last_try) = addrman.Select(true);
} else if (AlreadyConnectedToAddress(addr)) {
// If test-before-evict logic would have us connect to a
// peer that we're already connected to, just mark that
@@ -2027,11 +2030,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// a currently-connected peer.
addrman.Good(addr);
// Select a new table address for our feeler instead.
- addr = addrman.Select(true);
+ std::tie(addr, addr_last_try) = addrman.Select(true);
}
} else {
// Not a feeler
- addr = addrman.Select();
+ std::tie(addr, addr_last_try) = addrman.Select();
}
// Require outbound connections, other than feelers, to be to distinct network groups
@@ -2048,7 +2051,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
continue;
// only consider very recently tried nodes after 30 failed attempts
- if (nANow - addr.nLastTry < 600 && nTries < 30)
+ if (nANow - addr_last_try < 600 && nTries < 30)
continue;
// for non-feelers, require all the services we'll want,
@@ -2157,6 +2160,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
void CConnman::ThreadOpenAddedConnections()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
while (true)
{
CSemaphoreGrant grant(*semAddnode);
@@ -2220,6 +2224,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
void CConnman::ThreadMessageHandler()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::MESSAGE_HANDLER);
FastRandomContext rng;
while (!flagInterruptMsgProc)
{
@@ -2439,7 +2444,7 @@ void CConnman::SetNetworkActive(bool active)
}
}
-CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, CAddrMan& addrman_in, bool network_active)
+CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in, bool network_active)
: addrman(addrman_in), nSeed0(nSeed0In), nSeed1(nSeed1In)
{
SetTryNewOutboundPeer(false);
diff --git a/src/net.h b/src/net.h
index 0a72ca888d..e2071414b4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -7,10 +7,10 @@
#define BITCOIN_NET_H
#include <addrman.h>
-#include <amount.h>
#include <bloom.h>
#include <chainparams.h>
#include <compat.h>
+#include <consensus/amount.h>
#include <crypto/siphash.h>
#include <hash.h>
#include <i2p.h>
@@ -797,7 +797,7 @@ public:
m_onion_binds = connOptions.onion_binds;
}
- CConnman(uint64_t seed0, uint64_t seed1, CAddrMan& addrman, bool network_active = true);
+ CConnman(uint64_t seed0, uint64_t seed1, AddrMan& addrman, bool network_active = true);
~CConnman();
bool Start(CScheduler& scheduler, const Options& options);
@@ -1049,7 +1049,7 @@ private:
std::vector<ListenSocket> vhListenSocket;
std::atomic<bool> fNetworkActive{true};
bool fAddressesInitialized{false};
- CAddrMan& addrman;
+ AddrMan& addrman;
std::deque<std::string> m_addr_fetches GUARDED_BY(m_addr_fetches_mutex);
RecursiveMutex m_addr_fetches_mutex;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 80655c61e7..66b99aa2bb 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -10,6 +10,7 @@
#include <blockencodings.h>
#include <blockfilter.h>
#include <chainparams.h>
+#include <consensus/amount.h>
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <hash.h>
@@ -291,7 +292,7 @@ using PeerRef = std::shared_ptr<Peer>;
class PeerManagerImpl final : public PeerManager
{
public:
- PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
+ PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
@@ -409,7 +410,7 @@ private:
const CChainParams& m_chainparams;
CConnman& m_connman;
- CAddrMan& m_addrman;
+ AddrMan& m_addrman;
/** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
BanMan* const m_banman;
ChainstateManager& m_chainman;
@@ -884,6 +885,12 @@ bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, st
void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
{
AssertLockHeld(cs_main);
+
+ // Never request high-bandwidth mode from peers if we're blocks-only. Our
+ // mempool will not contain the transactions necessary to reconstruct the
+ // compact block.
+ if (m_ignore_incoming_txs) return;
+
CNodeState* nodestate = State(nodeid);
if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) {
// Never ask from peers who can't provide witnesses.
@@ -1300,7 +1307,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
{
- size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
+ size_t max_extra_txn = gArgs.GetIntArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
return;
if (!vExtraTxnForCompact.size())
@@ -1419,14 +1426,14 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
}
-std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
+std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
{
return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, chainman, pool, ignore_incoming_txs);
}
-PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
+PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
: m_chainparams(chainparams),
@@ -2165,7 +2172,11 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
}
if (vGetData.size() > 0) {
- if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ if (!m_ignore_incoming_txs &&
+ nodestate->fSupportsDesiredCmpctVersion &&
+ vGetData.size() == 1 &&
+ mapBlocksInFlight.size() == 1 &&
+ pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
@@ -2653,7 +2664,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// table is also potentially detrimental because new-table entries
// are subject to eviction in the event of addrman collisions. We
// mitigate the information-leak by never calling
- // CAddrMan::Connected() on block-relay-only peers; see
+ // AddrMan::Connected() on block-relay-only peers; see
// FinalizeNode().
//
// This moves an address from New to Tried table in Addrman,
@@ -3315,7 +3326,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
- unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
+ unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetIntArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
unsigned int nEvicted = m_orphanage.LimitOrphans(nMaxOrphanTx);
if (nEvicted > 0) {
LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
@@ -4416,7 +4427,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds c
// peers with the forcerelay permission should not filter txs to us
if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return;
- CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
diff --git a/src/net_processing.h b/src/net_processing.h
index 9d8d788583..27bc40687a 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -9,7 +9,7 @@
#include <net.h>
#include <validationinterface.h>
-class CAddrMan;
+class AddrMan;
class CChainParams;
class CTxMemPool;
class ChainstateManager;
@@ -37,7 +37,7 @@ struct CNodeStateStats {
class PeerManager : public CValidationInterface, public NetEventsInterface
{
public:
- static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
+ static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
virtual ~PeerManager() { }
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index b2f4945e3b..f9fff5a6d5 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -165,7 +165,7 @@ void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6)
}
/**
- * Create an "internal" address that represents a name or FQDN. CAddrMan uses
+ * Create an "internal" address that represents a name or FQDN. AddrMan uses
* these fake addresses to keep track of which DNS seeds were used.
* @returns Whether or not the operation was successful.
* @see NET_INTERNAL, INTERNAL_IN_IPV6_PREFIX, CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
diff --git a/src/netaddress.h b/src/netaddress.h
index cfb2edcd34..66c8c48f08 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -62,7 +62,7 @@ enum Network {
NET_CJDNS,
/// A set of addresses that represent the hash of a string or FQDN. We use
- /// them in CAddrMan to keep track of which DNS seeds were used.
+ /// them in AddrMan to keep track of which DNS seeds were used.
NET_INTERNAL,
/// Dummy value to indicate the number of NET_* constants.
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 5ddcf95c84..0b7df9bd9a 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -16,6 +16,7 @@
#include <signet.h>
#include <streams.h>
#include <undo.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <validation.h>
@@ -85,7 +86,7 @@ void CleanupBlockRevFiles()
// start removing block files.
int nContigCounter = 0;
for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
- if (atoi(item.first) == nContigCounter) {
+ if (LocaleIndependentAtoi<int>(item.first) == nContigCounter) {
nContigCounter++;
continue;
}
@@ -489,6 +490,7 @@ struct CImportingNow {
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_LOAD_BLOCKS);
ScheduleBatchPriority();
{
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 69e856dd15..9e9503ff5d 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -6,9 +6,9 @@
#ifndef BITCOIN_NODE_COINSTATS_H
#define BITCOIN_NODE_COINSTATS_H
-#include <amount.h>
#include <chain.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <streams.h>
#include <uint256.h>
diff --git a/src/node/context.h b/src/node/context.h
index 135f9ea1c6..26873345b4 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -12,7 +12,7 @@
class ArgsManager;
class BanMan;
-class CAddrMan;
+class AddrMan;
class CBlockPolicyEstimator;
class CConnman;
class CScheduler;
@@ -39,7 +39,7 @@ class WalletClient;
struct NodeContext {
//! Init interface for initializing current process and connecting to other processes.
interfaces::Init* init{nullptr};
- std::unique_ptr<CAddrMan> addrman;
+ std::unique_ptr<AddrMan> addrman;
std::unique_ptr<CConnman> connman;
std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index b6778ed4cc..73f4036057 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -575,16 +575,16 @@ public:
// that Chain clients do not need to know about.
return TransactionError::OK == err;
}
- void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
+ void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize, CAmount* ancestorfees) override
{
ancestors = descendants = 0;
if (!m_node.mempool) return;
- m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants);
+ m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants, ancestorsize, ancestorfees);
}
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
{
- limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ limit_ancestor_count = gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ limit_descendant_count = gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
}
bool checkChainLimits(const CTransactionRef& tx) override
{
@@ -592,10 +592,10 @@ public:
LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
CTxMemPool::setEntries ancestors;
- auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
- auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
- auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
+ auto limit_ancestor_count = gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ auto limit_ancestor_size = gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
+ auto limit_descendant_count = gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ auto limit_descendant_size = gArgs.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
std::string unused_error_string;
LOCK(m_node.mempool->cs);
return m_node.mempool->CalculateMemPoolAncestors(
@@ -615,7 +615,7 @@ public:
CFeeRate mempoolMinFee() override
{
if (!m_node.mempool) return {};
- return m_node.mempool->GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ return m_node.mempool->GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index b013b6d579..9ad65d15d2 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/tx_verify.h>
#include <node/psbt.h>
#include <policy/policy.h>
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index d296d32774..b16f3f8251 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_POLICY_FEERATE_H
#define BITCOIN_POLICY_FEERATE_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <serialize.h>
#include <string>
diff --git a/src/policy/fees.h b/src/policy/fees.h
index c444d71a31..27f9120c64 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_POLICY_FEES_H
#define BITCOIN_POLICY_FEES_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <policy/feerate.h>
#include <uint256.h>
#include <random.h>
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 15527afb8a..7ac2e22006 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -48,17 +48,19 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
}
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
- CTxMemPool& pool,
- const CTxMemPool::setEntries& iters_conflicting,
- CTxMemPool::setEntries& all_conflicts)
+ CTxMemPool& pool,
+ const CTxMemPool::setEntries& iters_conflicting,
+ CTxMemPool::setEntries& all_conflicts)
{
AssertLockHeld(pool.cs);
const uint256 txid = tx.GetHash();
uint64_t nConflictingCount = 0;
for (const auto& mi : iters_conflicting) {
nConflictingCount += mi->GetCountWithDescendants();
- // This potentially overestimates the number of actual descendants but we just want to be
- // conservative to avoid doing too much work.
+ // BIP125 Rule #5: don't consider replacing more than MAX_BIP125_REPLACEMENT_CANDIDATES
+ // entries from the mempool. This potentially overestimates the number of actual
+ // descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
+ // times), but we just want to be conservative to avoid doing too much work.
if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) {
return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
txid.ToString(),
@@ -66,8 +68,7 @@ std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
MAX_BIP125_REPLACEMENT_CANDIDATES);
}
}
- // If not too many to replace, then calculate the set of
- // transactions that would have to be evicted
+ // Calculate the set of all transactions that would have to be evicted.
for (CTxMemPool::txiter it : iters_conflicting) {
pool.CalculateDescendants(it, all_conflicts);
}
@@ -81,19 +82,19 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
AssertLockHeld(pool.cs);
std::set<uint256> parents_of_conflicts;
for (const auto& mi : iters_conflicting) {
- for (const CTxIn &txin : mi->GetTx().vin) {
+ for (const CTxIn& txin : mi->GetTx().vin) {
parents_of_conflicts.insert(txin.prevout.hash);
}
}
for (unsigned int j = 0; j < tx.vin.size(); j++) {
- // We don't want to accept replacements that require low feerate junk to be mined first.
- // Ideally we'd keep track of the ancestor feerates and make the decision based on that, but
- // for now requiring all new inputs to be confirmed works.
+ // BIP125 Rule #2: We don't want to accept replacements that require low feerate junk to be
+ // mined first. Ideally we'd keep track of the ancestor feerates and make the decision
+ // based on that, but for now requiring all new inputs to be confirmed works.
//
// Note that if you relax this to make RBF a little more useful, this may break the
- // CalculateMempoolAncestors RBF relaxation, above. See the comment above the first
- // CalculateMempoolAncestors call for more info.
+ // CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the
+ // descendant limit.
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
// if the new input refers to a tx that's in the mempool.
@@ -111,7 +112,7 @@ std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries&
const uint256& txid)
{
for (CTxMemPool::txiter ancestorIt : ancestors) {
- const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
+ const uint256& hashAncestor = ancestorIt->GetTx().GetHash();
if (direct_conflicts.count(hashAncestor)) {
return strprintf("%s spends conflicting transaction %s",
txid.ToString(),
@@ -150,24 +151,26 @@ std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& i
std::optional<std::string> PaysForRBF(CAmount original_fees,
CAmount replacement_fees,
size_t replacement_vsize,
+ CFeeRate relay_fee,
const uint256& txid)
{
- // The replacement must pay greater fees than the transactions it
- // replaces - if we did the bandwidth used by those conflicting
- // transactions would not be paid for.
+ // BIP125 Rule #3: The replacement fees must be greater than or equal to fees of the
+ // transactions it replaces, otherwise the bandwidth used by those conflicting transactions
+ // would not be paid for.
if (replacement_fees < original_fees) {
return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
}
- // Finally in addition to paying more fees than the conflicts the
- // new transaction must pay for its own bandwidth.
+ // BIP125 Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
+ // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
+ // increasing the fee by tiny amounts.
CAmount additional_fees = replacement_fees - original_fees;
- if (additional_fees < ::incrementalRelayFee.GetFee(replacement_vsize)) {
+ if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
txid.ToString(),
FormatMoney(additional_fees),
- FormatMoney(::incrementalRelayFee.GetFee(replacement_vsize)));
+ FormatMoney(relay_fee.GetFee(replacement_vsize)));
}
return std::nullopt;
}
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 56468a09b2..be8c2e5b8b 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -5,7 +5,12 @@
#ifndef BITCOIN_POLICY_RBF_H
#define BITCOIN_POLICY_RBF_H
+#include <primitives/transaction.h>
#include <txmempool.h>
+#include <uint256.h>
+
+#include <optional>
+#include <string>
/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
* mempool conflicts and their descendants. */
@@ -48,14 +53,14 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting,
CTxMemPool::setEntries& all_conflicts)
- EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input
* was included in one of the original transactions."
* @returns error message if Rule #2 is broken, otherwise std::nullopt. */
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting)
- EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
/** Check the intersection between two sets of transactions (a set of mempool entries and a set of
* txids) to make sure they are disjoint.
@@ -84,12 +89,14 @@ std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& i
* @param[in] original_fees Total modified fees of original transaction(s).
* @param[in] replacement_fees Total modified fees of replacement transaction(s).
* @param[in] replacement_vsize Total virtual size of replacement transaction(s).
+ * @param[in] relay_fee The node's minimum feerate for transaction relay.
* @param[in] txid Transaction ID, included in the error message if violation occurs.
* @returns error string if fees are insufficient, otherwise std::nullopt.
*/
std::optional<std::string> PaysForRBF(CAmount original_fees,
CAmount replacement_fees,
size_t replacement_vsize,
+ CFeeRate relay_fee,
const uint256& txid);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 245206b906..a871912225 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -5,6 +5,7 @@
#include <primitives/transaction.h>
+#include <consensus/amount.h>
#include <hash.h>
#include <tinyformat.h>
#include <util/strencodings.h>
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 6bf36ee854..46db39f8db 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -7,7 +7,7 @@
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include <stdint.h>
-#include <amount.h>
+#include <consensus/amount.h>
#include <script/script.h>
#include <serialize.h>
#include <uint256.h>
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index d14a20b870..956ff2b34a 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -337,8 +337,7 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
code[0] = nDepth;
memcpy(code+1, vchFingerprint, 4);
- code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
- code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ WriteBE32(code+5, nChild);
memcpy(code+9, chaincode.begin(), 32);
assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_SIZE);
@@ -347,7 +346,7 @@ void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
nDepth = code[0];
memcpy(vchFingerprint, code+1, 4);
- nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ nChild = ReadBE32(code+5);
memcpy(chaincode.begin(), code+9, 32);
pubkey.Set(code+41, code+BIP32_EXTKEY_SIZE);
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || !pubkey.IsFullyValid()) pubkey = CPubKey();
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index e78594390b..a617bb4451 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -182,14 +182,14 @@ void AddressBookPage::onEditAction()
if(indexes.isEmpty())
return;
- EditAddressDialog dlg(
+ auto dlg = new EditAddressDialog(
tab == SendingTab ?
EditAddressDialog::EditSendingAddress :
EditAddressDialog::EditReceivingAddress, this);
- dlg.setModel(model);
+ dlg->setModel(model);
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
- dlg.loadRow(origIndex.row());
- dlg.exec();
+ dlg->loadRow(origIndex.row());
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void AddressBookPage::on_newAddress_clicked()
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index d4895ea6ff..7de56a648a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -46,7 +46,6 @@
#include <QApplication>
#include <QDebug>
-#include <QFontDatabase>
#include <QLatin1String>
#include <QLibraryInfo>
#include <QLocale>
@@ -55,6 +54,7 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
+#include <QWindow>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -259,6 +259,7 @@ void BitcoinApplication::createOptionsModel(bool resetSettings)
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
{
window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
+ connect(window, &BitcoinGUI::quitRequested, this, &BitcoinApplication::requestShutdown);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
@@ -296,7 +297,7 @@ void BitcoinApplication::startThread()
/* communication to and from thread */
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &BitcoinApplication::shutdownResult);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit);
connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
@@ -326,13 +327,17 @@ void BitcoinApplication::requestInitialize()
void BitcoinApplication::requestShutdown()
{
+ for (const auto w : QGuiApplication::topLevelWindows()) {
+ w->hide();
+ }
+
// Show a simple window indicating shutdown status
// Do this first as some of the steps may take some time below,
// for example the RPC console may still be executing a command.
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
qDebug() << __func__ << ": Requesting shutdown";
- window->hide();
+
// Must disconnect node signals otherwise current thread can deadlock since
// no event loop is running.
window->unsubscribeFromCoreSignals();
@@ -409,15 +414,10 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
pollShutdownTimer->start(200);
} else {
Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
- quit(); // Exit first main loop invocation
+ requestShutdown();
}
}
-void BitcoinApplication::shutdownResult()
-{
- quit(); // Exit second main loop invocation after shutdown finished
-}
-
void BitcoinApplication::handleRunawayException(const QString &message)
{
QMessageBox::critical(
@@ -491,7 +491,7 @@ int GuiMain(int argc, char* argv[])
#endif
BitcoinApplication app;
- QFontDatabase::addApplicationFont(":/fonts/monospace");
+ GUIUtil::LoadFont(QStringLiteral(":/fonts/monospace"));
/// 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:
@@ -640,8 +640,6 @@ int GuiMain(int argc, char* argv[])
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
#endif
app.exec();
- app.requestShutdown();
- app.exec();
rv = app.getReturnValue();
} else {
// A dialog with detailed error will have been shown by InitError()
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 602b76052c..5678ca90d2 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -61,8 +61,6 @@ public:
/// Request core initialization
void requestInitialize();
- /// Request core shutdown
- void requestShutdown();
/// Get process return value
int getReturnValue() const { return returnValue; }
@@ -77,7 +75,8 @@ public:
public Q_SLOTS:
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
- void shutdownResult();
+ /// Request core shutdown
+ void requestShutdown();
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index c60d9a2c90..4855ada513 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_BITCOINAMOUNTFIELD_H
#define BITCOIN_QT_BITCOINAMOUNTFIELD_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <QWidget>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 862bdd3bfe..610637360b 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -107,7 +107,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
walletFrame = new WalletFrame(_platformStyle, this);
connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] {
auto activity = new CreateWalletActivity(getWalletController(), this);
- connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
connect(walletFrame, &WalletFrame::message, [this](const QString& title, const QString& message, unsigned int style) {
@@ -171,7 +170,9 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
frameBlocksLayout->addWidget(unitDisplayControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelWalletEncryptionIcon);
+ labelWalletEncryptionIcon->hide();
frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
+ labelWalletHDStatusIcon->hide();
}
frameBlocksLayout->addWidget(labelProxyIcon);
frameBlocksLayout->addStretch();
@@ -371,7 +372,7 @@ void BitcoinGUI::createActions()
m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
m_mask_values_action->setCheckable(true);
- connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
+ connect(quitAction, &QAction::triggered, this, &BitcoinGUI::quitRequested);
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked);
@@ -416,7 +417,6 @@ void BitcoinGUI::createActions()
connect(action, &QAction::triggered, [this, path] {
auto activity = new OpenWalletActivity(m_wallet_controller, this);
connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet);
- connect(activity, &OpenWalletActivity::finished, activity, &QObject::deleteLater);
activity->open(path);
});
}
@@ -431,7 +431,6 @@ void BitcoinGUI::createActions()
connect(m_create_wallet_action, &QAction::triggered, [this] {
auto activity = new CreateWalletActivity(m_wallet_controller, this);
connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet);
- connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
connect(m_close_all_wallets_action, &QAction::triggered, [this] {
@@ -662,9 +661,8 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
GUIUtil::ExceptionSafeConnect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
- for (WalletModel* wallet_model : m_wallet_controller->getOpenWallets()) {
- addWallet(wallet_model);
- }
+ auto activity = new LoadWalletsActivity(m_wallet_controller, this);
+ activity->load();
}
WalletController* BitcoinGUI::getWalletController()
@@ -848,8 +846,8 @@ void BitcoinGUI::aboutClicked()
if(!clientModel)
return;
- HelpMessageDialog dlg(this, true);
- dlg.exec();
+ auto dlg = new HelpMessageDialog(this, /* about */ true);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void BitcoinGUI::showDebugWindow()
@@ -990,10 +988,11 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
if (!clientModel || !clientModel->getOptionsModel())
return;
- OptionsDialog dlg(this, enableWallet);
- dlg.setCurrentTab(tab);
- dlg.setModel(clientModel->getOptionsModel());
- dlg.exec();
+ auto dlg = new OptionsDialog(this, enableWallet);
+ connect(dlg, &OptionsDialog::quitOnReset, this, &BitcoinGUI::quitRequested);
+ dlg->setCurrentTab(tab);
+ dlg->setModel(clientModel->getOptionsModel());
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
@@ -1216,7 +1215,7 @@ void BitcoinGUI::closeEvent(QCloseEvent *event)
// close rpcConsole in case it was open to make some space for the shutdown window
rpcConsole->close();
- QApplication::quit();
+ Q_EMIT quitRequested();
}
else
{
@@ -1410,7 +1409,7 @@ void BitcoinGUI::detectShutdown()
{
if(rpcConsole)
rpcConsole->hide();
- qApp->quit();
+ Q_EMIT quitRequested();
}
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index c83cd446a0..fa7ae4b87d 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -12,7 +12,7 @@
#include <qt/guiutil.h>
#include <qt/optionsdialog.h>
-#include <amount.h>
+#include <consensus/amount.h>
#include <QLabel>
#include <QMainWindow>
@@ -214,6 +214,7 @@ private:
void openOptionsDialogWithTab(OptionsDialog::Tab tab);
Q_SIGNALS:
+ void quitRequested();
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
/** Signal raised when RPC console shown */
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 9660ba99f7..66d5eea7ac 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -4,6 +4,8 @@
#include <qt/bitcoinunits.h>
+#include <consensus/amount.h>
+
#include <QStringList>
#include <cassert>
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index e22ba0a938..e78a347bb1 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_BITCOINUNITS_H
#define BITCOIN_QT_BITCOINUNITS_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <QAbstractListModel>
#include <QString>
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index d2a9365890..e93fedad28 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -55,7 +55,7 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
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);
+ m_copy_transaction_outpoint_action = contextMenu->addAction(tr("Copy transaction &ID and output index"), this, &CoinControlDialog::copyTransactionOutpoint);
contextMenu->addSeparator();
lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
@@ -180,7 +180,7 @@ void CoinControlDialog::showMenu(const QPoint &point)
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
- copyTransactionHashAction->setEnabled(true);
+ m_copy_transaction_outpoint_action->setEnabled(true);
if (model->wallet().isLockedCoin(COutPoint(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())))
{
lockAction->setEnabled(false);
@@ -194,7 +194,7 @@ void CoinControlDialog::showMenu(const QPoint &point)
}
else // this means click on parent node in tree mode -> disable all
{
- copyTransactionHashAction->setEnabled(false);
+ m_copy_transaction_outpoint_action->setEnabled(false);
lockAction->setEnabled(false);
unlockAction->setEnabled(false);
}
@@ -228,10 +228,14 @@ void CoinControlDialog::copyAddress()
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS));
}
-// context menu action: copy transaction id
-void CoinControlDialog::copyTransactionHash()
+// context menu action: copy transaction id and vout index
+void CoinControlDialog::copyTransactionOutpoint()
{
- GUIUtil::setClipboard(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString());
+ const QString address = contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString();
+ const QString vout = contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toString();
+ const QString outpoint = QString("%1:%2").arg(address).arg(vout);
+
+ GUIUtil::setClipboard(outpoint);
}
// context menu action: lock coin
@@ -241,7 +245,7 @@ void CoinControlDialog::lockCoin()
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
- model->wallet().lockCoin(outpt);
+ model->wallet().lockCoin(outpt, /* write_to_db = */ true);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 3a03341c9e..ec2619d115 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_COINCONTROLDIALOG_H
#define BITCOIN_QT_COINCONTROLDIALOG_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <QAbstractButton>
#include <QAction>
@@ -63,7 +63,7 @@ private:
QMenu *contextMenu;
QTreeWidgetItem *contextMenuItem;
- QAction *copyTransactionHashAction;
+ QAction* m_copy_transaction_outpoint_action;
QAction *lockAction;
QAction *unlockAction;
@@ -95,7 +95,7 @@ private Q_SLOTS:
void copyAmount();
void copyLabel();
void copyAddress();
- void copyTransactionHash();
+ void copyTransactionOutpoint();
void lockCoin();
void unlockCoin();
void clipboardQuantity();
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 59d220636d..1c22124616 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -33,7 +33,7 @@
<string>Automatically start %1 after logging in to the system.</string>
</property>
<property name="text">
- <string>&amp;Start %1 on system login</string>
+ <string>Start %1 on system &amp;login</string>
</property>
</widget>
</item>
@@ -179,7 +179,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
- <height>20</height>
+ <height>40</height>
</size>
</property>
</spacer>
@@ -187,6 +187,16 @@
</layout>
</item>
<item>
+ <widget class="QCheckBox" name="enableServer">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for Options window setting that enables the RPC server.">This allows you or a third party tool to communicate with the node through command-line and JSON-RPC commands.</string>
+ </property>
+ <property name="text">
+ <string extracomment="An Options window setting to enable the RPC server.">Enable RPC &amp;server</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer_Main">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -729,10 +739,10 @@
<item>
<widget class="QLabel" name="thirdPartyTxUrlsLabel">
<property name="toolTip">
- <string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string>
+ <string>Third-party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string>
</property>
<property name="text">
- <string>&amp;Third party transaction URLs</string>
+ <string>&amp;Third-party transaction URLs</string>
</property>
<property name="buddy">
<cstring>thirdPartyTxUrls</cstring>
@@ -742,7 +752,7 @@
<item>
<widget class="QLineEdit" name="thirdPartyTxUrls">
<property name="toolTip">
- <string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string>
+ <string>Third-party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string>
</property>
<property name="placeholderText">
<string notr="true">https://example.com/tx/%s</string>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index e98e50ba14..12d3a48d01 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -36,6 +36,7 @@
#include <QClipboard>
#include <QDateTime>
#include <QDesktopServices>
+#include <QDialog>
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
@@ -271,6 +272,12 @@ bool hasEntryData(const QAbstractItemView *view, int column, int role)
return !selection.at(0).data(role).toString().isEmpty();
}
+void LoadFont(const QString& file_name)
+{
+ const int id = QFontDatabase::addApplicationFont(file_name);
+ assert(id != -1);
+}
+
QString getDefaultDataDirectory()
{
return boostPathToQString(GetDefaultDataDir());
@@ -673,14 +680,26 @@ QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction
{
QString prefix;
if (prepend_direction) {
- prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " ";
+ prefix = (conn_type == ConnectionType::INBOUND) ?
+ /*: An inbound connection from a peer. An inbound connection
+ is a connection initiated by a peer. */
+ QObject::tr("Inbound") :
+ /*: An outbound connection to a peer. An outbound connection
+ is a connection initiated by us. */
+ QObject::tr("Outbound") + " ";
}
switch (conn_type) {
case ConnectionType::INBOUND: return prefix;
+ //: Peer connection type that relays all network information.
case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay");
+ /*: Peer connection type that relays network information about
+ blocks and not transactions or addresses. */
case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay");
+ //: Peer connection type established manually through one of several methods.
case ConnectionType::MANUAL: return prefix + QObject::tr("Manual");
+ //: Short-lived peer connection type that tests the aliveness of known addresses.
case ConnectionType::FEELER: return prefix + QObject::tr("Feeler");
+ //: Short-lived peer connection type that solicits known addresses from a peer.
case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch");
} // no default case, so the compiler can warn about missing cases
assert(false);
@@ -958,4 +977,11 @@ void PrintSlotException(
PrintExceptionContinue(exception, description.c_str());
}
+void ShowModalDialogAndDeleteOnClose(QDialog* dialog)
+{
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->setWindowModality(Qt::ApplicationModal);
+ dialog->show();
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 06a3b63668..211f3f506d 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_GUIUTIL_H
#define BITCOIN_QT_GUIUTIL_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <fs.h>
#include <net.h>
#include <netaddress.h>
@@ -41,6 +41,7 @@ class QAbstractButton;
class QAbstractItemView;
class QAction;
class QDateTime;
+class QDialog;
class QFont;
class QKeySequence;
class QLineEdit;
@@ -113,6 +114,11 @@ namespace GUIUtil
void setClipboard(const QString& str);
/**
+ * Loads the font from the file specified by file_name, aborts if it fails.
+ */
+ void LoadFont(const QString& file_name);
+
+ /**
* Determine default data directory for operating system.
*/
QString getDefaultDataDirectory();
@@ -417,6 +423,11 @@ namespace GUIUtil
type);
}
+ /**
+ * Shows a QDialog instance asynchronously, and deletes it on close.
+ */
+ void ShowModalDialogAndDeleteOnClose(QDialog* dialog);
+
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp
index 7060f74dab..24ae7ba73d 100644
--- a/src/qt/initexecutor.cpp
+++ b/src/qt/initexecutor.cpp
@@ -5,6 +5,7 @@
#include <qt/initexecutor.h>
#include <interfaces/node.h>
+#include <qt/guiutil.h>
#include <util/system.h>
#include <util/threadnames.h>
@@ -18,7 +19,7 @@
InitExecutor::InitExecutor(interfaces::Node& node)
: QObject(), m_node(node)
{
- this->moveToThread(&m_thread);
+ m_context.moveToThread(&m_thread);
m_thread.start();
}
@@ -38,29 +39,33 @@ void InitExecutor::handleRunawayException(const std::exception* e)
void InitExecutor::initialize()
{
- try {
- util::ThreadRename("qt-init");
- qDebug() << __func__ << ": Running initialization in thread";
- interfaces::BlockAndHeaderTipInfo tip_info;
- bool rv = m_node.appInitMain(&tip_info);
- Q_EMIT initializeResult(rv, tip_info);
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
+ GUIUtil::ObjectInvoke(&m_context, [this] {
+ try {
+ util::ThreadRename("qt-init");
+ qDebug() << "Running initialization in thread";
+ interfaces::BlockAndHeaderTipInfo tip_info;
+ bool rv = m_node.appInitMain(&tip_info);
+ Q_EMIT initializeResult(rv, tip_info);
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+ });
}
void InitExecutor::shutdown()
{
- try {
- qDebug() << __func__ << ": Running Shutdown in thread";
- m_node.appShutdown();
- qDebug() << __func__ << ": Shutdown finished";
- Q_EMIT shutdownResult();
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
+ GUIUtil::ObjectInvoke(&m_context, [this] {
+ try {
+ qDebug() << "Running Shutdown in thread";
+ m_node.appShutdown();
+ qDebug() << "Shutdown finished";
+ Q_EMIT shutdownResult();
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+ });
}
diff --git a/src/qt/initexecutor.h b/src/qt/initexecutor.h
index 319ce40465..410c44fa2d 100644
--- a/src/qt/initexecutor.h
+++ b/src/qt/initexecutor.h
@@ -40,6 +40,7 @@ private:
void handleRunawayException(const std::exception* e);
interfaces::Node& m_node;
+ QObject m_context;
QThread m_thread;
};
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index a698a96857..4c78fba752 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -113,7 +113,7 @@ namespace {
//! Return pruning size that will be used if automatic pruning is enabled.
int GetPruneTargetGB()
{
- int64_t prune_target_mib = gArgs.GetArg("-prune", 0);
+ int64_t prune_target_mib = gArgs.GetIntArg("-prune", 0);
// >1 means automatic pruning is enabled by config, 1 means manual pruning, 0 means no pruning.
return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib) : DEFAULT_PRUNE_TARGET_GB;
}
@@ -142,7 +142,7 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si
const int min_prune_target_GB = std::ceil(MIN_DISK_SPACE_FOR_BLOCK_FILES / 1e9);
ui->pruneGB->setRange(min_prune_target_GB, std::numeric_limits<int>::max());
- if (gArgs.GetArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB
+ if (gArgs.GetIntArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB
ui->prune->setChecked(true);
ui->prune->setEnabled(false);
}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 92644ef24b..0cc2d61df6 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -210,6 +210,7 @@ void OptionsDialog::setModel(OptionsModel *_model)
connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Network */
connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
+ connect(ui->enableServer, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Display */
@@ -246,6 +247,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp);
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
+ mapper->addMapping(ui->enableServer, OptionsModel::Server);
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
@@ -290,7 +292,8 @@ void OptionsDialog::on_resetButton_clicked()
/* reset all options and close GUI */
model->Reset();
- QApplication::quit();
+ close();
+ Q_EMIT quitOnReset();
}
}
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index ba35ff3b67..f14aec3449 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -68,6 +68,7 @@ private Q_SLOTS:
Q_SIGNALS:
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);
+ void quitOnReset();
private:
Ui::OptionsDialog *ui;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index d87fc1f84a..9e2f38f7ec 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -149,6 +149,13 @@ void OptionsModel::Init(bool resetSettings)
if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
addOverriddenOption("-listen");
+ if (!settings.contains("server")) {
+ settings.setValue("server", false);
+ }
+ if (!gArgs.SoftSetBoolArg("-server", settings.value("server").toBool())) {
+ addOverriddenOption("-server");
+ }
+
if (!settings.contains("fUseProxy"))
settings.setValue("fUseProxy", false);
if (!settings.contains("addrProxy"))
@@ -363,6 +370,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return settings.value("nThreadsScriptVerif");
case Listen:
return settings.value("fListen");
+ case Server:
+ return settings.value("server");
default:
return QVariant();
}
@@ -528,6 +537,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
setRestartRequired(true);
}
break;
+ case Server:
+ if (settings.value("server") != value) {
+ settings.setValue("server", value);
+ setRestartRequired(true);
+ }
+ break;
default:
break;
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 203ee27ad8..8f1513e48d 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -5,7 +5,6 @@
#ifndef BITCOIN_QT_OPTIONSMODEL_H
#define BITCOIN_QT_OPTIONSMODEL_H
-#include <amount.h>
#include <cstdint>
#include <qt/guiconstants.h>
@@ -69,6 +68,7 @@ public:
ExternalSignerPath, // QString
SpendZeroConfChange, // bool
Listen, // bool
+ Server, // bool
OptionIDRowCount,
};
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 289fb9f7c8..34d56e5506 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -71,6 +71,9 @@ void PSBTOperationsDialog::signTransaction()
{
bool complete;
size_t n_signed;
+
+ WalletModel::UnlockContext ctx(m_wallet_model->requestUnlock());
+
TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
@@ -81,7 +84,9 @@ void PSBTOperationsDialog::signTransaction()
updateTransactionDisplay();
- if (!complete && n_signed < 1) {
+ if (!complete && !ctx.isValid()) {
+ showStatus(tr("Cannot sign inputs while wallet is locked."), StatusLevel::WARN);
+ } else if (!complete && n_signed < 1) {
showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
} else if (!complete) {
showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 4554f11a41..0c3332ab76 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -247,10 +247,11 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes
UniValue subelement;
if (lastResult.isArray())
{
- for(char argch: curarg)
- if (!IsDigit(argch))
- throw std::runtime_error("Invalid result query");
- subelement = lastResult[atoi(curarg.c_str())];
+ const auto parsed{ToIntegral<size_t>(curarg)};
+ if (!parsed) {
+ throw std::runtime_error("Invalid result query");
+ }
+ subelement = lastResult[parsed.value()];
}
else if (lastResult.isObject())
subelement = find_value(lastResult, curarg);
@@ -495,14 +496,28 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
+ //: Explanatory text for an inbound peer connection.
tr("Inbound: initiated by peer"),
+ /*: Explanatory text for an outbound peer connection that
+ relays all network information. This is the default behavior for
+ outbound connections. */
tr("Outbound Full Relay: default"),
+ /*: Explanatory text for an outbound peer connection that relays
+ network information about blocks and not transactions or addresses. */
tr("Outbound Block Relay: does not relay transactions or addresses"),
+ /*: Explanatory text for an outbound peer connection that was
+ established manually through one of several methods. The numbered
+ arguments are stand-ins for the methods available to establish
+ manual connections. */
tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options")
.arg("addnode")
.arg(QString(nonbreaking_hyphen) + "addnode")
.arg(QString(nonbreaking_hyphen) + "connect"),
+ /*: Explanatory text for a short-lived outbound peer connection that
+ is used to test the aliveness of known addresses. */
tr("Outbound Feeler: short-lived, for testing addresses"),
+ /*: Explanatory text for a short-lived outbound peer connection that is used
+ to request addresses from a peer. */
tr("Outbound Address Fetch: short-lived, for soliciting addresses")};
const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list));
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index ff53d8160f..2718392940 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -399,9 +399,10 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
- SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
- confirmationDialog.exec();
- QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
+ auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
+ confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
+ // TODO: Replace QDialog::exec() with safer QDialog::show().
+ const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
if(retval != QMessageBox::Yes)
{
@@ -914,9 +915,9 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
// Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog::coinControlButtonClicked()
{
- CoinControlDialog dlg(*m_coin_control, model, platformStyle);
- dlg.exec();
- coinControlUpdateLabels();
+ auto dlg = new CoinControlDialog(*m_coin_control, model, platformStyle);
+ connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
// Coin Control: checkbox custom change address
diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h
index 01135cdfef..c23afcab24 100644
--- a/src/qt/sendcoinsrecipient.h
+++ b/src/qt/sendcoinsrecipient.h
@@ -9,7 +9,7 @@
#include <config/bitcoin-config.h>
#endif
-#include <amount.h>
+#include <consensus/amount.h>
#include <serialize.h>
#include <string>
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 884ed25637..55d00bb37e 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -9,7 +9,6 @@
#include <interfaces/init.h>
#include <interfaces/node.h>
#include <qt/bitcoin.h>
-#include <qt/initexecutor.h>
#include <qt/test/apptests.h>
#include <qt/test/rpcnestedtests.h>
#include <qt/test/uritests.h>
diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h
index 09bc9e75db..270b8ef78c 100644
--- a/src/qt/transactionfilterproxy.h
+++ b/src/qt/transactionfilterproxy.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_TRANSACTIONFILTERPROXY_H
#define BITCOIN_QT_TRANSACTIONFILTERPROXY_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <QDateTime>
#include <QSortFilterProxyModel>
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index e10243a28a..fb88ca424f 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_TRANSACTIONRECORD_H
#define BITCOIN_QT_TRANSACTIONRECORD_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <uint256.h>
#include <QList>
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 2f16e6edb4..653f3dda6d 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -222,17 +222,21 @@ void TransactionView::setModel(WalletModel *_model)
{
// Add third party transaction URLs to context menu
QStringList listUrls = GUIUtil::SplitSkipEmptyParts(_model->getOptionsModel()->getThirdPartyTxUrls(), "|");
+ bool actions_created = false;
for (int i = 0; i < listUrls.size(); ++i)
{
QString url = listUrls[i].trimmed();
QString host = QUrl(url, QUrl::StrictMode).host();
if (!host.isEmpty())
{
- QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label
- if (i == 0)
+ if (!actions_created) {
contextMenu->addSeparator();
- contextMenu->addAction(thirdPartyTxUrlAction);
- connect(thirdPartyTxUrlAction, &QAction::triggered, [this, url] { openThirdPartyTxUrl(url); });
+ actions_created = true;
+ }
+ /*: Transactions table context menu action to show the
+ selected transaction in a third-party block explorer.
+ %1 is a stand-in argument for the URL of the explorer. */
+ contextMenu->addAction(tr("Show in %1").arg(host), [this, url] { openThirdPartyTxUrl(url); });
}
}
}
@@ -500,22 +504,22 @@ void TransactionView::editLabel()
// Determine type of address, launch appropriate editor dialog type
QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
- EditAddressDialog dlg(
+ auto dlg = new EditAddressDialog(
type == AddressTableModel::Receive
? EditAddressDialog::EditReceivingAddress
: EditAddressDialog::EditSendingAddress, this);
- dlg.setModel(addressBook);
- dlg.loadRow(idx);
- dlg.exec();
+ dlg->setModel(addressBook);
+ dlg->loadRow(idx);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
else
{
// Add sending address
- EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
+ auto dlg = new EditAddressDialog(EditAddressDialog::NewSendingAddress,
this);
- dlg.setModel(addressBook);
- dlg.setAddress(address);
- dlg.exec();
+ dlg->setModel(addressBook);
+ dlg->setAddress(address);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
}
}
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 3cceb5ca5a..a0ad59f12a 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -41,10 +41,6 @@ WalletController::WalletController(ClientModel& client_model, const PlatformStyl
getOrCreateWallet(std::move(wallet));
});
- for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.walletClient().getWallets()) {
- getOrCreateWallet(std::move(wallet));
- }
-
m_activity_worker->moveToThread(m_activity_thread);
m_activity_thread->start();
QTimer::singleShot(0, m_activity_worker, []() {
@@ -61,12 +57,6 @@ WalletController::~WalletController()
delete m_activity_worker;
}
-std::vector<WalletModel*> WalletController::getOpenWallets() const
-{
- QMutexLocker locker(&m_mutex);
- return m_wallets;
-}
-
std::map<std::string, bool> WalletController::listWalletDir() const
{
QMutexLocker locker(&m_mutex);
@@ -191,33 +181,24 @@ WalletControllerActivity::WalletControllerActivity(WalletController* wallet_cont
, m_wallet_controller(wallet_controller)
, m_parent_widget(parent_widget)
{
+ connect(this, &WalletControllerActivity::finished, this, &QObject::deleteLater);
}
-WalletControllerActivity::~WalletControllerActivity()
+void WalletControllerActivity::showProgressDialog(const QString& title_text, const QString& label_text)
{
- delete m_progress_dialog;
-}
-
-void WalletControllerActivity::showProgressDialog(const QString& label_text)
-{
- assert(!m_progress_dialog);
- m_progress_dialog = new QProgressDialog(m_parent_widget);
-
- m_progress_dialog->setLabelText(label_text);
- m_progress_dialog->setRange(0, 0);
- m_progress_dialog->setCancelButton(nullptr);
- m_progress_dialog->setWindowModality(Qt::ApplicationModal);
- GUIUtil::PolishProgressDialog(m_progress_dialog);
+ auto progress_dialog = new QProgressDialog(m_parent_widget);
+ progress_dialog->setAttribute(Qt::WA_DeleteOnClose);
+ connect(this, &WalletControllerActivity::finished, progress_dialog, &QWidget::close);
+
+ progress_dialog->setWindowTitle(title_text);
+ progress_dialog->setLabelText(label_text);
+ progress_dialog->setRange(0, 0);
+ progress_dialog->setCancelButton(nullptr);
+ progress_dialog->setWindowModality(Qt::ApplicationModal);
+ GUIUtil::PolishProgressDialog(progress_dialog);
// The setValue call forces QProgressDialog to start the internal duration estimation.
// See details in https://bugreports.qt.io/browse/QTBUG-47042.
- m_progress_dialog->setValue(0);
-}
-
-void WalletControllerActivity::destroyProgressDialog()
-{
- assert(m_progress_dialog);
- delete m_progress_dialog;
- m_progress_dialog = nullptr;
+ progress_dialog->setValue(0);
}
CreateWalletActivity::CreateWalletActivity(WalletController* wallet_controller, QWidget* parent_widget)
@@ -251,7 +232,12 @@ void CreateWalletActivity::askPassphrase()
void CreateWalletActivity::createWallet()
{
- showProgressDialog(tr("Creating Wallet <b>%1</b>…").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
+ showProgressDialog(
+ //: Title of window indicating the progress of creation of a new wallet.
+ tr("Create Wallet"),
+ /*: Descriptive text of the create wallet progress window which indicates
+ to the user which wallet is currently being created. */
+ tr("Creating Wallet <b>%1</b>…").arg(m_create_wallet_dialog->walletName().toHtmlEscaped()));
std::string name = m_create_wallet_dialog->walletName().toStdString();
uint64_t flags = 0;
@@ -279,8 +265,6 @@ void CreateWalletActivity::createWallet()
void CreateWalletActivity::finish()
{
- destroyProgressDialog();
-
if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
@@ -329,8 +313,6 @@ OpenWalletActivity::OpenWalletActivity(WalletController* wallet_controller, QWid
void OpenWalletActivity::finish()
{
- destroyProgressDialog();
-
if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
@@ -346,7 +328,12 @@ void OpenWalletActivity::open(const std::string& path)
{
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
- showProgressDialog(tr("Opening Wallet <b>%1</b>…").arg(name.toHtmlEscaped()));
+ showProgressDialog(
+ //: Title of window indicating the progress of opening of a wallet.
+ tr("Open Wallet"),
+ /*: Descriptive text of the open wallet progress window which indicates
+ to the user which wallet is currently being opened. */
+ tr("Opening Wallet <b>%1</b>…").arg(name.toHtmlEscaped()));
QTimer::singleShot(0, worker(), [this, path] {
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message);
@@ -356,3 +343,26 @@ void OpenWalletActivity::open(const std::string& path)
QTimer::singleShot(0, this, &OpenWalletActivity::finish);
});
}
+
+LoadWalletsActivity::LoadWalletsActivity(WalletController* wallet_controller, QWidget* parent_widget)
+ : WalletControllerActivity(wallet_controller, parent_widget)
+{
+}
+
+void LoadWalletsActivity::load()
+{
+ showProgressDialog(
+ //: Title of progress window which is displayed when wallets are being loaded.
+ tr("Load Wallets"),
+ /*: Descriptive text of the load wallets progress window which indicates to
+ the user that wallets are currently being loaded.*/
+ tr("Loading wallets…"));
+
+ QTimer::singleShot(0, worker(), [this] {
+ for (auto& wallet : node().walletClient().getWallets()) {
+ m_wallet_controller->getOrCreateWallet(std::move(wallet));
+ }
+
+ QTimer::singleShot(0, this, [this] { Q_EMIT finished(); });
+ });
+}
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index f7e366878d..bbd990228f 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -52,9 +52,6 @@ public:
WalletController(ClientModel& client_model, const PlatformStyle* platform_style, QObject* parent);
~WalletController();
- //! Returns wallet models currently open.
- std::vector<WalletModel*> getOpenWallets() const;
-
WalletModel* getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet);
//! Returns all wallet names in the wallet dir mapped to whether the wallet
@@ -90,7 +87,7 @@ class WalletControllerActivity : public QObject
public:
WalletControllerActivity(WalletController* wallet_controller, QWidget* parent_widget);
- virtual ~WalletControllerActivity();
+ virtual ~WalletControllerActivity() = default;
Q_SIGNALS:
void finished();
@@ -99,12 +96,10 @@ protected:
interfaces::Node& node() const { return m_wallet_controller->m_node; }
QObject* worker() const { return m_wallet_controller->m_activity_worker; }
- void showProgressDialog(const QString& label_text);
- void destroyProgressDialog();
+ void showProgressDialog(const QString& title_text, const QString& label_text);
WalletController* const m_wallet_controller;
QWidget* const m_parent_widget;
- QProgressDialog* m_progress_dialog{nullptr};
WalletModel* m_wallet_model{nullptr};
bilingual_str m_error_message;
std::vector<bilingual_str> m_warning_message;
@@ -150,4 +145,14 @@ private:
void finish();
};
+class LoadWalletsActivity : public WalletControllerActivity
+{
+ Q_OBJECT
+
+public:
+ LoadWalletsActivity(WalletController* wallet_controller, QWidget* parent_widget);
+
+ void load();
+};
+
#endif // BITCOIN_QT_WALLETCONTROLLER_H
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 5eeb2d5308..4ff92bf82c 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -221,10 +221,9 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
return;
}
- PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
+ auto dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
dlg->openWithPSBT(psbtx);
- dlg->setAttribute(Qt::WA_DeleteOnClose);
- dlg->exec();
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void WalletFrame::encryptWallet()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 967dd588b4..052453cf65 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -506,9 +506,10 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
questionString.append(tr("Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy."));
}
- SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
- confirmationDialog.exec();
- QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
+ auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString);
+ confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
+ // TODO: Replace QDialog::exec() with safer QDialog::show().
+ const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
// cancel sign&broadcast if user doesn't want to bump the fee
if (retval != QMessageBox::Yes) {
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 120d240d91..0bae4bade3 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -8,7 +8,7 @@
#include <primitives/transaction.h>
#include <qt/sendcoinsrecipient.h>
-#include <amount.h>
+#include <consensus/amount.h>
#include <QObject>
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 309806a1c4..7813b89e41 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -205,11 +205,10 @@ void WalletView::showOutOfSyncWarning(bool fShow)
void WalletView::encryptWallet()
{
- AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this);
- dlg.setModel(walletModel);
- dlg.exec();
-
- Q_EMIT encryptionStatusChanged();
+ auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, this);
+ dlg->setModel(walletModel);
+ connect(dlg, &QDialog::finished, this, &WalletView::encryptionStatusChanged);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void WalletView::backupWallet()
@@ -234,19 +233,18 @@ void WalletView::backupWallet()
void WalletView::changePassphrase()
{
- AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
- dlg.setModel(walletModel);
- dlg.exec();
+ auto dlg = new AskPassphraseDialog(AskPassphraseDialog::ChangePass, this);
+ dlg->setModel(walletModel);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void WalletView::unlockWallet()
{
// Unlock wallet when requested by wallet model
- if (walletModel->getEncryptionStatus() == WalletModel::Locked)
- {
- AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
- dlg.setModel(walletModel);
- dlg.exec();
+ if (walletModel->getEncryptionStatus() == WalletModel::Locked) {
+ auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Unlock, this);
+ dlg->setModel(walletModel);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index eebc163624..86a835c484 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_WALLETVIEW_H
#define BITCOIN_QT_WALLETVIEW_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <QStackedWidget>
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index fa2a3a0607..bf23ea4a12 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -53,7 +53,7 @@
#include <sys/vmmeter.h>
#endif
#endif
-#if defined(HAVE_STRONG_GETAUXVAL) || defined(HAVE_WEAK_GETAUXVAL)
+#if defined(HAVE_STRONG_GETAUXVAL)
#include <sys/auxv.h>
#endif
@@ -326,7 +326,7 @@ void RandAddStaticEnv(CSHA512& hasher)
// Bitcoin client version
hasher << CLIENT_VERSION;
-#if defined(HAVE_STRONG_GETAUXVAL) || defined(HAVE_WEAK_GETAUXVAL)
+#if defined(HAVE_STRONG_GETAUXVAL)
// Information available through getauxval()
# ifdef AT_HWCAP
hasher << getauxval(AT_HWCAP);
@@ -346,7 +346,7 @@ void RandAddStaticEnv(CSHA512& hasher)
const char* exec_str = (const char*)getauxval(AT_EXECFN);
if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1);
# endif
-#endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL
+#endif // HAVE_STRONG_GETAUXVAL
#ifdef HAVE_GETCPUID
AddAllCPUID(hasher);
diff --git a/src/rest.cpp b/src/rest.cpp
index e50ab33e54..f6e34c2d81 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -189,9 +189,10 @@ static bool rest_headers(const std::any& context,
if (path.size() != 2)
return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
- long count = strtol(path[0].c_str(), nullptr, 10);
- if (count < 1 || count > 2000)
+ const auto parsed_count{ToIntegral<size_t>(path[0])};
+ if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > 2000) {
return RESTERR(req, HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
+ }
std::string hashStr = path[1];
uint256 hash;
@@ -199,8 +200,8 @@ static bool rest_headers(const std::any& context,
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
const CBlockIndex* tip = nullptr;
- std::vector<const CBlockIndex *> headers;
- headers.reserve(count);
+ std::vector<const CBlockIndex*> headers;
+ headers.reserve(*parsed_count);
{
ChainstateManager* maybe_chainman = GetChainman(context, req);
if (!maybe_chainman) return false;
@@ -211,8 +212,9 @@ static bool rest_headers(const std::any& context,
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
while (pindex != nullptr && active_chain.Contains(pindex)) {
headers.push_back(pindex);
- if (headers.size() == (unsigned long)count)
+ if (headers.size() == *parsed_count) {
break;
+ }
pindex = active_chain.Next(pindex);
}
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 909019d796..ac746de32f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -5,11 +5,11 @@
#include <rpc/blockchain.h>
-#include <amount.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
@@ -1115,11 +1115,11 @@ static RPCHelpMan gettxoutsetinfo()
{RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
{RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
{RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
- {RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
- {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
+ {RPCResult::Type::NUM, "transactions", /* optional */ true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
+ {RPCResult::Type::NUM, "disk_size", /* optional */ true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
- {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
- {RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
+ {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", /* optional */ true, "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
+ {RPCResult::Type::OBJ, "block_info", /* optional */ true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
{
{RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
{RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
@@ -1256,11 +1256,8 @@ static RPCHelpMan gettxout()
{RPCResult::Type::OBJ, "scriptPubKey", "", {
{RPCResult::Type::STR, "asm", ""},
{RPCResult::Type::STR_HEX, "hex", ""},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {{RPCResult::Type::STR, "address", "bitcoin address"}}},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
{RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
}},
@@ -1436,32 +1433,32 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
{RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
{RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
- {RPCResult::Type::NUM, "pruneheight", "lowest-height complete block stored (only present if pruning is enabled)"},
- {RPCResult::Type::BOOL, "automatic_pruning", "whether automatic pruning is enabled (only present if pruning is enabled)"},
- {RPCResult::Type::NUM, "prune_target_size", "the target size used by pruning (only present if automatic pruning is enabled)"},
+ {RPCResult::Type::NUM, "pruneheight", /* optional */ true, "lowest-height complete block stored (only present if pruning is enabled)"},
+ {RPCResult::Type::BOOL, "automatic_pruning", /* optional */ true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
+ {RPCResult::Type::NUM, "prune_target_size", /* optional */ true, "the target size used by pruning (only present if automatic pruning is enabled)"},
{RPCResult::Type::OBJ_DYN, "softforks", "status of softforks",
{
{RPCResult::Type::OBJ, "xxxx", "name of the softfork",
{
{RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
- {RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)",
+ {RPCResult::Type::OBJ, "bip9", /* optional */ true, "status of bip9 softforks (only for \"bip9\" type)",
{
{RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
+ {RPCResult::Type::NUM, "bit", /* optional */ true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
{RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
+ {RPCResult::Type::OBJ, "statistics", /* optional */ true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
{
{RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
- {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "threshold", /* optional */ true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
{RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
{RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
+ {RPCResult::Type::BOOL, "possible", /* optional */ true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
}},
}},
- {RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
+ {RPCResult::Type::NUM, "height", /* optional */ true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
{RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
}},
}},
@@ -1503,7 +1500,7 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("pruneheight", block->nHeight);
// if 0, execution bypasses the whole if block.
- bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1);
+ bool automatic_pruning = (gArgs.GetIntArg("-prune", 0) != 1);
obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
obj.pushKV("prune_target_size", nPruneTarget);
@@ -1650,7 +1647,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
- size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ size_t maxmempool = gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
@@ -1933,16 +1930,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
}
}
-void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
-{
- ScriptPubKeyToUniv(scriptPubKey, out, fIncludeHex, IsDeprecatedRPCEnabled("addresses"));
-}
-
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
-{
- TxToUniv(tx, hashBlock, IsDeprecatedRPCEnabled("addresses"), entry, include_hex, serialize_flags, txundo);
-}
-
template<typename T>
static inline bool SetHasKeys(const std::set<T>& set) {return false;}
template<typename T, typename Tk, typename... Args>
@@ -1971,11 +1958,11 @@ static RPCHelpMan getblockstats()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::NUM, "avgfee", "Average fee in the block"},
- {RPCResult::Type::NUM, "avgfeerate", "Average feerate (in satoshis per virtual byte)"},
- {RPCResult::Type::NUM, "avgtxsize", "Average transaction size"},
- {RPCResult::Type::STR_HEX, "blockhash", "The block hash (to check for potential reorgs)"},
- {RPCResult::Type::ARR_FIXED, "feerate_percentiles", "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",
+ {RPCResult::Type::NUM, "avgfee", /* optional */ true, "Average fee in the block"},
+ {RPCResult::Type::NUM, "avgfeerate", /* optional */ true, "Average feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "avgtxsize", /* optional */ true, "Average transaction size"},
+ {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash (to check for potential reorgs)"},
+ {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /* optional */ true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",
{
{RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
{RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
@@ -1983,30 +1970,30 @@ static RPCHelpMan getblockstats()
{RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
{RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
}},
- {RPCResult::Type::NUM, "height", "The height of the block"},
- {RPCResult::Type::NUM, "ins", "The number of inputs (excluding coinbase)"},
- {RPCResult::Type::NUM, "maxfee", "Maximum fee in the block"},
- {RPCResult::Type::NUM, "maxfeerate", "Maximum feerate (in satoshis per virtual byte)"},
- {RPCResult::Type::NUM, "maxtxsize", "Maximum transaction size"},
- {RPCResult::Type::NUM, "medianfee", "Truncated median fee in the block"},
- {RPCResult::Type::NUM, "mediantime", "The block median time past"},
- {RPCResult::Type::NUM, "mediantxsize", "Truncated median transaction size"},
- {RPCResult::Type::NUM, "minfee", "Minimum fee in the block"},
- {RPCResult::Type::NUM, "minfeerate", "Minimum feerate (in satoshis per virtual byte)"},
- {RPCResult::Type::NUM, "mintxsize", "Minimum transaction size"},
- {RPCResult::Type::NUM, "outs", "The number of outputs"},
- {RPCResult::Type::NUM, "subsidy", "The block subsidy"},
- {RPCResult::Type::NUM, "swtotal_size", "Total size of all segwit transactions"},
- {RPCResult::Type::NUM, "swtotal_weight", "Total weight of all segwit transactions"},
- {RPCResult::Type::NUM, "swtxs", "The number of segwit transactions"},
- {RPCResult::Type::NUM, "time", "The block time"},
- {RPCResult::Type::NUM, "total_out", "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
- {RPCResult::Type::NUM, "total_size", "Total size of all non-coinbase transactions"},
- {RPCResult::Type::NUM, "total_weight", "Total weight of all non-coinbase transactions"},
- {RPCResult::Type::NUM, "totalfee", "The fee total"},
- {RPCResult::Type::NUM, "txs", "The number of transactions (including coinbase)"},
- {RPCResult::Type::NUM, "utxo_increase", "The increase/decrease in the number of unspent outputs"},
- {RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
+ {RPCResult::Type::NUM, "height", /* optional */ true, "The height of the block"},
+ {RPCResult::Type::NUM, "ins", /* optional */ true, "The number of inputs (excluding coinbase)"},
+ {RPCResult::Type::NUM, "maxfee", /* optional */ true, "Maximum fee in the block"},
+ {RPCResult::Type::NUM, "maxfeerate", /* optional */ true, "Maximum feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "maxtxsize", /* optional */ true, "Maximum transaction size"},
+ {RPCResult::Type::NUM, "medianfee", /* optional */ true, "Truncated median fee in the block"},
+ {RPCResult::Type::NUM, "mediantime", /* optional */ true, "The block median time past"},
+ {RPCResult::Type::NUM, "mediantxsize", /* optional */ true, "Truncated median transaction size"},
+ {RPCResult::Type::NUM, "minfee", /* optional */ true, "Minimum fee in the block"},
+ {RPCResult::Type::NUM, "minfeerate", /* optional */ true, "Minimum feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "mintxsize", /* optional */ true, "Minimum transaction size"},
+ {RPCResult::Type::NUM, "outs", /* optional */ true, "The number of outputs"},
+ {RPCResult::Type::NUM, "subsidy", /* optional */ true, "The block subsidy"},
+ {RPCResult::Type::NUM, "swtotal_size", /* optional */ true, "Total size of all segwit transactions"},
+ {RPCResult::Type::NUM, "swtotal_weight", /* optional */ true, "Total weight of all segwit transactions"},
+ {RPCResult::Type::NUM, "swtxs", /* optional */ true, "The number of segwit transactions"},
+ {RPCResult::Type::NUM, "time", /* optional */ true, "The block time"},
+ {RPCResult::Type::NUM, "total_out", /* optional */ true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
+ {RPCResult::Type::NUM, "total_size", /* optional */ true, "Total size of all non-coinbase transactions"},
+ {RPCResult::Type::NUM, "total_weight", /* optional */ true, "Total weight of all non-coinbase transactions"},
+ {RPCResult::Type::NUM, "totalfee", /* optional */ true, "The fee total"},
+ {RPCResult::Type::NUM, "txs", /* optional */ true, "The number of transactions (including coinbase)"},
+ {RPCResult::Type::NUM, "utxo_increase", /* optional */ true, "The increase/decrease in the number of unspent outputs"},
+ {RPCResult::Type::NUM, "utxo_size_inc", /* optional */ true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
}},
RPCExamples{
HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index ffb6f03b47..09e471afdd 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -5,8 +5,7 @@
#ifndef BITCOIN_RPC_BLOCKCHAIN_H
#define BITCOIN_RPC_BLOCKCHAIN_H
-#include <amount.h>
-#include <core_io.h>
+#include <consensus/amount.h>
#include <streams.h>
#include <sync.h>
@@ -53,9 +52,6 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
/** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
-void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
-
NodeContext& EnsureAnyNodeContext(const std::any& context);
CTxMemPool& EnsureMemPool(const NodeContext& node);
CTxMemPool& EnsureAnyMemPool(const std::any& context);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 4357ab2bb3..93e49cb9a8 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -131,6 +131,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettxoutsetinfo", 2, "use_index"},
{ "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" },
+ { "lockunspent", 2, "persistent" },
{ "send", 0, "outputs" },
{ "send", 1, "conf_target" },
{ "send", 3, "fee_rate"},
@@ -192,6 +193,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "unloadwallet", 1, "load_on_startup"},
{ "getnodeaddresses", 0, "count"},
{ "addpeeraddress", 1, "port"},
+ { "addpeeraddress", 2, "tried"},
{ "stop", 0, "wait" },
};
// clang-format on
diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp
index 6ec2b1a07f..60ec15e904 100644
--- a/src/rpc/external_signer.cpp
+++ b/src/rpc/external_signer.cpp
@@ -24,8 +24,11 @@ static RPCHelpMan enumeratesigners()
{
{RPCResult::Type::ARR, "signers", /* optional */ false, "",
{
- {RPCResult::Type::STR_HEX, "masterkeyfingerprint", "Master key fingerprint"},
- {RPCResult::Type::STR, "name", "Device name"},
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "fingerprint", "Master key fingerprint"},
+ {RPCResult::Type::STR, "name", "Device name"},
+ }},
},
}
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 27cbb3a702..518c41d12a 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -3,9 +3,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
#include <chain.h>
#include <chainparams.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/params.h>
#include <consensus/validation.h>
@@ -553,6 +553,10 @@ static RPCHelpMan getblocktemplate()
{
{RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
}},
+ {RPCResult::Type::ARR, "capabilities", "",
+ {
+ {RPCResult::Type::STR, "value", "A supported feature, for example 'proposal'"},
+ }},
{RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
{RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
{RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
@@ -586,11 +590,12 @@ static RPCHelpMan getblocktemplate()
{RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
{RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
{RPCResult::Type::NUM, "sizelimit", "limit of block size"},
- {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
+ {RPCResult::Type::NUM, "weightlimit", /* optional */ true, "limit of block weight"},
{RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
{RPCResult::Type::STR, "bits", "compressed target of next block"},
{RPCResult::Type::NUM, "height", "The height of the next block"},
- {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"},
+ {RPCResult::Type::STR_HEX, "signet_challenge", /* optional */ true, "Only on signet"},
+ {RPCResult::Type::STR_HEX, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"},
}},
},
RPCExamples{
@@ -697,7 +702,7 @@ static RPCHelpMan getblocktemplate()
std::string lpstr = lpval.get_str();
hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid");
- nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
+ nTransactionsUpdatedLastLP = LocaleIndependentAtoi<int64_t>(lpstr.substr(64));
}
else
{
@@ -1098,6 +1103,8 @@ static RPCHelpMan estimatesmartfee()
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
+ const NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
@@ -1113,7 +1120,10 @@ static RPCHelpMan estimatesmartfee()
UniValue result(UniValue::VOBJ);
UniValue errors(UniValue::VARR);
FeeCalculation feeCalc;
- CFeeRate feeRate = fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative);
+ CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)};
+ CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)};
+ CFeeRate min_relay_feerate{::minRelayTxFee};
+ feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate});
if (feeRate != CFeeRate(0)) {
result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
} else {
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 14b0e5a984..39bd9c6091 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -22,6 +22,7 @@
#include <util/check.h>
#include <util/message.h> // For MessageSign(), MessageVerify()
#include <util/strencodings.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <optional>
@@ -44,10 +45,10 @@ static RPCHelpMan validateaddress()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
- {RPCResult::Type::STR, "address", "The bitcoin address validated"},
- {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"},
- {RPCResult::Type::BOOL, "isscript", "If the key is a script"},
- {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address validated"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", /* optional */ true, "The hex-encoded scriptPubKey generated by the address"},
+ {RPCResult::Type::BOOL, "isscript", /* optional */ true, "If the key is a script"},
+ {RPCResult::Type::BOOL, "iswitness", /* optional */ true, "If the address is a witness address"},
{RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"},
{RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"},
{RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"},
@@ -417,6 +418,27 @@ static RPCHelpMan setmocktime()
};
}
+#if defined(USE_SYSCALL_SANDBOX)
+static RPCHelpMan invokedisallowedsyscall()
+{
+ return RPCHelpMan{
+ "invokedisallowedsyscall",
+ "\nInvoke a disallowed syscall to trigger a syscall sandbox violation. Used for testing purposes.\n",
+ {},
+ RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCExamples{
+ HelpExampleCli("invokedisallowedsyscall", "") + HelpExampleRpc("invokedisallowedsyscall", "")},
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ if (!Params().IsTestChain()) {
+ throw std::runtime_error("invokedisallowedsyscall is used for testing only.");
+ }
+ TestDisallowedSandboxCall();
+ return NullUniValue;
+ },
+ };
+}
+#endif // USE_SYSCALL_SANDBOX
+
static RPCHelpMan mockscheduler()
{
return RPCHelpMan{"mockscheduler",
@@ -717,7 +739,7 @@ static RPCHelpMan getindexinfo()
{"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."},
},
RPCResult{
- RPCResult::Type::OBJ, "", "", {
+ RPCResult::Type::OBJ_DYN, "", "", {
{
RPCResult::Type::OBJ, "name", "The name of the index",
{
@@ -777,6 +799,9 @@ static const CRPCCommand commands[] =
{ "hidden", &echo, },
{ "hidden", &echojson, },
{ "hidden", &echoipc, },
+#if defined(USE_SYSCALL_SANDBOX)
+ { "hidden", &invokedisallowedsyscall, },
+#endif // USE_SYSCALL_SANDBOX
};
// clang-format on
for (const auto& c : commands) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 0f554ec5e7..a9bee33c5d 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -116,10 +116,10 @@ static RPCHelpMan getpeerinfo()
{
{RPCResult::Type::NUM, "id", "Peer index"},
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
- {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
- {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
+ {RPCResult::Type::STR, "addrbind", /* optional */ true, "(ip:port) Bind address of the connection to the peer"},
+ {RPCResult::Type::STR, "addrlocal", /* optional */ true, "(ip:port) Local address as reported by the peer"},
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"},
- {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
+ {RPCResult::Type::NUM, "mapped_as", /* optional */ true, "The AS in the BGP route to the peer used for diversifying\n"
"peer selection (only available if the asmap config flag is set)"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
{RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
@@ -135,9 +135,9 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
{RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
{RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"},
- {RPCResult::Type::NUM, "pingtime", "ping time (if available)"},
- {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"},
- {RPCResult::Type::NUM, "pingwait", "ping wait (if non-zero)"},
+ {RPCResult::Type::NUM, "pingtime", /* optional */ true, "ping time (if available)"},
+ {RPCResult::Type::NUM, "minping", /* optional */ true, "minimum observed ping time (if any at all)"},
+ {RPCResult::Type::NUM, "pingwait", /* optional */ true, "ping wait (if non-zero)"},
{RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
{RPCResult::Type::STR, "subver", "The string version"},
{RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
@@ -921,6 +921,7 @@ static RPCHelpMan addpeeraddress()
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address of the peer"},
{"port", RPCArg::Type::NUM, RPCArg::Optional::NO, "The port of the peer"},
+ {"tried", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, attempt to add the peer to the tried addresses table"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -929,8 +930,8 @@ static RPCHelpMan addpeeraddress()
},
},
RPCExamples{
- HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333")
- + HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333")
+ HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333 true")
+ + HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333, true")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@@ -941,6 +942,7 @@ static RPCHelpMan addpeeraddress()
const std::string& addr_string{request.params[0].get_str()};
const uint16_t port{static_cast<uint16_t>(request.params[1].get_int())};
+ const bool tried{request.params[2].isTrue()};
UniValue obj(UniValue::VOBJ);
CNetAddr net_addr;
@@ -951,7 +953,13 @@ static RPCHelpMan addpeeraddress()
address.nTime = GetAdjustedTime();
// The source address is set equal to the address. This is equivalent to the peer
// announcing itself.
- if (node.addrman->Add({address}, address)) success = true;
+ if (node.addrman->Add({address}, address)) {
+ success = true;
+ if (tried) {
+ // Attempt to move the address to the tried addresses table.
+ node.addrman->Good(address);
+ }
+ }
}
obj.pushKV("success", success);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 00e77d89e5..483717aa7a 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -5,6 +5,7 @@
#include <chain.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
@@ -94,7 +95,7 @@ static RPCHelpMan getrawtransaction()
RPCResult{"if verbose is set to true",
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "in_active_chain", "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"},
+ {RPCResult::Type::BOOL, "in_active_chain", /* optional */ true, "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"},
{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"},
{RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"},
{RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"},
@@ -115,7 +116,7 @@ static RPCHelpMan getrawtransaction()
{RPCResult::Type::STR_HEX, "hex", "hex"},
}},
{RPCResult::Type::NUM, "sequence", "The script sequence number"},
- {RPCResult::Type::ARR, "txinwitness", "",
+ {RPCResult::Type::ARR, "txinwitness", /* optional */ true, "",
{
{RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
}},
@@ -131,20 +132,15 @@ static RPCHelpMan getrawtransaction()
{
{RPCResult::Type::STR, "asm", "the asm"},
{RPCResult::Type::STR, "hex", "the hex"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "bitcoin address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
}},
}},
- {RPCResult::Type::STR_HEX, "blockhash", "the block hash"},
- {RPCResult::Type::NUM, "confirmations", "The confirmations"},
- {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::NUM, "time", "Same as \"blocktime\""},
+ {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "the block hash"},
+ {RPCResult::Type::NUM, "confirmations", /* optional */ true, "The confirmations"},
+ {RPCResult::Type::NUM_TIME, "blocktime", /* optional */ true, "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM, "time", /* optional */ true, "Same as \"blocktime\""},
}
},
},
@@ -470,14 +466,15 @@ static RPCHelpMan decoderawtransaction()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
- {RPCResult::Type::NUM, "vout", "The output number"},
- {RPCResult::Type::OBJ, "scriptSig", "The script",
+ {RPCResult::Type::STR_HEX, "coinbase", /* optional */ true, ""},
+ {RPCResult::Type::STR_HEX, "txid", /* optional */ true, "The transaction id"},
+ {RPCResult::Type::NUM, "vout", /* optional */ true, "The output number"},
+ {RPCResult::Type::OBJ, "scriptSig", /* optional */ true, "The script",
{
{RPCResult::Type::STR, "asm", "asm"},
{RPCResult::Type::STR_HEX, "hex", "hex"},
}},
- {RPCResult::Type::ARR, "txinwitness", "",
+ {RPCResult::Type::ARR, "txinwitness", /* optional */ true, "",
{
{RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
}},
@@ -494,13 +491,8 @@ static RPCHelpMan decoderawtransaction()
{
{RPCResult::Type::STR, "asm", "the asm"},
{RPCResult::Type::STR_HEX, "hex", "the hex"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "bitcoin address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
}},
}},
@@ -553,24 +545,14 @@ static RPCHelpMan decodescript()
{
{RPCResult::Type::STR, "asm", "Script public key"},
{RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "bitcoin address"},
- }},
- {RPCResult::Type::STR, "p2sh", "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
- {RPCResult::Type::OBJ, "segwit", "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
+ {RPCResult::Type::STR, "p2sh", /* optional */ true, "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
+ {RPCResult::Type::OBJ, "segwit", /* optional */ true, "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
{
{RPCResult::Type::STR, "asm", "String representation of the script public key"},
{RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
{RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "segwit address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
{RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"},
}},
}
@@ -591,7 +573,7 @@ static RPCHelpMan decodescript()
} else {
// Empty scripts are valid
}
- ScriptPubKeyToUniv(script, r, /* fIncludeHex */ false);
+ ScriptPubKeyToUniv(script, r, /* include_hex */ false);
UniValue type;
type = find_value(r, "type");
@@ -625,7 +607,7 @@ static RPCHelpMan decodescript()
// Newer segwit program versions should be considered when then become available.
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
- ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true);
+ ScriptPubKeyToUniv(segwitScr, sr, /* include_hex */ true);
sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr)));
r.pushKV("segwit", sr);
}
@@ -772,6 +754,10 @@ static RPCHelpMan signrawtransactionwithkey()
{
{RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
{RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::ARR, "witness", "",
+ {
+ {RPCResult::Type::STR_HEX, "witness", ""},
+ }},
{RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
{RPCResult::Type::NUM, "sequence", "Script sequence number"},
{RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
@@ -909,15 +895,15 @@ static RPCHelpMan testmempoolaccept()
{
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
- {RPCResult::Type::STR, "package-error", "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
- {RPCResult::Type::BOOL, "allowed", "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate."
+ {RPCResult::Type::STR, "package-error", /* optional */ true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
+ {RPCResult::Type::BOOL, "allowed", /* optional */ true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
"If not present, the tx was not fully validated due to a failure in another tx in the list."},
- {RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
- {RPCResult::Type::OBJ, "fees", "Transaction fees (only present if 'allowed' is true)",
+ {RPCResult::Type::NUM, "vsize", /* optional */ true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
+ {RPCResult::Type::OBJ, "fees", /* optional */ true, "Transaction fees (only present if 'allowed' is true)",
{
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
}},
- {RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
+ {RPCResult::Type::STR, "reject-reason", /* optional */ true, "Rejection string (only present when 'allowed' is false)"},
}},
}
},
@@ -1056,7 +1042,7 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR, "asm", "The asm"},
{RPCResult::Type::STR_HEX, "hex", "The hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address"," Bitcoin address if there is one"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
}},
{RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "",
@@ -1078,22 +1064,23 @@ static RPCHelpMan decodepsbt()
}},
{RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "",
{
- {RPCResult::Type::OBJ, "pubkey", /* optional */ true, "The public key with the derivation path as the value.",
+ {RPCResult::Type::OBJ, "", "",
{
+ {RPCResult::Type::STR, "pubkey", "The public key with the derivation path as the value."},
{RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"},
{RPCResult::Type::STR, "path", "The path"},
}},
}},
- {RPCResult::Type::OBJ, "final_scriptsig", /* optional */ true, "",
+ {RPCResult::Type::OBJ, "final_scriptSig", /* optional */ true, "",
{
{RPCResult::Type::STR, "asm", "The asm"},
{RPCResult::Type::STR, "hex", "The hex"},
}},
- {RPCResult::Type::ARR, "final_scriptwitness", "",
+ {RPCResult::Type::ARR, "final_scriptwitness", /* optional */ true, "",
{
{RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
}},
- {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {RPCResult::Type::OBJ_DYN, "unknown", /* optional */ true, "The unknown global fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
@@ -1124,7 +1111,7 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR, "path", "The path"},
}},
}},
- {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {RPCResult::Type::OBJ_DYN, "unknown", /* optional */ true, "The unknown global fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
@@ -1175,7 +1162,7 @@ static RPCHelpMan decodepsbt()
txout = input.witness_utxo;
UniValue o(UniValue::VOBJ);
- ScriptToUniv(txout.scriptPubKey, o, true);
+ ScriptPubKeyToUniv(txout.scriptPubKey, o, /* include_hex */ true);
UniValue out(UniValue::VOBJ);
out.pushKV("amount", ValueFromAmount(txout.nValue));
@@ -1222,12 +1209,12 @@ static RPCHelpMan decodepsbt()
// Redeem script and witness script
if (!input.redeem_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(input.redeem_script, r, false);
+ ScriptToUniv(input.redeem_script, r);
in.pushKV("redeem_script", r);
}
if (!input.witness_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(input.witness_script, r, false);
+ ScriptToUniv(input.witness_script, r);
in.pushKV("witness_script", r);
}
@@ -1282,12 +1269,12 @@ static RPCHelpMan decodepsbt()
// Redeem script and witness script
if (!output.redeem_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(output.redeem_script, r, false);
+ ScriptToUniv(output.redeem_script, r);
out.pushKV("redeem_script", r);
}
if (!output.witness_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(output.witness_script, r, false);
+ ScriptToUniv(output.witness_script, r);
out.pushKV("witness_script", r);
}
@@ -1398,8 +1385,8 @@ static RPCHelpMan finalizepsbt()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction if not extracted"},
- {RPCResult::Type::STR_HEX, "hex", "The hex-encoded network transaction if extracted"},
+ {RPCResult::Type::STR, "psbt", /* optional */ true, "The base64-encoded partially signed transaction if not extracted"},
+ {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The hex-encoded network transaction if extracted"},
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
}
},
@@ -1791,7 +1778,7 @@ static RPCHelpMan analyzepsbt()
RPCResult {
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::ARR, "inputs", "",
+ {RPCResult::Type::ARR, "inputs", /* optional */ true, "",
{
{RPCResult::Type::OBJ, "", "",
{
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index f21eddf56c..d550160260 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -6,6 +6,7 @@
#include <rpc/rawtransaction_util.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <core_io.h>
#include <key_io.h>
#include <policy/policy.h>
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index cf80b08b96..0d02ec5c47 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -540,7 +540,7 @@ void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nS
int RPCSerializationFlags()
{
int flag = 0;
- if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
+ if (gArgs.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
return flag;
}
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 03967020c2..e6bb35fc33 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_RPC_SERVER_H
#define BITCOIN_RPC_SERVER_H
-#include <amount.h>
#include <rpc/request.h>
#include <rpc/util.h>
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 2059628b54..2d7f5f2894 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <key_io.h>
#include <outputtype.h>
#include <rpc/util.h>
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 02ada969a4..162cced6c7 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -5,6 +5,7 @@
#include <scheduler.h>
#include <random.h>
+#include <util/syscall_sandbox.h>
#include <util/time.h>
#include <assert.h>
@@ -24,6 +25,7 @@ CScheduler::~CScheduler()
void CScheduler::serviceQueue()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::SCHEDULER);
WAIT_LOCK(newTaskMutex, lock);
++nThreadsServicingQueue;
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 65867c1c14..6f911f4fe7 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -96,7 +96,7 @@ void InitSignatureCache()
{
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
- size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index b912b00365..4cb2125747 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -5,6 +5,7 @@
#include <script/sign.h>
+#include <consensus/amount.h>
#include <key.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 67a79a157c..d9656c781d 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -266,47 +266,6 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
assert(false);
}
-// TODO: from v23 ("addresses" and "reqSigs" deprecated) "ExtractDestinations" should be removed
-bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
-{
- addressRet.clear();
- std::vector<valtype> vSolutions;
- typeRet = Solver(scriptPubKey, vSolutions);
- if (typeRet == TxoutType::NONSTANDARD) {
- return false;
- } else if (typeRet == TxoutType::NULL_DATA) {
- // This is data, not addresses
- return false;
- }
-
- if (typeRet == TxoutType::MULTISIG)
- {
- nRequiredRet = vSolutions.front()[0];
- for (unsigned int i = 1; i < vSolutions.size()-1; i++)
- {
- CPubKey pubKey(vSolutions[i]);
- if (!pubKey.IsValid())
- continue;
-
- CTxDestination address = PKHash(pubKey);
- addressRet.push_back(address);
- }
-
- if (addressRet.empty())
- return false;
- }
- else
- {
- nRequiredRet = 1;
- CTxDestination address;
- if (!ExtractDestination(scriptPubKey, address))
- return false;
- addressRet.push_back(address);
- }
-
- return true;
-}
-
namespace {
class CScriptVisitor
{
diff --git a/src/script/standard.h b/src/script/standard.h
index 78492733db..a8e57231bf 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -176,28 +176,12 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
/**
* Parse a standard scriptPubKey for the destination address. Assigns result to
- * the addressRet parameter and returns true if successful. For multisig
- * scripts, instead use ExtractDestinations. Currently only works for P2PK,
+ * the addressRet parameter and returns true if successful. Currently only works for P2PK,
* P2PKH, P2SH, P2WPKH, and P2WSH scripts.
*/
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
/**
- * Parse a standard scriptPubKey with one or more destination addresses. For
- * multisig scripts, this populates the addressRet vector with the pubkey IDs
- * and nRequiredRet with the n required to spend. For other destinations,
- * addressRet is populated with a single value and nRequiredRet is set to 1.
- * Returns true if successful.
- *
- * Note: this function confuses destinations (a subset of CScripts that are
- * encodable as an address) with key identifiers (of keys involved in a
- * CScript), and its use should be phased out.
- *
- * TODO: from v23 ("addresses" and "reqSigs" deprecated) "ExtractDestinations" should be removed
- */
-bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
-
-/**
* Generate a Bitcoin scriptPubKey for the given CTxDestination. Returns a P2PKH
* script for a CKeyID destination, a P2SH script for a CScriptID, and an empty
* script for CNoDestination.
diff --git a/src/sync.cpp b/src/sync.cpp
index 98e6d3d65d..c9fd8e347e 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -97,27 +97,29 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac
LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
LogPrintf("Previous lock order was:\n");
for (const LockStackItem& i : s1) {
+ std::string prefix{};
if (i.first == mismatch.first) {
- LogPrintf(" (1)"); /* Continued */
+ prefix = " (1)";
}
if (i.first == mismatch.second) {
- LogPrintf(" (2)"); /* Continued */
+ prefix = " (2)";
}
- LogPrintf(" %s\n", i.second.ToString());
+ LogPrintf("%s %s\n", prefix, i.second.ToString());
}
std::string mutex_a, mutex_b;
LogPrintf("Current lock order is:\n");
for (const LockStackItem& i : s2) {
+ std::string prefix{};
if (i.first == mismatch.first) {
- LogPrintf(" (1)"); /* Continued */
+ prefix = " (1)";
mutex_a = i.second.Name();
}
if (i.first == mismatch.second) {
- LogPrintf(" (2)"); /* Continued */
+ prefix = " (2)";
mutex_b = i.second.Name();
}
- LogPrintf(" %s\n", i.second.ToString());
+ LogPrintf("%s %s\n", prefix, i.second.ToString());
}
if (g_debug_lockorder_abort) {
tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order for %s, details in debug log.\n", s2.back().second.ToString());
@@ -131,10 +133,11 @@ static void double_lock_detected(const void* mutex, const LockStack& lock_stack)
LogPrintf("DOUBLE LOCK DETECTED\n");
LogPrintf("Lock order:\n");
for (const LockStackItem& i : lock_stack) {
+ std::string prefix{};
if (i.first == mutex) {
- LogPrintf(" (*)"); /* Continued */
+ prefix = " (*)";
}
- LogPrintf(" %s\n", i.second.ToString());
+ LogPrintf("%s %s\n", prefix, i.second.ToString());
}
if (g_debug_lockorder_abort) {
tfm::format(std::cerr,
diff --git a/src/test/README.md b/src/test/README.md
index 57cda26d7c..d03411c3ed 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -74,3 +74,29 @@ start debugging, just like you would with any other program:
```bash
gdb src/test/test_bitcoin
```
+
+#### Segmentation faults
+
+If you hit a segmentation fault during a test run, you can diagnose where the fault
+is happening by running `gdb ./src/test/test_bitcoin` and then using the `bt` command
+within gdb.
+
+Another tool that can be used to resolve segmentation faults is
+[valgrind](https://valgrind.org/).
+
+If for whatever reason you want to produce a core dump file for this fault, you can do
+that as well. By default, the boost test runner will intercept system errors and not
+produce a core file. To bypass this, add `--catch_system_errors=no` to the
+`test_bitcoin` arguments and ensure that your ulimits are set properly (e.g. `ulimit -c
+unlimited`).
+
+Running the tests and hitting a segmentation fault should now produce a file called `core`
+(on Linux platforms, the file name will likely depend on the contents of
+`/proc/sys/kernel/core_pattern`).
+
+You can then explore the core dump using
+``` bash
+gdb src/test/test_bitcoin core
+
+(gbd) bt # produce a backtrace for where a segfault occurred
+```
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 01a492a20b..bd6f470219 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -4,6 +4,7 @@
#include <addrdb.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <chainparams.h>
#include <clientversion.h>
#include <hash.h>
@@ -21,26 +22,26 @@
using namespace std::literals;
-class CAddrManSerializationMock : public CAddrMan
+class AddrManSerializationMock : public AddrMan
{
public:
virtual void Serialize(CDataStream& s) const = 0;
- CAddrManSerializationMock()
- : CAddrMan(/* asmap */ std::vector<bool>(), /* deterministic */ true, /* consistency_check_ratio */ 100)
+ AddrManSerializationMock()
+ : AddrMan(/* asmap */ std::vector<bool>(), /* deterministic */ true, /* consistency_check_ratio */ 100)
{}
};
-class CAddrManUncorrupted : public CAddrManSerializationMock
+class AddrManUncorrupted : public AddrManSerializationMock
{
public:
void Serialize(CDataStream& s) const override
{
- CAddrMan::Serialize(s);
+ AddrMan::Serialize(s);
}
};
-class CAddrManCorrupted : public CAddrManSerializationMock
+class AddrManCorrupted : public AddrManSerializationMock
{
public:
void Serialize(CDataStream& s) const override
@@ -61,12 +62,12 @@ public:
CAddress addr = CAddress(serv, NODE_NONE);
CNetAddr resolved;
BOOST_CHECK(LookupHost("252.2.2.2", resolved, false));
- CAddrInfo info = CAddrInfo(addr, resolved);
+ AddrInfo info = AddrInfo(addr, resolved);
s << info;
}
};
-static CDataStream AddrmanToStream(const CAddrManSerializationMock& _addrman)
+static CDataStream AddrmanToStream(const AddrManSerializationMock& _addrman)
{
CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
ssPeersIn << Params().MessageStart();
@@ -76,44 +77,44 @@ static CDataStream AddrmanToStream(const CAddrManSerializationMock& _addrman)
return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}
-class CAddrManTest : public CAddrMan
+class AddrManTest : public AddrMan
{
private:
bool deterministic;
public:
- explicit CAddrManTest(bool makeDeterministic = true,
- std::vector<bool> asmap = std::vector<bool>())
- : CAddrMan(asmap, makeDeterministic, /* consistency_check_ratio */ 100)
+ explicit AddrManTest(bool makeDeterministic = true,
+ std::vector<bool> asmap = std::vector<bool>())
+ : AddrMan(asmap, makeDeterministic, /* consistency_check_ratio */ 100)
{
deterministic = makeDeterministic;
}
- CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
+ AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
{
- LOCK(cs);
- return CAddrMan::Find(addr, pnId);
+ LOCK(m_impl->cs);
+ return m_impl->Find(addr, pnId);
}
- CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr)
+ AddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr)
{
- LOCK(cs);
- return CAddrMan::Create(addr, addrSource, pnId);
+ LOCK(m_impl->cs);
+ return m_impl->Create(addr, addrSource, pnId);
}
void Delete(int nId)
{
- LOCK(cs);
- CAddrMan::Delete(nId);
+ LOCK(m_impl->cs);
+ m_impl->Delete(nId);
}
// Used to test deserialization
std::pair<int, int> GetBucketAndEntry(const CAddress& addr)
{
- LOCK(cs);
- int nId = mapAddr[addr];
+ LOCK(m_impl->cs);
+ int nId = m_impl->mapAddr[addr];
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
for (int entry = 0; entry < ADDRMAN_BUCKET_SIZE; ++entry) {
- if (nId == vvNew[bucket][entry]) {
+ if (nId == m_impl->vvNew[bucket][entry]) {
return std::pair<int, int>(bucket, entry);
}
}
@@ -165,20 +166,20 @@ BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(addrman_simple)
{
- auto addrman = std::make_unique<CAddrManTest>();
+ auto addrman = std::make_unique<AddrManTest>();
CNetAddr source = ResolveIP("252.2.2.2");
// Test: Does Addrman respond correctly when empty.
BOOST_CHECK_EQUAL(addrman->size(), 0U);
- CAddrInfo addr_null = addrman->Select();
+ auto addr_null = addrman->Select().first;
BOOST_CHECK_EQUAL(addr_null.ToString(), "[::]:0");
// Test: Does Addrman::Add work as expected.
CService addr1 = ResolveService("250.1.1.1", 8333);
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman->size(), 1U);
- CAddrInfo addr_ret1 = addrman->Select();
+ auto addr_ret1 = addrman->Select().first;
BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
// Test: Does IP address deduplication work correctly.
@@ -199,7 +200,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_CHECK(addrman->size() >= 1);
// Test: reset addrman and test AddrMan::Add multiple addresses works as expected
- addrman = std::make_unique<CAddrManTest>();
+ addrman = std::make_unique<AddrManTest>();
std::vector<CAddress> vAddr;
vAddr.push_back(CAddress(ResolveService("250.1.1.3", 8333), NODE_NONE));
vAddr.push_back(CAddress(ResolveService("250.1.1.4", 8333), NODE_NONE));
@@ -209,7 +210,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple)
BOOST_AUTO_TEST_CASE(addrman_ports)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CNetAddr source = ResolveIP("252.2.2.2");
@@ -223,7 +224,7 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
CService addr1_port = ResolveService("250.1.1.1", 8334);
BOOST_CHECK(!addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
- CAddrInfo addr_ret2 = addrman.Select();
+ auto addr_ret2 = addrman.Select().first;
BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
// Test: Add same IP but diff port to tried table, it doesn't get added.
@@ -231,14 +232,14 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
addrman.Good(CAddress(addr1_port, NODE_NONE));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
bool newOnly = true;
- CAddrInfo addr_ret3 = addrman.Select(newOnly);
+ auto addr_ret3 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_select)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CNetAddr source = ResolveIP("252.2.2.2");
@@ -248,16 +249,16 @@ BOOST_AUTO_TEST_CASE(addrman_select)
BOOST_CHECK_EQUAL(addrman.size(), 1U);
bool newOnly = true;
- CAddrInfo addr_ret1 = addrman.Select(newOnly);
+ auto addr_ret1 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret1.ToString(), "250.1.1.1:8333");
// Test: move addr to tried, select from new expected nothing returned.
addrman.Good(CAddress(addr1, NODE_NONE));
BOOST_CHECK_EQUAL(addrman.size(), 1U);
- CAddrInfo addr_ret2 = addrman.Select(newOnly);
+ auto addr_ret2 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret2.ToString(), "[::]:0");
- CAddrInfo addr_ret3 = addrman.Select();
+ auto addr_ret3 = addrman.Select().first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
BOOST_CHECK_EQUAL(addrman.size(), 1U);
@@ -290,14 +291,14 @@ BOOST_AUTO_TEST_CASE(addrman_select)
// Test: Select pulls from new and tried regardless of port number.
std::set<uint16_t> ports;
for (int i = 0; i < 20; ++i) {
- ports.insert(addrman.Select().GetPort());
+ ports.insert(addrman.Select().first.GetPort());
}
BOOST_CHECK_EQUAL(ports.size(), 3U);
}
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CNetAddr source = ResolveIP("252.2.2.2");
@@ -326,7 +327,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions)
BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CNetAddr source = ResolveIP("252.2.2.2");
@@ -356,7 +357,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
BOOST_AUTO_TEST_CASE(addrman_find)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
BOOST_CHECK_EQUAL(addrman.size(), 0U);
@@ -372,24 +373,24 @@ BOOST_AUTO_TEST_CASE(addrman_find)
BOOST_CHECK(addrman.Add({addr3}, source1));
// Test: ensure Find returns an IP matching what we searched on.
- CAddrInfo* info1 = addrman.Find(addr1);
+ AddrInfo* info1 = addrman.Find(addr1);
BOOST_REQUIRE(info1);
BOOST_CHECK_EQUAL(info1->ToString(), "250.1.2.1:8333");
// Test 18; Find does not discriminate by port number.
- CAddrInfo* info2 = addrman.Find(addr2);
+ AddrInfo* info2 = addrman.Find(addr2);
BOOST_REQUIRE(info2);
BOOST_CHECK_EQUAL(info2->ToString(), info1->ToString());
// Test: Find returns another IP matching what we searched on.
- CAddrInfo* info3 = addrman.Find(addr3);
+ AddrInfo* info3 = addrman.Find(addr3);
BOOST_REQUIRE(info3);
BOOST_CHECK_EQUAL(info3->ToString(), "251.255.2.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_create)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
BOOST_CHECK_EQUAL(addrman.size(), 0U);
@@ -397,19 +398,19 @@ BOOST_AUTO_TEST_CASE(addrman_create)
CNetAddr source1 = ResolveIP("250.1.2.1");
int nId;
- CAddrInfo* pinfo = addrman.Create(addr1, source1, &nId);
+ AddrInfo* pinfo = addrman.Create(addr1, source1, &nId);
// Test: The result should be the same as the input addr.
BOOST_CHECK_EQUAL(pinfo->ToString(), "250.1.2.1:8333");
- CAddrInfo* info2 = addrman.Find(addr1);
+ AddrInfo* info2 = addrman.Find(addr1);
BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:8333");
}
BOOST_AUTO_TEST_CASE(addrman_delete)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
BOOST_CHECK_EQUAL(addrman.size(), 0U);
@@ -423,13 +424,13 @@ BOOST_AUTO_TEST_CASE(addrman_delete)
BOOST_CHECK_EQUAL(addrman.size(), 1U);
addrman.Delete(nId);
BOOST_CHECK_EQUAL(addrman.size(), 0U);
- CAddrInfo* info2 = addrman.Find(addr1);
+ AddrInfo* info2 = addrman.Find(addr1);
BOOST_CHECK(info2 == nullptr);
}
BOOST_AUTO_TEST_CASE(addrman_getaddr)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
// Test: Sanity check, GetAddr should never return anything if addrman
// is empty.
@@ -489,7 +490,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
@@ -497,7 +498,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
CNetAddr source1 = ResolveIP("250.1.1.1");
- CAddrInfo info1 = CAddrInfo(addr1, source1);
+ AddrInfo info1 = AddrInfo(addr1, source1);
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
@@ -512,14 +513,14 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
// Test: Two addresses with same IP but different ports can map to
// different buckets because they have different keys.
- CAddrInfo info2 = CAddrInfo(addr2, source1);
+ AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap));
std::set<int> buckets;
for (int i = 0; i < 255; i++) {
- CAddrInfo infoi = CAddrInfo(
+ AddrInfo infoi = AddrInfo(
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
ResolveIP("250.1.1." + ToString(i)));
int bucket = infoi.GetTriedBucket(nKey1, asmap);
@@ -531,7 +532,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
buckets.clear();
for (int j = 0; j < 255; j++) {
- CAddrInfo infoj = CAddrInfo(
+ AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
ResolveIP("250." + ToString(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1, asmap);
@@ -544,14 +545,14 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
- CAddrInfo info1 = CAddrInfo(addr1, source1);
+ AddrInfo info1 = AddrInfo(addr1, source1);
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
@@ -567,13 +568,13 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
BOOST_CHECK(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap));
// Test: Ports should not affect bucket placement in the addr
- CAddrInfo info2 = CAddrInfo(addr2, source1);
+ AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap));
std::set<int> buckets;
for (int i = 0; i < 255; i++) {
- CAddrInfo infoi = CAddrInfo(
+ AddrInfo infoi = AddrInfo(
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
ResolveIP("250.1.1." + ToString(i)));
int bucket = infoi.GetNewBucket(nKey1, asmap);
@@ -585,7 +586,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
buckets.clear();
for (int j = 0; j < 4 * 255; j++) {
- CAddrInfo infoj = CAddrInfo(CAddress(
+ AddrInfo infoj = AddrInfo(CAddress(
ResolveService(
ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
ResolveIP("251.4.1.1"));
@@ -598,7 +599,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
buckets.clear();
for (int p = 0; p < 255; p++) {
- CAddrInfo infoj = CAddrInfo(
+ AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
ResolveIP("250." + ToString(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap);
@@ -622,7 +623,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
// 101.8.0.0/16 AS8
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
@@ -630,7 +631,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
CNetAddr source1 = ResolveIP("250.1.1.1");
- CAddrInfo info1 = CAddrInfo(addr1, source1);
+ AddrInfo info1 = AddrInfo(addr1, source1);
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
@@ -645,14 +646,14 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
// Test: Two addresses with same IP but different ports can map to
// different buckets because they have different keys.
- CAddrInfo info2 = CAddrInfo(addr2, source1);
+ AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap));
std::set<int> buckets;
for (int j = 0; j < 255; j++) {
- CAddrInfo infoj = CAddrInfo(
+ AddrInfo infoj = AddrInfo(
CAddress(ResolveService("101." + ToString(j) + ".1.1"), NODE_NONE),
ResolveIP("101." + ToString(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1, asmap);
@@ -664,7 +665,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
buckets.clear();
for (int j = 0; j < 255; j++) {
- CAddrInfo infoj = CAddrInfo(
+ AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250." + ToString(j) + ".1.1"), NODE_NONE),
ResolveIP("250." + ToString(j) + ".1.1"));
int bucket = infoj.GetTriedBucket(nKey1, asmap);
@@ -677,14 +678,14 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
CNetAddr source1 = ResolveIP("250.1.2.1");
- CAddrInfo info1 = CAddrInfo(addr1, source1);
+ AddrInfo info1 = AddrInfo(addr1, source1);
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
@@ -700,13 +701,13 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
BOOST_CHECK(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap));
// Test: Ports should not affect bucket placement in the addr
- CAddrInfo info2 = CAddrInfo(addr2, source1);
+ AddrInfo info2 = AddrInfo(addr2, source1);
BOOST_CHECK(info1.GetKey() != info2.GetKey());
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap));
std::set<int> buckets;
for (int i = 0; i < 255; i++) {
- CAddrInfo infoi = CAddrInfo(
+ AddrInfo infoi = AddrInfo(
CAddress(ResolveService("250.1.1." + ToString(i)), NODE_NONE),
ResolveIP("250.1.1." + ToString(i)));
int bucket = infoi.GetNewBucket(nKey1, asmap);
@@ -718,7 +719,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
buckets.clear();
for (int j = 0; j < 4 * 255; j++) {
- CAddrInfo infoj = CAddrInfo(CAddress(
+ AddrInfo infoj = AddrInfo(CAddress(
ResolveService(
ToString(250 + (j / 255)) + "." + ToString(j % 256) + ".1.1"), NODE_NONE),
ResolveIP("251.4.1.1"));
@@ -731,7 +732,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
buckets.clear();
for (int p = 0; p < 255; p++) {
- CAddrInfo infoj = CAddrInfo(
+ AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
ResolveIP("101." + ToString(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap);
@@ -743,7 +744,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
buckets.clear();
for (int p = 0; p < 255; p++) {
- CAddrInfo infoj = CAddrInfo(
+ AddrInfo infoj = AddrInfo(
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
ResolveIP("250." + ToString(p) + ".1.1"));
int bucket = infoj.GetNewBucket(nKey1, asmap);
@@ -759,9 +760,9 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
{
std::vector<bool> asmap1 = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
- auto addrman_asmap1 = std::make_unique<CAddrManTest>(true, asmap1);
- auto addrman_asmap1_dup = std::make_unique<CAddrManTest>(true, asmap1);
- auto addrman_noasmap = std::make_unique<CAddrManTest>();
+ auto addrman_asmap1 = std::make_unique<AddrManTest>(true, asmap1);
+ auto addrman_asmap1_dup = std::make_unique<AddrManTest>(true, asmap1);
+ auto addrman_noasmap = std::make_unique<AddrManTest>();
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
@@ -791,8 +792,8 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(bucketAndEntry_asmap1.second != bucketAndEntry_noasmap.second);
// deserializing non-asmaped peers.dat to asmaped addrman
- addrman_asmap1 = std::make_unique<CAddrManTest>(true, asmap1);
- addrman_noasmap = std::make_unique<CAddrManTest>();
+ addrman_asmap1 = std::make_unique<AddrManTest>(true, asmap1);
+ addrman_noasmap = std::make_unique<AddrManTest>();
addrman_noasmap->Add({addr}, default_source);
stream << *addrman_noasmap;
stream >> *addrman_asmap1;
@@ -803,8 +804,8 @@ BOOST_AUTO_TEST_CASE(addrman_serialization)
BOOST_CHECK(bucketAndEntry_asmap1_deser.second == bucketAndEntry_asmap1_dup.second);
// used to map to different buckets, now maps to the same bucket.
- addrman_asmap1 = std::make_unique<CAddrManTest>(true, asmap1);
- addrman_noasmap = std::make_unique<CAddrManTest>();
+ addrman_asmap1 = std::make_unique<AddrManTest>(true, asmap1);
+ addrman_noasmap = std::make_unique<AddrManTest>();
CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
addrman_noasmap->Add({addr, addr2}, default_source);
@@ -824,7 +825,7 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
{
// Confirm that invalid addresses are ignored in unserialization.
- auto addrman = std::make_unique<CAddrManTest>();
+ auto addrman = std::make_unique<AddrManTest>();
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
const CAddress new1{ResolveService("5.5.5.5"), NODE_NONE};
@@ -856,19 +857,19 @@ BOOST_AUTO_TEST_CASE(remove_invalid)
BOOST_REQUIRE(pos + sizeof(tried2_raw_replacement) <= stream.size());
memcpy(stream.data() + pos, tried2_raw_replacement, sizeof(tried2_raw_replacement));
- addrman = std::make_unique<CAddrManTest>();
+ addrman = std::make_unique<AddrManTest>();
stream >> *addrman;
BOOST_CHECK_EQUAL(addrman->size(), 2);
}
BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
BOOST_CHECK(addrman.size() == 0);
// Empty addrman should return blank addrman info.
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
// Add twenty two addresses.
CNetAddr source = ResolveIP("252.2.2.2");
@@ -879,7 +880,7 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
// No collisions yet.
BOOST_CHECK(addrman.size() == i);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
// Ensure Good handles duplicates well.
@@ -888,14 +889,14 @@ BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision)
addrman.Good(addr);
BOOST_CHECK(addrman.size() == 22);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
}
BOOST_AUTO_TEST_CASE(addrman_noevict)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
// Add 35 addresses.
CNetAddr source = ResolveIP("252.2.2.2");
@@ -906,7 +907,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
// No collision yet.
BOOST_CHECK(addrman.size() == i);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
// Collision between 36 and 19.
@@ -915,11 +916,11 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
addrman.Good(addr36);
BOOST_CHECK(addrman.size() == 36);
- BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().ToString(), "250.1.1.19:0");
+ BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.19:0");
// 36 should be discarded and 19 not evicted.
addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
// Lets create two collisions.
for (unsigned int i = 37; i < 59; i++) {
@@ -928,7 +929,7 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
addrman.Good(addr);
BOOST_CHECK(addrman.size() == i);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
// Cause a collision.
@@ -937,26 +938,26 @@ BOOST_AUTO_TEST_CASE(addrman_noevict)
addrman.Good(addr59);
BOOST_CHECK(addrman.size() == 59);
- BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().ToString(), "250.1.1.10:0");
+ BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.10:0");
// Cause a second collision.
BOOST_CHECK(!addrman.Add({CAddress(addr36, NODE_NONE)}, source));
addrman.Good(addr36);
BOOST_CHECK(addrman.size() == 59);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() != "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() != "[::]:0");
addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
BOOST_AUTO_TEST_CASE(addrman_evictionworks)
{
- CAddrManTest addrman;
+ AddrManTest addrman;
BOOST_CHECK(addrman.size() == 0);
// Empty addrman should return blank addrman info.
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
// Add 35 addresses
CNetAddr source = ResolveIP("252.2.2.2");
@@ -967,7 +968,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
// No collision yet.
BOOST_CHECK(addrman.size() == i);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
// Collision between 36 and 19.
@@ -976,7 +977,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
addrman.Good(addr);
BOOST_CHECK_EQUAL(addrman.size(), 36);
- CAddrInfo info = addrman.SelectTriedCollision();
+ auto info = addrman.SelectTriedCollision().first;
BOOST_CHECK_EQUAL(info.ToString(), "250.1.1.19:0");
// Ensure test of address fails, so that it is evicted.
@@ -984,28 +985,28 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
// Should swap 36 for 19.
addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
// If 36 was swapped for 19, then this should cause no collisions.
BOOST_CHECK(!addrman.Add({CAddress(addr, NODE_NONE)}, source));
addrman.Good(addr);
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
// If we insert 19 it should collide with 36
CService addr19 = ResolveService("250.1.1.19");
BOOST_CHECK(!addrman.Add({CAddress(addr19, NODE_NONE)}, source));
addrman.Good(addr19);
- BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().ToString(), "250.1.1.36:0");
+ BOOST_CHECK_EQUAL(addrman.SelectTriedCollision().first.ToString(), "250.1.1.36:0");
addrman.ResolveCollisions();
- BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
+ BOOST_CHECK(addrman.SelectTriedCollision().first.ToString() == "[::]:0");
}
BOOST_AUTO_TEST_CASE(load_addrman)
{
- CAddrManUncorrupted addrmanUncorrupted;
+ AddrManUncorrupted addrmanUncorrupted;
CService addr1, addr2, addr3;
BOOST_CHECK(Lookup("250.7.1.1", addr1, 8333, false));
@@ -1024,7 +1025,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
// Test that the de-serialization does not throw an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
bool exceptionThrown = false;
- CAddrMan addrman1(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
+ AddrMan addrman1(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman1.size() == 0);
try {
@@ -1041,7 +1042,7 @@ BOOST_AUTO_TEST_CASE(load_addrman)
// Test that ReadFromStream creates an addrman with the correct number of addrs.
CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);
- CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
+ AddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
ReadFromStream(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
@@ -1050,12 +1051,12 @@ BOOST_AUTO_TEST_CASE(load_addrman)
BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
{
- CAddrManCorrupted addrmanCorrupted;
+ AddrManCorrupted addrmanCorrupted;
// Test that the de-serialization of corrupted addrman throws an exception.
CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
bool exceptionThrown = false;
- CAddrMan addrman1(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
+ AddrMan addrman1(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman1.size() == 0);
try {
unsigned char pchMsgTmp[4];
@@ -1071,7 +1072,7 @@ BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
// Test that ReadFromStream fails if peers.dat is corrupt
CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);
- CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
+ AddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
}
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 77b7758a17..114fe3907c 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
+#include <consensus/amount.h>
#include <policy/feerate.h>
#include <limits>
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 5a98558240..23ef2062ef 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL);
filter.insert(vchPubKey);
uint160 hash = pubkey.GetID();
- filter.insert(std::vector<unsigned char>(hash.begin(), hash.end()));
+ filter.insert(hash);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
diff --git a/src/test/data/README.md b/src/test/data/README.md
index 2463daa42a..a05d9c668b 100644
--- a/src/test/data/README.md
+++ b/src/test/data/README.md
@@ -8,5 +8,5 @@ License
The data files in this directory are distributed under the MIT software
license, see the accompanying file COPYING or
-http://www.opensource.org/licenses/mit-license.php.
+https://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index fdbfb3b93b..8df3707fc9 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -4,8 +4,10 @@
#include <addrdb.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <chainparams.h>
#include <merkleblock.h>
+#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
@@ -23,111 +25,97 @@ void initialize_addrman()
SelectParams(CBaseChainParams::REGTEST);
}
-class CAddrManDeterministic : public CAddrMan
+FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
{
-public:
- FuzzedDataProvider& m_fuzzed_data_provider;
-
- explicit CAddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider)
- : CAddrMan(std::move(asmap), /* deterministic */ true, /* consistency_check_ratio */ 0)
- , m_fuzzed_data_provider(fuzzed_data_provider)
- {
- WITH_LOCK(cs, insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
+ AddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ try {
+ ReadFromStream(addr_man, data_stream);
+ } catch (const std::exception&) {
}
+}
- /**
- * Generate a random address. Always returns a valid address.
- */
- CNetAddr RandAddr() EXCLUSIVE_LOCKS_REQUIRED(cs)
- {
- CNetAddr addr;
- if (m_fuzzed_data_provider.remaining_bytes() > 1 && m_fuzzed_data_provider.ConsumeBool()) {
- addr = ConsumeNetAddr(m_fuzzed_data_provider);
- } else {
- // The networks [1..6] correspond to CNetAddr::BIP155Network (private).
- static const std::map<uint8_t, uint8_t> net_len_map = {{1, ADDR_IPV4_SIZE},
- {2, ADDR_IPV6_SIZE},
- {4, ADDR_TORV3_SIZE},
- {5, ADDR_I2P_SIZE},
- {6, ADDR_CJDNS_SIZE}};
- uint8_t net = insecure_rand.randrange(5) + 1; // [1..5]
- if (net == 3) {
- net = 6;
- }
-
- CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+/**
+ * Generate a random address. Always returns a valid address.
+ */
+CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& fast_random_context)
+{
+ CNetAddr addr;
+ if (fuzzed_data_provider.remaining_bytes() > 1 && fuzzed_data_provider.ConsumeBool()) {
+ addr = ConsumeNetAddr(fuzzed_data_provider);
+ } else {
+ // The networks [1..6] correspond to CNetAddr::BIP155Network (private).
+ static const std::map<uint8_t, uint8_t> net_len_map = {{1, ADDR_IPV4_SIZE},
+ {2, ADDR_IPV6_SIZE},
+ {4, ADDR_TORV3_SIZE},
+ {5, ADDR_I2P_SIZE},
+ {6, ADDR_CJDNS_SIZE}};
+ uint8_t net = fast_random_context.randrange(5) + 1; // [1..5]
+ if (net == 3) {
+ net = 6;
+ }
- s << net;
- s << insecure_rand.randbytes(net_len_map.at(net));
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
- s >> addr;
- }
+ s << net;
+ s << fast_random_context.randbytes(net_len_map.at(net));
- // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
- if (!addr.IsValid()) {
- in_addr v4_addr = {};
- v4_addr.s_addr = 0x05050505;
- addr = CNetAddr{v4_addr};
- }
+ s >> addr;
+ }
- return addr;
+ // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
+ if (!addr.IsValid()) {
+ in_addr v4_addr = {};
+ v4_addr.s_addr = 0x05050505;
+ addr = CNetAddr{v4_addr};
}
- /**
- * Fill this addrman with lots of addresses from lots of sources.
- */
- void Fill()
- {
- LOCK(cs);
-
- // Add some of the addresses directly to the "tried" table.
-
- // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
- const size_t n = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
-
- const size_t num_sources = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(10, 50);
- CNetAddr prev_source;
- // Use insecure_rand inside the loops instead of m_fuzzed_data_provider because when
- // the latter is exhausted it just returns 0.
- for (size_t i = 0; i < num_sources; ++i) {
- const auto source = RandAddr();
- const size_t num_addresses = insecure_rand.randrange(500) + 1; // [1..500]
-
- for (size_t j = 0; j < num_addresses; ++j) {
- const auto addr = CAddress{CService{RandAddr(), 8333}, NODE_NETWORK};
- const auto time_penalty = insecure_rand.randrange(100000001);
-#if 1
- // 2.83 sec to fill.
- if (n > 0 && mapInfo.size() % n == 0 && mapAddr.find(addr) == mapAddr.end()) {
- // Add to the "tried" table (if the bucket slot is free).
- const CAddrInfo dummy{addr, source};
- const int bucket = dummy.GetTriedBucket(nKey, m_asmap);
- const int bucket_pos = dummy.GetBucketPosition(nKey, false, bucket);
- if (vvTried[bucket][bucket_pos] == -1) {
- int id;
- CAddrInfo* addr_info = Create(addr, source, &id);
- vvTried[bucket][bucket_pos] = id;
- addr_info->fInTried = true;
- ++nTried;
- }
- } else {
- // Add to the "new" table.
- Add_(addr, source, time_penalty);
- }
-#else
- // 261.91 sec to fill.
- Add_(addr, source, time_penalty);
- if (n > 0 && mapInfo.size() % n == 0) {
- Good_(addr, false, GetTime());
- }
-#endif
- // Add 10% of the addresses from more than one source.
- if (insecure_rand.randrange(10) == 0 && prev_source.IsValid()) {
- Add_(addr, prev_source, time_penalty);
- }
+ return addr;
+}
+
+/** Fill addrman with lots of addresses from lots of sources. */
+void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)
+{
+ // Add a fraction of the addresses to the "tried" table.
+ // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
+ const size_t n = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
+
+ const size_t num_sources = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
+ CNetAddr prev_source;
+ // Generate a FastRandomContext seed to use inside the loops instead of
+ // fuzzed_data_provider. When fuzzed_data_provider is exhausted it
+ // just returns 0.
+ FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
+ for (size_t i = 0; i < num_sources; ++i) {
+ const auto source = RandAddr(fuzzed_data_provider, fast_random_context);
+ const size_t num_addresses = fast_random_context.randrange(500) + 1; // [1..500]
+
+ for (size_t j = 0; j < num_addresses; ++j) {
+ const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK};
+ const auto time_penalty = fast_random_context.randrange(100000001);
+ addrman.Add({addr}, source, time_penalty);
+
+ if (n > 0 && addrman.size() % n == 0) {
+ addrman.Good(addr, GetTime());
+ }
+
+ // Add 10% of the addresses from more than one source.
+ if (fast_random_context.randrange(10) == 0 && prev_source.IsValid()) {
+ addrman.Add({addr}, prev_source, time_penalty);
}
- prev_source = source;
}
+ prev_source = source;
+ }
+}
+
+class AddrManDeterministic : public AddrMan
+{
+public:
+ explicit AddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider)
+ : AddrMan(std::move(asmap), /* deterministic */ true, /* consistency_check_ratio */ 0)
+ {
+ WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
}
/**
@@ -137,46 +125,46 @@ public:
* - vvNew entries refer to the same addresses
* - vvTried entries refer to the same addresses
*/
- bool operator==(const CAddrManDeterministic& other)
+ bool operator==(const AddrManDeterministic& other)
{
- LOCK2(cs, other.cs);
+ LOCK2(m_impl->cs, other.m_impl->cs);
- if (mapInfo.size() != other.mapInfo.size() || nNew != other.nNew ||
- nTried != other.nTried) {
+ if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
+ m_impl->nTried != other.m_impl->nTried) {
return false;
}
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
// Keys may be different.
- using CAddrInfoHasher = std::function<size_t(const CAddrInfo&)>;
- using CAddrInfoEq = std::function<bool(const CAddrInfo&, const CAddrInfo&)>;
+ using AddrInfoHasher = std::function<size_t(const AddrInfo&)>;
+ using AddrInfoEq = std::function<bool(const AddrInfo&, const AddrInfo&)>;
CNetAddrHash netaddr_hasher;
- CAddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const CAddrInfo& a) {
+ AddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const AddrInfo& a) {
return netaddr_hasher(static_cast<CNetAddr>(a)) ^ netaddr_hasher(a.source) ^
a.nLastSuccess ^ a.nAttempts ^ a.nRefCount ^ a.fInTried;
};
- CAddrInfoEq addrinfo_eq = [](const CAddrInfo& lhs, const CAddrInfo& rhs) {
+ AddrInfoEq addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
return static_cast<CNetAddr>(lhs) == static_cast<CNetAddr>(rhs) &&
lhs.source == rhs.source && lhs.nLastSuccess == rhs.nLastSuccess &&
lhs.nAttempts == rhs.nAttempts && lhs.nRefCount == rhs.nRefCount &&
lhs.fInTried == rhs.fInTried;
};
- using Addresses = std::unordered_set<CAddrInfo, CAddrInfoHasher, CAddrInfoEq>;
+ using Addresses = std::unordered_set<AddrInfo, AddrInfoHasher, AddrInfoEq>;
- const size_t num_addresses{mapInfo.size()};
+ const size_t num_addresses{m_impl->mapInfo.size()};
Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
- for (const auto& [id, addr] : mapInfo) {
+ for (const auto& [id, addr] : m_impl->mapInfo) {
addresses.insert(addr);
}
Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
- for (const auto& [id, addr] : other.mapInfo) {
+ for (const auto& [id, addr] : other.m_impl->mapInfo) {
other_addresses.insert(addr);
}
@@ -184,14 +172,14 @@ public:
return false;
}
- auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(cs, other.cs) {
+ auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
if (id == -1 && other_id == -1) {
return true;
}
if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
return false;
}
- return mapInfo.at(id) == other.mapInfo.at(other_id);
+ return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
};
// Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
@@ -199,7 +187,7 @@ public:
// themselves may differ between `vvNew` and `other.vvNew`.
for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
- if (!IdsReferToSameAddress(vvNew[i][j], other.vvNew[i][j])) {
+ if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
return false;
}
}
@@ -208,7 +196,7 @@ public:
// Same for `vvTried`.
for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
- if (!IdsReferToSameAddress(vvTried[i][j], other.vvTried[i][j])) {
+ if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
return false;
}
}
@@ -230,7 +218,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider);
- auto addr_man_ptr = std::make_unique<CAddrManDeterministic>(asmap, fuzzed_data_provider);
+ auto addr_man_ptr = std::make_unique<AddrManDeterministic>(asmap, fuzzed_data_provider);
if (fuzzed_data_provider.ConsumeBool()) {
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
@@ -239,10 +227,10 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
try {
ds >> *addr_man_ptr;
} catch (const std::ios_base::failure&) {
- addr_man_ptr = std::make_unique<CAddrManDeterministic>(asmap, fuzzed_data_provider);
+ addr_man_ptr = std::make_unique<AddrManDeterministic>(asmap, fuzzed_data_provider);
}
}
- CAddrManDeterministic& addr_man = *addr_man_ptr;
+ AddrManDeterministic& addr_man = *addr_man_ptr;
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
fuzzed_data_provider,
@@ -291,7 +279,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
}
});
}
- const CAddrMan& const_addr_man{addr_man};
+ const AddrMan& 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),
@@ -309,12 +297,12 @@ FUZZ_TARGET_INIT(addrman_serdeser, initialize_addrman)
SetMockTime(ConsumeTime(fuzzed_data_provider));
std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider);
- CAddrManDeterministic addr_man1{asmap, fuzzed_data_provider};
- CAddrManDeterministic addr_man2{asmap, fuzzed_data_provider};
+ AddrManDeterministic addr_man1{asmap, fuzzed_data_provider};
+ AddrManDeterministic addr_man2{asmap, fuzzed_data_provider};
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
- addr_man1.Fill();
+ FillAddrman(addr_man1, fuzzed_data_provider);
data_stream << addr_man1;
data_stream >> addr_man2;
assert(addr_man1 == addr_man2);
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index bbdb2c6917..87e70861fa 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -2,10 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
#include <chainparams.h>
#include <chainparamsbase.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 01741103e4..d381345a0d 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -25,7 +25,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
- CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), addrman, fuzzed_data_provider.ConsumeBool()};
CNetAddr random_netaddr;
CNode random_node = ConsumeNode(fuzzed_data_provider);
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
deleted file mode 100644
index 323090e041..0000000000
--- a/src/test/fuzz/data_stream.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2020-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 <addrdb.h>
-#include <addrman.h>
-#include <net.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-#include <test/util/setup_common.h>
-
-#include <cstdint>
-#include <vector>
-
-void initialize_data_stream_addr_man()
-{
- static const auto testing_setup = MakeNoLogFileContext<>();
-}
-
-FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
-{
- FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
- CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
- CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- try {
- ReadFromStream(addr_man, data_stream);
- } catch (const std::exception&) {
- }
-}
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 83ae1680e3..a9325fa738 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -4,6 +4,7 @@
#include <addrdb.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <blockencodings.h>
#include <blockfilter.h>
#include <chain.h>
@@ -104,7 +105,7 @@ FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, {
DeserializeFromFuzzingInput(buffer, block_filter);
})
FUZZ_TARGET_DESERIALIZE(addr_info_deserialize, {
- CAddrInfo addr_info;
+ AddrInfo addr_info;
DeserializeFromFuzzingInput(buffer, addr_info);
})
FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, {
@@ -188,7 +189,7 @@ FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
BlockMerkleRoot(block, &mutated);
})
FUZZ_TARGET_DESERIALIZE(addrman_deserialize, {
- CAddrMan am(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddrMan am(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
DeserializeFromFuzzingInput(buffer, am);
})
FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, {
diff --git a/src/test/fuzz/fee_rate.cpp b/src/test/fuzz/fee_rate.cpp
index dff0e58000..a852f8fb60 100644
--- a/src/test/fuzz/fee_rate.cpp
+++ b/src/test/fuzz/fee_rate.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
+#include <consensus/amount.h>
#include <policy/feerate.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp
index 61c7681bf9..b5a07c7ba3 100644
--- a/src/test/fuzz/fees.cpp
+++ b/src/test/fuzz/fees.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
+#include <consensus/amount.h>
#include <policy/fees.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 5a732aeeff..b6c40809e3 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -2,9 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
#include <arith_uint256.h>
#include <compressor.h>
+#include <consensus/amount.h>
#include <consensus/merkle.h>
#include <core_io.h>
#include <crypto/common.h>
diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp
index 5b1acae57b..4ad8123554 100644
--- a/src/test/fuzz/locale.cpp
+++ b/src/test/fuzz/locale.cpp
@@ -50,8 +50,6 @@ FUZZ_TARGET(locale)
const bool parseint32_without_locale = ParseInt32(random_string, &parseint32_out_without_locale);
int64_t parseint64_out_without_locale;
const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale);
- const int64_t atoi64_without_locale = atoi64(random_string);
- const int atoi_without_locale = atoi(random_string);
const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
const std::string tostring_without_locale = ToString(random_int64);
// The variable `random_int32` is no longer used, but the harness still needs to
@@ -77,10 +75,6 @@ FUZZ_TARGET(locale)
if (parseint64_without_locale) {
assert(parseint64_out_without_locale == parseint64_out_with_locale);
}
- const int64_t atoi64_with_locale = atoi64(random_string);
- assert(atoi64_without_locale == atoi64_with_locale);
- const int atoi_with_locale = atoi(random_string);
- assert(atoi_without_locale == atoi_with_locale);
const std::string tostring_with_locale = ToString(random_int64);
assert(tostring_without_locale == tostring_with_locale);
const std::string strprintf_int_with_locale = strprintf("%d", random_int64);
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
index 4ea9511870..8304e6fdb8 100644
--- a/src/test/fuzz/muhash.cpp
+++ b/src/test/fuzz/muhash.cpp
@@ -12,52 +12,47 @@
FUZZ_TARGET(muhash)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
- std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- std::vector<uint8_t> data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- if (data.empty()) {
- data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
- }
- if (data2.empty()) {
- data2.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
- }
-
- data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ std::vector<uint8_t> data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ std::vector<uint8_t> data2{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
MuHash3072 muhash;
- // Test that MuHash result is consistent independent of order of operations
muhash.Insert(data);
muhash.Insert(data2);
+ const std::string initial_state_hash{"dd5ad2a105c2d29495f577245c357409002329b9f4d6182c0af3dc2f462555c8"};
uint256 out;
- muhash.Finalize(out);
-
- muhash = MuHash3072();
- muhash.Insert(data2);
- muhash.Insert(data);
-
uint256 out2;
- muhash.Finalize(out2);
-
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ // Test that MuHash result is consistent independent of order of operations
+ muhash.Finalize(out);
+
+ muhash = MuHash3072();
+ muhash.Insert(data2);
+ muhash.Insert(data);
+ muhash.Finalize(out2);
+ },
+ [&] {
+ // Test that multiplication with the initial state never changes the finalized result
+ muhash.Finalize(out);
+ MuHash3072 muhash3;
+ muhash3 *= muhash;
+ muhash3.Finalize(out2);
+ },
+ [&] {
+ // Test that dividing a MuHash by itself brings it back to it's initial state
+ muhash /= muhash;
+ muhash.Finalize(out);
+ out2 = uint256S(initial_state_hash);
+ },
+ [&] {
+ // Test that removing all added elements brings the object back to it's initial state
+ muhash.Remove(data);
+ muhash.Remove(data2);
+ muhash.Finalize(out);
+ out2 = uint256S(initial_state_hash);
+ });
assert(out == out2);
- MuHash3072 muhash3;
- muhash3 *= muhash;
- uint256 out3;
- muhash3.Finalize(out3);
- assert(out == out3);
-
- // Test that removing all added elements brings the object back to it's initial state
- muhash /= muhash;
- muhash.Finalize(out);
-
- MuHash3072 muhash2;
- muhash2.Finalize(out2);
-
- assert(out == out2);
-
- muhash3.Remove(data);
- muhash3.Remove(data2);
- muhash3.Finalize(out3);
- assert(out == out3);
}
diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp
index 69e58c3f63..85fee062f0 100644
--- a/src/test/fuzz/parse_numbers.cpp
+++ b/src/test/fuzz/parse_numbers.cpp
@@ -14,9 +14,6 @@ FUZZ_TARGET(parse_numbers)
(void)ParseMoney(random_string);
- double d;
- (void)ParseDouble(random_string, &d);
-
uint8_t u8;
(void)ParseUInt8(random_string, &u8);
@@ -25,13 +22,13 @@ FUZZ_TARGET(parse_numbers)
int32_t i32;
(void)ParseInt32(random_string, &i32);
- (void)atoi(random_string);
+ (void)LocaleIndependentAtoi<int>(random_string);
uint32_t u32;
(void)ParseUInt32(random_string, &u32);
int64_t i64;
- (void)atoi64(random_string);
+ (void)LocaleIndependentAtoi<int64_t>(random_string);
(void)ParseFixedPoint(random_string, 3, &i64);
(void)ParseInt64(random_string, &i64);
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 950ee45d1d..74c576322a 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -56,46 +56,8 @@ FUZZ_TARGET_INIT(script, initialize_script)
assert(script == decompressed_script);
}
- CTxDestination address;
- TxoutType type_ret;
- std::vector<CTxDestination> addresses;
- int required_ret;
- bool extract_destinations_ret = ExtractDestinations(script, type_ret, addresses, required_ret);
- bool extract_destination_ret = ExtractDestination(script, address);
- if (!extract_destinations_ret) {
- assert(!extract_destination_ret);
- if (type_ret == TxoutType::MULTISIG) {
- assert(addresses.empty() && required_ret == 0);
- } else {
- assert(type_ret == TxoutType::PUBKEY ||
- type_ret == TxoutType::NONSTANDARD ||
- type_ret == TxoutType::NULL_DATA);
- }
- } else {
- assert(required_ret >= 1 && required_ret <= 16);
- assert((unsigned long)required_ret == addresses.size());
- assert(type_ret == TxoutType::MULTISIG || required_ret == 1);
- }
- if (type_ret == TxoutType::NONSTANDARD || type_ret == TxoutType::NULL_DATA) {
- assert(!extract_destinations_ret);
- }
- if (!extract_destination_ret) {
- assert(type_ret == TxoutType::PUBKEY ||
- type_ret == TxoutType::NONSTANDARD ||
- type_ret == TxoutType::NULL_DATA ||
- type_ret == TxoutType::MULTISIG);
- } else {
- assert(address == addresses[0]);
- }
- if (type_ret == TxoutType::NONSTANDARD ||
- type_ret == TxoutType::NULL_DATA ||
- type_ret == TxoutType::MULTISIG) {
- assert(!extract_destination_ret);
- }
-
TxoutType which_type;
bool is_standard_ret = IsStandard(script, which_type);
- assert(type_ret == which_type);
if (!is_standard_ret) {
assert(which_type == TxoutType::NONSTANDARD ||
which_type == TxoutType::NULL_DATA ||
@@ -112,6 +74,20 @@ FUZZ_TARGET_INIT(script, initialize_script)
which_type == TxoutType::NONSTANDARD);
}
+ CTxDestination address;
+ bool extract_destination_ret = ExtractDestination(script, address);
+ if (!extract_destination_ret) {
+ assert(which_type == TxoutType::PUBKEY ||
+ which_type == TxoutType::NONSTANDARD ||
+ which_type == TxoutType::NULL_DATA ||
+ which_type == TxoutType::MULTISIG);
+ }
+ if (which_type == TxoutType::NONSTANDARD ||
+ which_type == TxoutType::NULL_DATA ||
+ which_type == TxoutType::MULTISIG) {
+ assert(!extract_destination_ret);
+ }
+
const FlatSigningProvider signing_provider;
(void)InferDescriptor(script, signing_provider);
(void)IsSegWitOutput(signing_provider, script);
@@ -133,15 +109,11 @@ FUZZ_TARGET_INIT(script, initialize_script)
(void)ScriptToAsmStr(script, true);
UniValue o1(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o1, true, true);
- ScriptPubKeyToUniv(script, o1, true, false);
+ ScriptPubKeyToUniv(script, o1, true);
UniValue o2(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o2, false, true);
- ScriptPubKeyToUniv(script, o2, false, false);
+ ScriptPubKeyToUniv(script, o2, false);
UniValue o3(UniValue::VOBJ);
- ScriptToUniv(script, o3, true);
- UniValue o4(UniValue::VOBJ);
- ScriptToUniv(script, o4, false);
+ ScriptToUniv(script, o3);
{
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index 1278dc87d4..43927772ae 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <streams.h>
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 0c1b45b86c..ab646c68fc 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -31,9 +31,105 @@
#include <version.h>
#include <cstdint>
+#include <cstdlib>
#include <string>
#include <vector>
+namespace {
+bool LegacyParsePrechecks(const std::string& str)
+{
+ if (str.empty()) // No empty string allowed
+ return false;
+ if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size() - 1]))) // No padding allowed
+ return false;
+ if (!ValidAsCString(str)) // No embedded NUL characters allowed
+ return false;
+ return true;
+}
+
+bool LegacyParseInt32(const std::string& str, int32_t* out)
+{
+ if (!LegacyParsePrechecks(str))
+ return false;
+ char* endp = nullptr;
+ errno = 0; // strtol will not set errno if valid
+ long int n = strtol(str.c_str(), &endp, 10);
+ if (out) *out = (int32_t)n;
+ // Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
+ // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
+ // platforms the size of these types may be different.
+ return endp && *endp == 0 && !errno &&
+ n >= std::numeric_limits<int32_t>::min() &&
+ n <= std::numeric_limits<int32_t>::max();
+}
+
+bool LegacyParseInt64(const std::string& str, int64_t* out)
+{
+ if (!LegacyParsePrechecks(str))
+ return false;
+ char* endp = nullptr;
+ errno = 0; // strtoll will not set errno if valid
+ long long int n = strtoll(str.c_str(), &endp, 10);
+ if (out) *out = (int64_t)n;
+ // Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
+ // we still have to check that the returned value is within the range of an *int64_t*.
+ return endp && *endp == 0 && !errno &&
+ n >= std::numeric_limits<int64_t>::min() &&
+ n <= std::numeric_limits<int64_t>::max();
+}
+
+bool LegacyParseUInt32(const std::string& str, uint32_t* out)
+{
+ if (!LegacyParsePrechecks(str))
+ return false;
+ if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
+ return false;
+ char* endp = nullptr;
+ errno = 0; // strtoul will not set errno if valid
+ unsigned long int n = strtoul(str.c_str(), &endp, 10);
+ if (out) *out = (uint32_t)n;
+ // Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
+ // we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
+ // platforms the size of these types may be different.
+ return endp && *endp == 0 && !errno &&
+ n <= std::numeric_limits<uint32_t>::max();
+}
+
+bool LegacyParseUInt8(const std::string& str, uint8_t* out)
+{
+ uint32_t u32;
+ if (!LegacyParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
+ return false;
+ }
+ if (out != nullptr) {
+ *out = static_cast<uint8_t>(u32);
+ }
+ return true;
+}
+
+bool LegacyParseUInt64(const std::string& str, uint64_t* out)
+{
+ if (!LegacyParsePrechecks(str))
+ return false;
+ if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
+ return false;
+ char* endp = nullptr;
+ errno = 0; // strtoull will not set errno if valid
+ unsigned long long int n = strtoull(str.c_str(), &endp, 10);
+ if (out) *out = (uint64_t)n;
+ // Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
+ // we still have to check that the returned value is within the range of an *uint64_t*.
+ return endp && *endp == 0 && !errno &&
+ n <= std::numeric_limits<uint64_t>::max();
+}
+
+// For backwards compatibility checking.
+int64_t atoi64_legacy(const std::string& str)
+{
+ return strtoll(str.c_str(), nullptr, 10);
+}
+}; // namespace
+
FUZZ_TARGET(string)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -133,4 +229,67 @@ FUZZ_TARGET(string)
const bilingual_str bs2{random_string_2, random_string_1};
(void)(bs1 + bs2);
}
+ {
+ int32_t i32;
+ int64_t i64;
+ uint32_t u32;
+ uint64_t u64;
+ uint8_t u8;
+ const bool ok_i32 = ParseInt32(random_string_1, &i32);
+ const bool ok_i64 = ParseInt64(random_string_1, &i64);
+ const bool ok_u32 = ParseUInt32(random_string_1, &u32);
+ const bool ok_u64 = ParseUInt64(random_string_1, &u64);
+ const bool ok_u8 = ParseUInt8(random_string_1, &u8);
+
+ int32_t i32_legacy;
+ int64_t i64_legacy;
+ uint32_t u32_legacy;
+ uint64_t u64_legacy;
+ uint8_t u8_legacy;
+ const bool ok_i32_legacy = LegacyParseInt32(random_string_1, &i32_legacy);
+ const bool ok_i64_legacy = LegacyParseInt64(random_string_1, &i64_legacy);
+ const bool ok_u32_legacy = LegacyParseUInt32(random_string_1, &u32_legacy);
+ const bool ok_u64_legacy = LegacyParseUInt64(random_string_1, &u64_legacy);
+ const bool ok_u8_legacy = LegacyParseUInt8(random_string_1, &u8_legacy);
+
+ assert(ok_i32 == ok_i32_legacy);
+ assert(ok_i64 == ok_i64_legacy);
+ assert(ok_u32 == ok_u32_legacy);
+ assert(ok_u64 == ok_u64_legacy);
+ assert(ok_u8 == ok_u8_legacy);
+
+ if (ok_i32) {
+ assert(i32 == i32_legacy);
+ }
+ if (ok_i64) {
+ assert(i64 == i64_legacy);
+ }
+ if (ok_u32) {
+ assert(u32 == u32_legacy);
+ }
+ if (ok_u64) {
+ assert(u64 == u64_legacy);
+ }
+ if (ok_u8) {
+ assert(u8 == u8_legacy);
+ }
+ }
+
+ {
+ const int atoi_result = atoi(random_string_1.c_str());
+ const int locale_independent_atoi_result = LocaleIndependentAtoi<int>(random_string_1);
+ const int64_t atoi64_result = atoi64_legacy(random_string_1);
+ const bool out_of_range = atoi64_result < std::numeric_limits<int>::min() || atoi64_result > std::numeric_limits<int>::max();
+ if (out_of_range) {
+ assert(locale_independent_atoi_result == 0);
+ } else {
+ assert(atoi_result == locale_independent_atoi_result);
+ }
+ }
+
+ {
+ const int64_t atoi64_result = atoi64_legacy(random_string_1);
+ const int64_t locale_independent_atoi_result = LocaleIndependentAtoi<int64_t>(random_string_1);
+ assert(atoi64_result == locale_independent_atoi_result || locale_independent_atoi_result == 0);
+ }
}
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index 0f53939eac..dc3f9c8b8f 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -5,6 +5,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <cstdint>
@@ -12,6 +13,11 @@
#include <vector>
namespace {
+void initialize_system()
+{
+ static const auto testing_setup = MakeNoLogFileContext<>();
+}
+
std::string GetArgumentName(const std::string& name)
{
size_t idx = name.find('=');
@@ -20,9 +26,8 @@ std::string GetArgumentName(const std::string& name)
}
return name.substr(0, idx);
}
-} // namespace
-FUZZ_TARGET(system)
+FUZZ_TARGET_INIT(system, initialize_system)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
ArgsManager args_manager{};
@@ -97,7 +102,7 @@ FUZZ_TARGET(system)
const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
const bool b = fuzzed_data_provider.ConsumeBool();
- (void)args_manager.GetArg(s1, i64);
+ (void)args_manager.GetIntArg(s1, i64);
(void)args_manager.GetArg(s1, s2);
(void)args_manager.GetArgFlags(s1);
(void)args_manager.GetArgs(s1);
@@ -114,3 +119,4 @@ FUZZ_TARGET(system)
(void)HelpRequested(args_manager);
}
+} // namespace
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index ff34cc87b2..a21e5cea0c 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -103,6 +103,6 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
- TxToUniv(tx, /* hashBlock */ uint256::ZERO, /* include_addresses */ true, u);
- TxToUniv(tx, /* hashBlock */ uint256::ONE, /* include_addresses */ false, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ZERO, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ONE, u);
}
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index 0d87f687d3..d83d2924bb 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <pubkey.h>
#include <test/fuzz/util.h>
#include <test/util/script.h>
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index bb017b3497..1bc6f1db45 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -5,12 +5,12 @@
#ifndef BITCOIN_TEST_FUZZ_UTIL_H
#define BITCOIN_TEST_FUZZ_UTIL_H
-#include <amount.h>
#include <arith_uint256.h>
#include <attributes.h>
#include <chainparamsbase.h>
#include <coins.h>
#include <compat.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <merkleblock.h>
#include <net.h>
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index 6f2bc081c6..8d2a06f11a 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -4,6 +4,7 @@
#include <chainparams.h>
#include <consensus/validation.h>
+#include <node/utxo_snapshot.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 2a217f3455..17e904fcff 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -137,20 +137,20 @@ BOOST_AUTO_TEST_CASE(intarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 11);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 11), 11);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 0), 0);
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 0);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 0);
ResetArgs("-foo=11 -bar=12");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 11);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 12);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 0), 11);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 12);
ResetArgs("-foo=NaN -bar=NotANumber");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 1), 0);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 1), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 0);
}
BOOST_AUTO_TEST_CASE(doubledash)
@@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(doubledash)
ResetArgs("--foo=verbose --bar=1");
BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "verbose");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), 1);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 0), 1);
}
BOOST_AUTO_TEST_CASE(boolargno)
diff --git a/src/test/policy_fee_tests.cpp b/src/test/policy_fee_tests.cpp
index 4a15be6ca6..f9c7d04d6c 100644
--- a/src/test/policy_fee_tests.cpp
+++ b/src/test/policy_fee_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
+#include <consensus/amount.h>
#include <policy/fees.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_parse_tests.cpp b/src/test/script_parse_tests.cpp
new file mode 100644
index 0000000000..004c1a9a84
--- /dev/null
+++ b/src/test/script_parse_tests.cpp
@@ -0,0 +1,55 @@
+// 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 <core_io.h>
+#include <script/script.h>
+#include <util/strencodings.h>
+#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(script_parse_tests)
+BOOST_AUTO_TEST_CASE(parse_script)
+{
+ const std::vector<std::pair<std::string,std::string>> IN_OUT{
+ // {IN: script string , OUT: hex string }
+ {"", ""},
+ {"0", "00"},
+ {"1", "51"},
+ {"2", "52"},
+ {"3", "53"},
+ {"4", "54"},
+ {"5", "55"},
+ {"6", "56"},
+ {"7", "57"},
+ {"8", "58"},
+ {"9", "59"},
+ {"10", "5a"},
+ {"11", "5b"},
+ {"12", "5c"},
+ {"13", "5d"},
+ {"14", "5e"},
+ {"15", "5f"},
+ {"16", "60"},
+ {"17", "0111"},
+ {"-9", "0189"},
+ {"0x17", "17"},
+ {"'17'", "023137"},
+ {"ELSE", "67"},
+ {"NOP10", "b9"},
+ };
+ std::string all_in;
+ std::string all_out;
+ for (const auto& [in, out] : IN_OUT) {
+ BOOST_CHECK_EQUAL(HexStr(ParseScript(in)), out);
+ all_in += " " + in + " ";
+ all_out += out;
+ }
+ BOOST_CHECK_EQUAL(HexStr(ParseScript(all_in)), all_out);
+
+ BOOST_CHECK_EXCEPTION(ParseScript("11111111111111111111"), std::runtime_error, HasReason("script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF"));
+ BOOST_CHECK_EXCEPTION(ParseScript("11111111111"), std::runtime_error, HasReason("script parse error: decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF"));
+ BOOST_CHECK_EXCEPTION(ParseScript("OP_CHECKSIGADD"), std::runtime_error, HasReason("script parse error: unknown opcode"));
+}
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index a01d3fa03a..bf8ff5f5e2 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -252,67 +252,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(std::get<WitnessUnknown>(address) == unk);
}
-BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
-{
- CKey keys[3];
- CPubKey pubkeys[3];
- for (int i = 0; i < 3; i++) {
- keys[i].MakeNewKey(true);
- pubkeys[i] = keys[i].GetPubKey();
- }
-
- CScript s;
- TxoutType whichType;
- std::vector<CTxDestination> addresses;
- int nRequired;
-
- // TxoutType::PUBKEY
- s.clear();
- s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
- BOOST_CHECK_EQUAL(addresses.size(), 1U);
- BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
-
- // TxoutType::PUBKEYHASH
- s.clear();
- s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
- BOOST_CHECK_EQUAL(addresses.size(), 1U);
- BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
-
- // TxoutType::SCRIPTHASH
- CScript redeemScript(s); // initialize with leftover P2PKH script
- s.clear();
- s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
- BOOST_CHECK_EQUAL(addresses.size(), 1U);
- BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get<ScriptHash>(addresses[0]) == ScriptHash(redeemScript));
-
- // TxoutType::MULTISIG
- s.clear();
- s << OP_2 <<
- ToByteVector(pubkeys[0]) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
- BOOST_CHECK_EQUAL(addresses.size(), 2U);
- BOOST_CHECK_EQUAL(nRequired, 2);
- BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
- BOOST_CHECK(std::get<PKHash>(addresses[1]) == PKHash(pubkeys[1]));
-
- // TxoutType::NULL_DATA
- s.clear();
- s << OP_RETURN << std::vector<unsigned char>({75});
- BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
-}
-
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
{
CKey keys[3];
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 24029ea02e..c813fbea32 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -8,6 +8,7 @@
#include <checkqueue.h>
#include <clientversion.h>
+#include <consensus/amount.h>
#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <core_io.h>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 1924ea55b1..afb3ad0cfd 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -13,6 +13,11 @@
#include <boost/test/unit_test.hpp>
+struct Dersig100Setup : public TestChain100Setup {
+ Dersig100Setup()
+ : TestChain100Setup{{"-testactivationheight=dersig@102"}} {}
+};
+
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
@@ -20,7 +25,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
-BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
{
// Make sure skipping validation of transactions that were
// validated going into the memory pool does not allow
@@ -153,7 +158,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
}
}
-BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
{
// Test that passing CheckInputScripts with one set of script flags doesn't imply
// that we would pass again with a different set of flags.
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
new file mode 100644
index 0000000000..81ea4c38f5
--- /dev/null
+++ b/src/test/util/chainstate.h
@@ -0,0 +1,54 @@
+// 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.
+//
+#ifndef BITCOIN_TEST_UTIL_CHAINSTATE_H
+#define BITCOIN_TEST_UTIL_CHAINSTATE_H
+
+#include <clientversion.h>
+#include <fs.h>
+#include <node/context.h>
+#include <node/utxo_snapshot.h>
+#include <rpc/blockchain.h>
+#include <validation.h>
+
+#include <univalue.h>
+
+#include <boost/test/unit_test.hpp>
+
+const auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
+
+/**
+ * Create and activate a UTXO snapshot, optionally providing a function to
+ * malleate the snapshot.
+ */
+template<typename F = decltype(NoMalleation)>
+static bool
+CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
+{
+ // Write out a snapshot to the test's tempdir.
+ //
+ int height;
+ WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
+ fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
+ FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
+ CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
+
+ UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
+ BOOST_TEST_MESSAGE(
+ "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
+
+ // Read the written snapshot in and then activate it.
+ //
+ FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
+ CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
+ SnapshotMetadata metadata;
+ auto_infile >> metadata;
+
+ malleation(auto_infile, metadata);
+
+ return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
+}
+
+
+#endif // BITCOIN_TEST_UTIL_CHAINSTATE_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index cabc4b3b49..ebefa9974e 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -192,7 +192,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
- m_node.addrman = std::make_unique<CAddrMan>(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ m_node.addrman = std::make_unique<AddrMan>(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
@@ -205,7 +205,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup()
+TestChain100Setup::TestChain100Setup(const std::vector<const char*>& extra_args)
+ : TestingSetup{CBaseChainParams::REGTEST, extra_args}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
@@ -234,11 +235,14 @@ void TestChain100Setup::mineBlocks(int num_blocks)
}
}
-CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
+CBlock TestChain100Setup::CreateBlock(
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey,
+ CChainState& chainstate)
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(chainstate, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -248,6 +252,20 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+ return block;
+}
+
+CBlock TestChain100Setup::CreateAndProcessBlock(
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey,
+ CChainState* chainstate)
+{
+ if (!chainstate) {
+ chainstate = &Assert(m_node.chainman)->ActiveChainstate();
+ }
+
+ const CChainParams& chainparams = Params();
+ const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
@@ -304,11 +322,6 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
return mempool_txn;
}
-TestChain100Setup::~TestChain100Setup()
-{
- gArgs.ForceSetArg("-segwitheight", "0");
-}
-
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
{
return FromTx(MakeTransactionRef(tx));
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 5d12dc2323..7518cdb042 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -113,15 +113,26 @@ class CScript;
/**
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
-struct TestChain100Setup : public RegTestingSetup {
- TestChain100Setup();
+struct TestChain100Setup : public TestingSetup {
+ TestChain100Setup(const std::vector<const char*>& extra_args = {});
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey, and try to add it to the current chain.
+ * If no chainstate is specified, default to the active.
*/
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
- const CScript& scriptPubKey);
+ const CScript& scriptPubKey,
+ CChainState* chainstate = nullptr);
+
+ /**
+ * Create a new block with just given transactions, coinbase paying to
+ * scriptPubKey.
+ */
+ CBlock CreateBlock(
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey,
+ CChainState& chainstate);
//! Mine a series of new blocks on the active chain.
void mineBlocks(int num_blocks);
@@ -145,8 +156,6 @@ struct TestChain100Setup : public RegTestingSetup {
CAmount output_amount = CAmount(1 * COIN),
bool submit = true);
- ~TestChain100Setup();
-
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index a62abf9b9c..b5088d3c33 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -300,9 +300,9 @@ public:
}
if (expect.default_int) {
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), 99999);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), 99999);
} else if (expect.int_value) {
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), *expect.int_value);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), *expect.int_value);
} else {
BOOST_CHECK(!success);
}
@@ -432,8 +432,8 @@ static void TestParse(const std::string& str, bool expected_bool, int64_t expect
BOOST_CHECK(test.ParseParameters(2, (char**)argv, error));
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool);
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool);
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99998), expected_int);
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), expected_int);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99998), expected_int);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), expected_int);
}
// Test bool and int parsing.
@@ -784,9 +784,9 @@ BOOST_AUTO_TEST_CASE(util_GetArg)
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
- BOOST_CHECK_EQUAL(testArgs.GetArg("inttest1", -1), 12345);
- BOOST_CHECK_EQUAL(testArgs.GetArg("inttest2", -1), 81985529216486895LL);
- BOOST_CHECK_EQUAL(testArgs.GetArg("inttest3", -1), -1);
+ BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest1", -1), 12345);
+ BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest2", -1), 81985529216486895LL);
+ BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest3", -1), -1);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false);
@@ -1239,6 +1239,11 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney)
BOOST_AUTO_TEST_CASE(util_ParseMoney)
{
BOOST_CHECK_EQUAL(ParseMoney("0.0").value(), 0);
+ BOOST_CHECK_EQUAL(ParseMoney(".").value(), 0);
+ BOOST_CHECK_EQUAL(ParseMoney("0.").value(), 0);
+ BOOST_CHECK_EQUAL(ParseMoney(".0").value(), 0);
+ BOOST_CHECK_EQUAL(ParseMoney(".6789").value(), 6789'0000);
+ BOOST_CHECK_EQUAL(ParseMoney("12345.").value(), COIN * 12345);
BOOST_CHECK_EQUAL(ParseMoney("12345.6789").value(), (COIN/10000)*123456789);
@@ -1276,11 +1281,18 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney)
BOOST_CHECK(!ParseMoney(" "));
// Parsing two numbers should fail
+ BOOST_CHECK(!ParseMoney(".."));
+ BOOST_CHECK(!ParseMoney("0..0"));
BOOST_CHECK(!ParseMoney("1 2"));
BOOST_CHECK(!ParseMoney(" 1 2 "));
BOOST_CHECK(!ParseMoney(" 1.2 3 "));
BOOST_CHECK(!ParseMoney(" 1 2.3 "));
+ // Embedded whitespace should fail
+ BOOST_CHECK(!ParseMoney(" -1 .2 "));
+ BOOST_CHECK(!ParseMoney(" 1 .2 "));
+ BOOST_CHECK(!ParseMoney(" +1 .2 "));
+
// Attempted 63 bit overflow should fail
BOOST_CHECK(!ParseMoney("92233720368.54775808"));
@@ -1474,6 +1486,168 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32)
BOOST_CHECK(!ParseInt32("32482348723847471234", nullptr));
}
+template <typename T>
+static void RunToIntegralTests()
+{
+ BOOST_CHECK(!ToIntegral<T>(STRING_WITH_EMBEDDED_NULL_CHAR));
+ BOOST_CHECK(!ToIntegral<T>(" 1"));
+ BOOST_CHECK(!ToIntegral<T>("1 "));
+ BOOST_CHECK(!ToIntegral<T>("1a"));
+ BOOST_CHECK(!ToIntegral<T>("1.1"));
+ BOOST_CHECK(!ToIntegral<T>("1.9"));
+ BOOST_CHECK(!ToIntegral<T>("+01.9"));
+ BOOST_CHECK(!ToIntegral<T>("-"));
+ BOOST_CHECK(!ToIntegral<T>("+"));
+ BOOST_CHECK(!ToIntegral<T>(" -1"));
+ BOOST_CHECK(!ToIntegral<T>("-1 "));
+ BOOST_CHECK(!ToIntegral<T>(" -1 "));
+ BOOST_CHECK(!ToIntegral<T>("+1"));
+ BOOST_CHECK(!ToIntegral<T>(" +1"));
+ BOOST_CHECK(!ToIntegral<T>(" +1 "));
+ BOOST_CHECK(!ToIntegral<T>("+-1"));
+ BOOST_CHECK(!ToIntegral<T>("-+1"));
+ BOOST_CHECK(!ToIntegral<T>("++1"));
+ BOOST_CHECK(!ToIntegral<T>("--1"));
+ BOOST_CHECK(!ToIntegral<T>(""));
+ BOOST_CHECK(!ToIntegral<T>("aap"));
+ BOOST_CHECK(!ToIntegral<T>("0x1"));
+ BOOST_CHECK(!ToIntegral<T>("-32482348723847471234"));
+ BOOST_CHECK(!ToIntegral<T>("32482348723847471234"));
+}
+
+BOOST_AUTO_TEST_CASE(test_ToIntegral)
+{
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("1234").value(), 1'234);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("0").value(), 0);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("01234").value(), 1'234);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("00000000000000001234").value(), 1'234);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-00000000000000001234").value(), -1'234);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("00000000000000000000").value(), 0);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-00000000000000000000").value(), 0);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-1234").value(), -1'234);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-1").value(), -1);
+
+ RunToIntegralTests<uint64_t>();
+ RunToIntegralTests<int64_t>();
+ RunToIntegralTests<uint32_t>();
+ RunToIntegralTests<int32_t>();
+ RunToIntegralTests<uint16_t>();
+ RunToIntegralTests<int16_t>();
+ RunToIntegralTests<uint8_t>();
+ RunToIntegralTests<int8_t>();
+
+ BOOST_CHECK(!ToIntegral<int64_t>("-9223372036854775809"));
+ BOOST_CHECK_EQUAL(ToIntegral<int64_t>("-9223372036854775808").value(), -9'223'372'036'854'775'807LL - 1LL);
+ BOOST_CHECK_EQUAL(ToIntegral<int64_t>("9223372036854775807").value(), 9'223'372'036'854'775'807);
+ BOOST_CHECK(!ToIntegral<int64_t>("9223372036854775808"));
+
+ BOOST_CHECK(!ToIntegral<uint64_t>("-1"));
+ BOOST_CHECK_EQUAL(ToIntegral<uint64_t>("0").value(), 0U);
+ BOOST_CHECK_EQUAL(ToIntegral<uint64_t>("18446744073709551615").value(), 18'446'744'073'709'551'615ULL);
+ BOOST_CHECK(!ToIntegral<uint64_t>("18446744073709551616"));
+
+ BOOST_CHECK(!ToIntegral<int32_t>("-2147483649"));
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-2147483648").value(), -2'147'483'648LL);
+ BOOST_CHECK_EQUAL(ToIntegral<int32_t>("2147483647").value(), 2'147'483'647);
+ BOOST_CHECK(!ToIntegral<int32_t>("2147483648"));
+
+ BOOST_CHECK(!ToIntegral<uint32_t>("-1"));
+ BOOST_CHECK_EQUAL(ToIntegral<uint32_t>("0").value(), 0U);
+ BOOST_CHECK_EQUAL(ToIntegral<uint32_t>("4294967295").value(), 4'294'967'295U);
+ BOOST_CHECK(!ToIntegral<uint32_t>("4294967296"));
+
+ BOOST_CHECK(!ToIntegral<int16_t>("-32769"));
+ BOOST_CHECK_EQUAL(ToIntegral<int16_t>("-32768").value(), -32'768);
+ BOOST_CHECK_EQUAL(ToIntegral<int16_t>("32767").value(), 32'767);
+ BOOST_CHECK(!ToIntegral<int16_t>("32768"));
+
+ BOOST_CHECK(!ToIntegral<uint16_t>("-1"));
+ BOOST_CHECK_EQUAL(ToIntegral<uint16_t>("0").value(), 0U);
+ BOOST_CHECK_EQUAL(ToIntegral<uint16_t>("65535").value(), 65'535U);
+ BOOST_CHECK(!ToIntegral<uint16_t>("65536"));
+
+ BOOST_CHECK(!ToIntegral<int8_t>("-129"));
+ BOOST_CHECK_EQUAL(ToIntegral<int8_t>("-128").value(), -128);
+ BOOST_CHECK_EQUAL(ToIntegral<int8_t>("127").value(), 127);
+ BOOST_CHECK(!ToIntegral<int8_t>("128"));
+
+ BOOST_CHECK(!ToIntegral<uint8_t>("-1"));
+ BOOST_CHECK_EQUAL(ToIntegral<uint8_t>("0").value(), 0U);
+ BOOST_CHECK_EQUAL(ToIntegral<uint8_t>("255").value(), 255U);
+ BOOST_CHECK(!ToIntegral<uint8_t>("256"));
+}
+
+BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi)
+{
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("1234"), 1'234);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("0"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("01234"), 1'234);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-1234"), -1'234);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(" 1"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("1 "), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("1a"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("1.1"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("1.9"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("+01.9"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-1"), -1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(" -1"), -1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-1 "), -1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(" -1 "), -1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("+1"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(" +1"), 1);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(" +1 "), 1);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("+-1"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-+1"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("++1"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("--1"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>(""), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("aap"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("0x1"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-32482348723847471234"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("32482348723847471234"), 0);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("-9223372036854775809"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("-9223372036854775808"), -9'223'372'036'854'775'807LL - 1LL);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("9223372036854775807"), 9'223'372'036'854'775'807);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int64_t>("9223372036854775808"), 0);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("-1"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("0"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("18446744073709551615"), 18'446'744'073'709'551'615ULL);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint64_t>("18446744073709551616"), 0U);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-2147483649"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("-2147483648"), -2'147'483'648LL);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("2147483647"), 2'147'483'647);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int32_t>("2147483648"), 0);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("-1"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("0"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("4294967295"), 4'294'967'295U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint32_t>("4294967296"), 0U);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("-32769"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("-32768"), -32'768);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("32767"), 32'767);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int16_t>("32768"), 0);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("-1"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("0"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("65535"), 65'535U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint16_t>("65536"), 0U);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("-129"), 0);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("-128"), -128);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("127"), 127);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<int8_t>("128"), 0);
+
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("-1"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("0"), 0U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("255"), 255U);
+ BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("256"), 0U);
+}
+
BOOST_AUTO_TEST_CASE(test_ParseInt64)
{
int64_t n;
@@ -1639,32 +1813,6 @@ BOOST_AUTO_TEST_CASE(test_ParseUInt64)
BOOST_CHECK(!ParseUInt64("-1234", &n));
}
-BOOST_AUTO_TEST_CASE(test_ParseDouble)
-{
- double n;
- // Valid values
- BOOST_CHECK(ParseDouble("1234", nullptr));
- BOOST_CHECK(ParseDouble("0", &n) && n == 0.0);
- BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0);
- BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal
- BOOST_CHECK(ParseDouble("2147483647", &n) && n == 2147483647.0);
- BOOST_CHECK(ParseDouble("-2147483648", &n) && n == -2147483648.0);
- BOOST_CHECK(ParseDouble("-1234", &n) && n == -1234.0);
- BOOST_CHECK(ParseDouble("1e6", &n) && n == 1e6);
- BOOST_CHECK(ParseDouble("-1e6", &n) && n == -1e6);
- // Invalid values
- BOOST_CHECK(!ParseDouble("", &n));
- BOOST_CHECK(!ParseDouble(" 1", &n)); // no padding inside
- BOOST_CHECK(!ParseDouble("1 ", &n));
- BOOST_CHECK(!ParseDouble("1a", &n));
- BOOST_CHECK(!ParseDouble("aap", &n));
- BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex
- BOOST_CHECK(!ParseDouble(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
- // Overflow and underflow
- BOOST_CHECK(!ParseDouble("-1e10000", nullptr));
- BOOST_CHECK(!ParseDouble("1e10000", nullptr));
-}
-
BOOST_AUTO_TEST_CASE(test_FormatParagraph)
{
BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), "");
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 315ef22599..9bb08f774f 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -2,10 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
+#include <chainparams.h>
#include <random.h>
#include <uint256.h>
#include <consensus/validation.h>
#include <sync.h>
+#include <rpc/blockchain.h>
+#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
#include <validation.h>
@@ -73,4 +76,78 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
WITH_LOCK(::cs_main, manager.Unload());
}
+//! Test UpdateTip behavior for both active and background chainstates.
+//!
+//! When run on the background chainstate, UpdateTip should do a subset
+//! of what it does for the active chainstate.
+BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
+{
+ ChainstateManager& chainman = *Assert(m_node.chainman);
+ uint256 curr_tip = ::g_best_block;
+
+ // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
+ // be found.
+ mineBlocks(10);
+
+ // After adding some blocks to the tip, best block should have changed.
+ BOOST_CHECK(::g_best_block != curr_tip);
+
+ BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+
+ // Ensure our active chain is the snapshot chainstate.
+ BOOST_CHECK(chainman.IsSnapshotActive());
+
+ curr_tip = ::g_best_block;
+
+ // Mine a new block on top of the activated snapshot chainstate.
+ mineBlocks(1); // Defined in TestChain100Setup.
+
+ // After adding some blocks to the snapshot tip, best block should have changed.
+ BOOST_CHECK(::g_best_block != curr_tip);
+
+ curr_tip = ::g_best_block;
+
+ BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
+
+ CChainState& background_cs{*[&] {
+ for (CChainState* cs : chainman.GetAll()) {
+ if (cs != &chainman.ActiveChainstate()) {
+ return cs;
+ }
+ }
+ assert(false);
+ }()};
+
+ // Create a block to append to the validation chain.
+ std::vector<CMutableTransaction> noTxns;
+ CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, background_cs);
+ auto pblock = std::make_shared<const CBlock>(validation_block);
+ BlockValidationState state;
+ CBlockIndex* pindex = nullptr;
+ const CChainParams& chainparams = Params();
+ bool newblock = false;
+
+ // TODO: much of this is inlined from ProcessNewBlock(); just reuse PNB()
+ // once it is changed to support multiple chainstates.
+ {
+ LOCK(::cs_main);
+ bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
+ BOOST_CHECK(checked);
+ bool accepted = background_cs.AcceptBlock(
+ pblock, state, &pindex, true, nullptr, &newblock);
+ BOOST_CHECK(accepted);
+ }
+ // UpdateTip is called here
+ bool block_added = background_cs.ActivateBestChain(state, pblock);
+
+ // Ensure tip is as expected
+ BOOST_CHECK_EQUAL(background_cs.m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
+
+ // g_best_block should be unchanged after adding a block to the background
+ // validation chain.
+ BOOST_CHECK(block_added);
+ BOOST_CHECK_EQUAL(curr_tip, ::g_best_block);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 0bd378631b..be9e05a65e 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -8,13 +8,13 @@
#include <random.h>
#include <rpc/blockchain.h>
#include <sync.h>
+#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <validation.h>
#include <validationinterface.h>
#include <tinyformat.h>
-#include <univalue.h>
#include <vector>
@@ -44,7 +44,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_CHECK(!manager.IsSnapshotActive());
BOOST_CHECK(!manager.IsSnapshotValidated());
- BOOST_CHECK(!manager.IsBackgroundIBD(&c1));
auto all = manager.GetAll();
BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
@@ -57,9 +56,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
auto exp_tip = c1.m_chain.Tip();
BOOST_CHECK_EQUAL(active_tip, exp_tip);
- auto& validated_cs = manager.ValidatedChainstate();
- BOOST_CHECK_EQUAL(&validated_cs, &c1);
-
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
// Create a snapshot-based chainstate.
@@ -81,8 +77,8 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_CHECK(manager.IsSnapshotActive());
BOOST_CHECK(!manager.IsSnapshotValidated());
- BOOST_CHECK(manager.IsBackgroundIBD(&c1));
- BOOST_CHECK(!manager.IsBackgroundIBD(&c2));
+ BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate());
+ BOOST_CHECK(&c1 != &manager.ActiveChainstate());
auto all2 = manager.GetAll();
BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
@@ -99,16 +95,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// CCoinsViewCache instances.
BOOST_CHECK(exp_tip != exp_tip2);
- auto& validated_cs2 = manager.ValidatedChainstate();
- BOOST_CHECK_EQUAL(&validated_cs2, &c1);
-
- auto& validated_chain = manager.ValidatedChain();
- BOOST_CHECK_EQUAL(&validated_chain, &c1.m_chain);
-
- auto validated_tip = manager.ValidatedTip();
- exp_tip = c1.m_chain.Tip();
- BOOST_CHECK_EQUAL(validated_tip, exp_tip);
-
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
SyncWithValidationInterfaceQueue();
@@ -168,36 +154,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
}
-auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
-
-template<typename F = decltype(NoMalleation)>
-static bool
-CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
-{
- // Write out a snapshot to the test's tempdir.
- //
- int height;
- WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
- fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
- FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
- CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
-
- UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
- BOOST_TEST_MESSAGE(
- "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
-
- // Read the written snapshot in and then activate it.
- //
- FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
- CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
- SnapshotMetadata metadata;
- auto_infile >> metadata;
-
- malleation(auto_infile, metadata);
-
- return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
-}
-
//! Test basic snapshot activation.
BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
{
@@ -321,27 +277,27 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
{
LOCK(::cs_main);
size_t coins_in_active{0};
- size_t coins_in_ibd{0};
- size_t coins_missing_ibd{0};
+ size_t coins_in_background{0};
+ size_t coins_missing_from_background{0};
for (CChainState* chainstate : chainman.GetAll()) {
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
CCoinsViewCache& coinscache = chainstate->CoinsTip();
- bool is_ibd = chainman.IsBackgroundIBD(chainstate);
+ bool is_background = chainstate != &chainman.ActiveChainstate();
for (CTransactionRef& txn : m_coinbase_txns) {
COutPoint op{txn->GetHash(), 0};
if (coinscache.HaveCoin(op)) {
- (is_ibd ? coins_in_ibd : coins_in_active)++;
- } else if (is_ibd) {
- coins_missing_ibd++;
+ (is_background ? coins_in_background : coins_in_active)++;
+ } else if (is_background) {
+ coins_missing_from_background++;
}
}
}
BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
- BOOST_CHECK_EQUAL(coins_in_ibd, initial_total_coins);
- BOOST_CHECK_EQUAL(coins_missing_ibd, new_coins);
+ BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);
+ BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);
}
// Snapshot should refuse to load after one has already loaded.
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 22aafcaa6c..9136c497ea 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -9,7 +9,7 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
+BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, ChainTestingSetup)
//! Test utilities for detecting when we need to flush the coins cache based
//! on estimated memory usage.
@@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
CTxMemPool mempool;
BlockManager blockman{};
- CChainState chainstate{&mempool, blockman};
+ CChainState chainstate{&mempool, blockman, *Assert(m_node.chainman)};
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index a0c2e76f00..ca52ecba2f 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <consensus/amount.h>
#include <net.h>
#include <signet.h>
#include <uint256.h>
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 354092752d..69d0273e79 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -11,6 +11,7 @@
#include <netaddress.h>
#include <node/ui_interface.h>
#include <sync.h>
+#include <tinyformat.h>
#include <util/system.h>
#include <util/translation.h>
#include <warnings.h>
@@ -74,7 +75,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
int64_t nMedian = vTimeOffsets.median();
std::vector<int64_t> vSorted = vTimeOffsets.sorted();
// Only let other nodes change our time by so much
- int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
+ int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetIntArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
if (nMedian >= -max_adjustment && nMedian <= max_adjustment) {
nTimeOffset = nMedian;
} else {
@@ -98,11 +99,12 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
}
if (LogAcceptCategory(BCLog::NET)) {
+ std::string log_message{"time data samples: "};
for (const int64_t n : vSorted) {
- LogPrint(BCLog::NET, "%+d ", n); /* Continued */
+ log_message += strprintf("%+d ", n);
}
- LogPrint(BCLog::NET, "| "); /* Continued */
- LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset / 60);
+ log_message += strprintf("| median offset = %+d (%+d minutes)", nTimeOffset, nTimeOffset / 60);
+ LogPrint(BCLog::NET, "%s\n", log_message);
}
}
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index bb296456ba..f9caf48df8 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -14,6 +14,7 @@
#include <netbase.h>
#include <util/readwritefile.h>
#include <util/strencodings.h>
+#include <util/syscall_sandbox.h>
#include <util/system.h>
#include <util/thread.h>
#include <util/time.h>
@@ -83,7 +84,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
if (s.size() < 4) // Short line
continue;
// <status>(-|+| )<data><CRLF>
- self->message.code = atoi(s.substr(0,3));
+ self->message.code = LocaleIndependentAtoi<int>(s.substr(0,3));
self->message.lines.push_back(s.substr(4));
char ch = s[3]; // '-','+' or ' '
if (ch == ' ') {
@@ -585,6 +586,7 @@ static std::thread torControlThread;
static void TorControlThread(CService onion_service_target)
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::TOR_CONTROL);
TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL), onion_service_target);
event_base_dispatch(gBase);
diff --git a/src/txdb.cpp b/src/txdb.cpp
index cfa864668a..3839c9083c 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -107,8 +107,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
CDBBatch batch(*m_db);
size_t count = 0;
size_t changed = 0;
- size_t batch_size = (size_t)gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize);
- int crash_simulate = gArgs.GetArg("-dbcrashratio", 0);
+ size_t batch_size = (size_t)gArgs.GetIntArg("-dbbatchsize", nDefaultDbBatchSize);
+ int crash_simulate = gArgs.GetIntArg("-dbcrashratio", 0);
assert(!hashBlock.IsNull());
uint256 old_tip = GetBestBlock();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index d5a888ac67..5a93f30c8a 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -21,23 +21,23 @@
#include <cmath>
#include <optional>
-CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
- int64_t _nTime, unsigned int _entryHeight,
- bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp)
- : tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight),
- spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
-{
- nCountWithDescendants = 1;
- nSizeWithDescendants = GetTxSize();
- nModFeesWithDescendants = nFee;
-
- feeDelta = 0;
-
- nCountWithAncestors = 1;
- nSizeWithAncestors = GetTxSize();
- nModFeesWithAncestors = nFee;
- nSigOpCostWithAncestors = sigOpCost;
-}
+CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
+ int64_t time, unsigned int entry_height,
+ bool spends_coinbase, int64_t sigops_cost, LockPoints lp)
+ : tx{tx},
+ nFee{fee},
+ nTxWeight(GetTransactionWeight(*tx)),
+ nUsageSize{RecursiveDynamicUsage(tx)},
+ nTime{time},
+ entryHeight{entry_height},
+ spendsCoinbase{spends_coinbase},
+ sigOpCost{sigops_cost},
+ lockPoints{lp},
+ nSizeWithDescendants{GetTxSize()},
+ nModFeesWithDescendants{nFee},
+ nSizeWithAncestors{GetTxSize()},
+ nModFeesWithAncestors{nFee},
+ nSigOpCostWithAncestors{sigOpCost} {}
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
{
@@ -924,7 +924,7 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
++nTransactionsUpdated;
}
}
- LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
+ LogPrintf("PrioritiseTransaction: %s fee += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
}
void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const
@@ -1174,12 +1174,14 @@ uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
return maximum;
}
-void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const {
+void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* const ancestorsize, CAmount* const ancestorfees) const {
LOCK(cs);
auto it = mapTx.find(txid);
ancestors = descendants = 0;
if (it != mapTx.end()) {
ancestors = it->GetCountWithAncestors();
+ if (ancestorsize) *ancestorsize = it->GetSizeWithAncestors();
+ if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors();
descendants = CalculateDescendantMaximum(it);
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 0a84a6e6b1..0be51db181 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -14,8 +14,8 @@
#include <utility>
#include <vector>
-#include <amount.h>
#include <coins.h>
+#include <consensus/amount.h>
#include <indirectmap.h>
#include <policy/feerate.h>
#include <policy/packages.h>
@@ -37,19 +37,16 @@ extern RecursiveMutex cs_main;
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF;
-struct LockPoints
-{
+struct LockPoints {
// Will be set to the blockchain height and median time past
// values that would be necessary to satisfy all relative locktime
// constraints (BIP68) of this tx given our view of block chain history
- int height;
- int64_t time;
+ int height{0};
+ int64_t time{0};
// As long as the current chain descends from the highest height block
// containing one of the inputs used in the calculation, then the cached
// values are still valid even after a reorg.
- CBlockIndex* maxInputBlock;
-
- LockPoints() : height(0), time(0), maxInputBlock(nullptr) { }
+ CBlockIndex* maxInputBlock{nullptr};
};
struct CompareIteratorByHash {
@@ -98,27 +95,27 @@ private:
const unsigned int entryHeight; //!< Chain height when entering the mempool
const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
const int64_t sigOpCost; //!< Total sigop cost
- int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block
+ int64_t feeDelta{0}; //!< Used for determining the priority of the transaction for mining in a block
LockPoints lockPoints; //!< Track the height and time at which tx was final
// Information about descendants of this transaction that are in the
// mempool; if we remove this transaction we must remove all of these
// descendants as well.
- uint64_t nCountWithDescendants; //!< number of descendant transactions
+ uint64_t nCountWithDescendants{1}; //!< number of descendant transactions
uint64_t nSizeWithDescendants; //!< ... and size
CAmount nModFeesWithDescendants; //!< ... and total fees (all including us)
// Analogous statistics for ancestor transactions
- uint64_t nCountWithAncestors;
+ uint64_t nCountWithAncestors{1};
uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors;
int64_t nSigOpCostWithAncestors;
public:
- CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
- int64_t _nTime, unsigned int _entryHeight,
- bool spendsCoinbase,
- int64_t nSigOpsCost, LockPoints lp);
+ CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
+ int64_t time, unsigned int entry_height,
+ bool spends_coinbase,
+ int64_t sigops_cost, LockPoints lp);
const CTransaction& GetTx() const { return *this->tx; }
CTransactionRef GetSharedTx() const { return this->tx; }
@@ -748,8 +745,10 @@ public:
/**
* Calculate the ancestor and descendant count for the given transaction.
* The counts include the transaction itself.
+ * When ancestors is non-zero (ie, the transaction itself is in the mempool),
+ * ancestorsize and ancestorfees will also be set to the appropriate values.
*/
- void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
+ void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) const;
/** @returns true if the mempool is fully loaded */
bool IsLoaded() const;
diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp
index d3f4029607..1aed7daacf 100644
--- a/src/util/moneystr.cpp
+++ b/src/util/moneystr.cpp
@@ -5,7 +5,7 @@
#include <util/moneystr.h>
-#include <amount.h>
+#include <consensus/amount.h>
#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -77,8 +77,7 @@ std::optional<CAmount> ParseMoney(const std::string& money_string)
return std::nullopt;
if (nUnits < 0 || nUnits > COIN)
return std::nullopt;
- int64_t nWhole = atoi64(strWhole);
-
+ int64_t nWhole = LocaleIndependentAtoi<int64_t>(strWhole);
CAmount value = nWhole * COIN + nUnits;
if (!MoneyRange(value)) {
diff --git a/src/util/moneystr.h b/src/util/moneystr.h
index b71dffd0db..f37dc1cffd 100644
--- a/src/util/moneystr.h
+++ b/src/util/moneystr.h
@@ -9,8 +9,8 @@
#ifndef BITCOIN_UTIL_MONEYSTR_H
#define BITCOIN_UTIL_MONEYSTR_H
-#include <amount.h>
#include <attributes.h>
+#include <consensus/amount.h>
#include <optional>
#include <string>
diff --git a/src/util/rbf.h b/src/util/rbf.h
index 6d44a2cb83..aa522d8bfb 100644
--- a/src/util/rbf.h
+++ b/src/util/rbf.h
@@ -9,7 +9,7 @@
class CTransaction;
-static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
+static constexpr uint32_t MAX_BIP125_RBF_SEQUENCE{0xfffffffd};
/** Check whether the sequence numbers on this transaction are signaling opt-in to replace-by-fee,
* according to BIP 125. Allow opt-out of transaction replacement by setting nSequence >
@@ -17,7 +17,7 @@ static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
*
* SEQUENCE_FINAL-1 is picked to still allow use of nLockTime by non-replaceable transactions. All
* inputs rather than just one is for the sake of multi-party protocols, where we don't want a single
-* party to be able to disable replacement. */
-bool SignalsOptInRBF(const CTransaction &tx);
+* party to be able to disable replacement by opting out in their own input. */
+bool SignalsOptInRBF(const CTransaction& tx);
#endif // BITCOIN_UTIL_RBF_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index f514613f0d..15bd07b374 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -11,8 +11,7 @@
#include <algorithm>
#include <cstdlib>
#include <cstring>
-#include <errno.h>
-#include <limits>
+#include <optional>
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -282,118 +281,55 @@ std::string DecodeBase32(const std::string& str, bool* pf_invalid)
return std::string((const char*)vchRet.data(), vchRet.size());
}
-[[nodiscard]] static bool ParsePrechecks(const std::string& str)
+namespace {
+template <typename T>
+bool ParseIntegral(const std::string& str, T* out)
{
- if (str.empty()) // No empty string allowed
+ static_assert(std::is_integral<T>::value);
+ // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
+ // handling leading +/- for backwards compatibility.
+ if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
return false;
- if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
- return false;
- if (!ValidAsCString(str)) // No embedded NUL characters allowed
+ }
+ const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
+ if (!opt_int) {
return false;
+ }
+ if (out != nullptr) {
+ *out = *opt_int;
+ }
return true;
}
+}; // namespace
-bool ParseInt32(const std::string& str, int32_t *out)
+bool ParseInt32(const std::string& str, int32_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- char *endp = nullptr;
- errno = 0; // strtol will not set errno if valid
- long int n = strtol(str.c_str(), &endp, 10);
- if(out) *out = (int32_t)n;
- // Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
- // platforms the size of these types may be different.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int32_t>::min() &&
- n <= std::numeric_limits<int32_t>::max();
+ return ParseIntegral<int32_t>(str, out);
}
-bool ParseInt64(const std::string& str, int64_t *out)
+bool ParseInt64(const std::string& str, int64_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- char *endp = nullptr;
- errno = 0; // strtoll will not set errno if valid
- long long int n = strtoll(str.c_str(), &endp, 10);
- if(out) *out = (int64_t)n;
- // Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *int64_t*.
- return endp && *endp == 0 && !errno &&
- n >= std::numeric_limits<int64_t>::min() &&
- n <= std::numeric_limits<int64_t>::max();
+ return ParseIntegral<int64_t>(str, out);
}
-bool ParseUInt8(const std::string& str, uint8_t *out)
+bool ParseUInt8(const std::string& str, uint8_t* out)
{
- uint32_t u32;
- if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint8_t>::max()) {
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<uint8_t>(u32);
- }
- return true;
+ return ParseIntegral<uint8_t>(str, out);
}
bool ParseUInt16(const std::string& str, uint16_t* out)
{
- uint32_t u32;
- if (!ParseUInt32(str, &u32) || u32 > std::numeric_limits<uint16_t>::max()) {
- return false;
- }
- if (out != nullptr) {
- *out = static_cast<uint16_t>(u32);
- }
- return true;
+ return ParseIntegral<uint16_t>(str, out);
}
-bool ParseUInt32(const std::string& str, uint32_t *out)
+bool ParseUInt32(const std::string& str, uint32_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
- return false;
- char *endp = nullptr;
- errno = 0; // strtoul will not set errno if valid
- unsigned long int n = strtoul(str.c_str(), &endp, 10);
- if(out) *out = (uint32_t)n;
- // Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
- // platforms the size of these types may be different.
- return endp && *endp == 0 && !errno &&
- n <= std::numeric_limits<uint32_t>::max();
-}
-
-bool ParseUInt64(const std::string& str, uint64_t *out)
-{
- if (!ParsePrechecks(str))
- return false;
- if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
- return false;
- char *endp = nullptr;
- errno = 0; // strtoull will not set errno if valid
- unsigned long long int n = strtoull(str.c_str(), &endp, 10);
- if(out) *out = (uint64_t)n;
- // Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
- // we still have to check that the returned value is within the range of an *uint64_t*.
- return endp && *endp == 0 && !errno &&
- n <= std::numeric_limits<uint64_t>::max();
+ return ParseIntegral<uint32_t>(str, out);
}
-
-bool ParseDouble(const std::string& str, double *out)
+bool ParseUInt64(const std::string& str, uint64_t* out)
{
- if (!ParsePrechecks(str))
- return false;
- if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
- return false;
- std::istringstream text(str);
- text.imbue(std::locale::classic());
- double result;
- text >> result;
- if(out) *out = result;
- return text.eof() && !text.fail();
+ return ParseIntegral<uint64_t>(str, out);
}
std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
@@ -437,20 +373,6 @@ std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
return out.str();
}
-int64_t atoi64(const std::string& str)
-{
-#ifdef _MSC_VER
- return _atoi64(str.c_str());
-#else
- return strtoll(str.c_str(), nullptr, 10);
-#endif
-}
-
-int atoi(const std::string& str)
-{
- return atoi(str.c_str());
-}
-
/** Upper bound for mantissa.
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
* Larger integers cannot consist of arbitrary combinations of 0-9:
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 26dc0a0ce3..eedb5ec2f8 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -11,9 +11,12 @@
#include <attributes.h>
#include <span.h>
+#include <util/string.h>
+#include <charconv>
#include <cstdint>
#include <iterator>
+#include <optional>
#include <string>
#include <vector>
@@ -66,8 +69,33 @@ std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
std::string EncodeBase32(const std::string& str, bool pad = true);
void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut);
-int64_t atoi64(const std::string& str);
-int atoi(const std::string& str);
+
+// LocaleIndependentAtoi is provided for backwards compatibility reasons.
+//
+// New code should use ToIntegral or the ParseInt* functions
+// which provide parse error feedback.
+//
+// The goal of LocaleIndependentAtoi is to replicate the exact defined behaviour
+// of atoi and atoi64 as they behave under the "C" locale.
+template <typename T>
+T LocaleIndependentAtoi(const std::string& str)
+{
+ static_assert(std::is_integral<T>::value);
+ T result;
+ // Emulate atoi(...) handling of white space and leading +/-.
+ std::string s = TrimString(str);
+ if (!s.empty() && s[0] == '+') {
+ if (s.length() >= 2 && s[1] == '-') {
+ return 0;
+ }
+ s = s.substr(1);
+ }
+ auto [_, error_condition] = std::from_chars(s.data(), s.data() + s.size(), result);
+ if (error_condition != std::errc{}) {
+ return 0;
+ }
+ return result;
+}
/**
* Tests if the given character is a decimal digit.
@@ -95,6 +123,26 @@ constexpr inline bool IsSpace(char c) noexcept {
}
/**
+ * Convert string to integral type T. Leading whitespace, a leading +, or any
+ * trailing character fail the parsing. The required format expressed as regex
+ * is `-?[0-9]+`. The minus sign is only permitted for signed integer types.
+ *
+ * @returns std::nullopt if the entire string could not be parsed, or if the
+ * parsed value is not in the range representable by the type T.
+ */
+template <typename T>
+std::optional<T> ToIntegral(const std::string& str)
+{
+ static_assert(std::is_integral<T>::value);
+ T result;
+ const auto [first_nonmatching, error_condition] = std::from_chars(str.data(), str.data() + str.size(), result);
+ if (first_nonmatching != str.data() + str.size() || error_condition != std::errc{}) {
+ return std::nullopt;
+ }
+ return result;
+}
+
+/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
@@ -137,13 +185,6 @@ constexpr inline bool IsSpace(char c) noexcept {
[[nodiscard]] bool ParseUInt64(const std::string& str, uint64_t *out);
/**
- * Convert string to double with strict parse error feedback.
- * @returns true if the entire string could be parsed as valid double,
- * false if not the entire string could be parsed or when overflow or underflow occurred.
- */
-[[nodiscard]] bool ParseDouble(const std::string& str, double *out);
-
-/**
* Convert a span of bytes to a lower-case hexadecimal string.
*/
std::string HexStr(const Span<const uint8_t> s);
diff --git a/src/util/syscall_sandbox.cpp b/src/util/syscall_sandbox.cpp
new file mode 100644
index 0000000000..b361b09568
--- /dev/null
+++ b/src/util/syscall_sandbox.cpp
@@ -0,0 +1,918 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif // defined(HAVE_CONFIG_H)
+
+#include <util/syscall_sandbox.h>
+
+#if defined(USE_SYSCALL_SANDBOX)
+#include <array>
+#include <cassert>
+#include <cstdint>
+#include <exception>
+#include <map>
+#include <new>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <logging.h>
+#include <tinyformat.h>
+#include <util/threadnames.h>
+
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/unistd.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace {
+bool g_syscall_sandbox_enabled{false};
+bool g_syscall_sandbox_log_violation_before_terminating{false};
+
+#if !defined(__x86_64__)
+#error Syscall sandbox is an experimental feature currently available only under Linux x86-64.
+#endif // defined(__x86_64__)
+
+#ifndef SECCOMP_RET_KILL_PROCESS
+#define SECCOMP_RET_KILL_PROCESS 0x80000000U
+#endif
+
+// Define system call numbers for x86_64 that are referenced in the system call profile
+// but not provided by the kernel headers used in the GUIX build.
+// Usually, they can be found via "grep name /usr/include/x86_64-linux-gnu/asm/unistd_64.h"
+
+#ifndef __NR_clone3
+#define __NR_clone3 435
+#endif
+
+#ifndef __NR_statx
+#define __NR_statx 332
+#endif
+
+#ifndef __NR_getrandom
+#define __NR_getrandom 318
+#endif
+
+#ifndef __NR_membarrier
+#define __NR_membarrier 324
+#endif
+
+#ifndef __NR_copy_file_range
+#define __NR_copy_file_range 326
+#endif
+
+// This list of syscalls in LINUX_SYSCALLS is only used to map syscall numbers to syscall names in
+// order to be able to print user friendly error messages which include the syscall name in addition
+// to the syscall number.
+//
+// Example output in case of a syscall violation where the syscall is present in LINUX_SYSCALLS:
+//
+// ```
+// 2021-06-09T12:34:56Z ERROR: The syscall "execve" (syscall number 59) is not allowed by the syscall sandbox in thread "msghand". Please report.
+// ```
+//
+// Example output in case of a syscall violation where the syscall is not present in LINUX_SYSCALLS:
+//
+// ```
+// 2021-06-09T12:34:56Z ERROR: The syscall "*unknown*" (syscall number 314) is not allowed by the syscall sandbox in thread "msghand". Please report.
+// ``
+//
+// LINUX_SYSCALLS contains two types of syscalls:
+// 1.) Syscalls that are present under all architectures or relevant Linux kernel versions for which
+// we support the syscall sandbox feature (currently only Linux x86-64). Examples include read,
+// write, open, close, etc.
+// 2.) Syscalls that are present under a subset of architectures or relevant Linux kernel versions
+// for which we support the syscall sandbox feature. This type of syscalls should be added to
+// LINUX_SYSCALLS conditional on availability like in the following example:
+// ...
+// #if defined(__NR_arch_dependent_syscall)
+// {__NR_arch_dependent_syscall, "arch_dependent_syscall"},
+// #endif // defined(__NR_arch_dependent_syscall)
+// ...
+const std::map<uint32_t, std::string> LINUX_SYSCALLS{
+ {__NR_accept, "accept"},
+ {__NR_accept4, "accept4"},
+ {__NR_access, "access"},
+ {__NR_acct, "acct"},
+ {__NR_add_key, "add_key"},
+ {__NR_adjtimex, "adjtimex"},
+ {__NR_afs_syscall, "afs_syscall"},
+ {__NR_alarm, "alarm"},
+ {__NR_arch_prctl, "arch_prctl"},
+ {__NR_bind, "bind"},
+ {__NR_bpf, "bpf"},
+ {__NR_brk, "brk"},
+ {__NR_capget, "capget"},
+ {__NR_capset, "capset"},
+ {__NR_chdir, "chdir"},
+ {__NR_chmod, "chmod"},
+ {__NR_chown, "chown"},
+ {__NR_chroot, "chroot"},
+ {__NR_clock_adjtime, "clock_adjtime"},
+ {__NR_clock_getres, "clock_getres"},
+ {__NR_clock_gettime, "clock_gettime"},
+ {__NR_clock_nanosleep, "clock_nanosleep"},
+ {__NR_clock_settime, "clock_settime"},
+ {__NR_clone, "clone"},
+ {__NR_clone3, "clone3"},
+ {__NR_close, "close"},
+ {__NR_connect, "connect"},
+ {__NR_copy_file_range, "copy_file_range"},
+ {__NR_creat, "creat"},
+ {__NR_create_module, "create_module"},
+ {__NR_delete_module, "delete_module"},
+ {__NR_dup, "dup"},
+ {__NR_dup2, "dup2"},
+ {__NR_dup3, "dup3"},
+ {__NR_epoll_create, "epoll_create"},
+ {__NR_epoll_create1, "epoll_create1"},
+ {__NR_epoll_ctl, "epoll_ctl"},
+ {__NR_epoll_ctl_old, "epoll_ctl_old"},
+ {__NR_epoll_pwait, "epoll_pwait"},
+ {__NR_epoll_wait, "epoll_wait"},
+ {__NR_epoll_wait_old, "epoll_wait_old"},
+ {__NR_eventfd, "eventfd"},
+ {__NR_eventfd2, "eventfd2"},
+ {__NR_execve, "execve"},
+ {__NR_execveat, "execveat"},
+ {__NR_exit, "exit"},
+ {__NR_exit_group, "exit_group"},
+ {__NR_faccessat, "faccessat"},
+ {__NR_fadvise64, "fadvise64"},
+ {__NR_fallocate, "fallocate"},
+ {__NR_fanotify_init, "fanotify_init"},
+ {__NR_fanotify_mark, "fanotify_mark"},
+ {__NR_fchdir, "fchdir"},
+ {__NR_fchmod, "fchmod"},
+ {__NR_fchmodat, "fchmodat"},
+ {__NR_fchown, "fchown"},
+ {__NR_fchownat, "fchownat"},
+ {__NR_fcntl, "fcntl"},
+ {__NR_fdatasync, "fdatasync"},
+ {__NR_fgetxattr, "fgetxattr"},
+ {__NR_finit_module, "finit_module"},
+ {__NR_flistxattr, "flistxattr"},
+ {__NR_flock, "flock"},
+ {__NR_fork, "fork"},
+ {__NR_fremovexattr, "fremovexattr"},
+ {__NR_fsetxattr, "fsetxattr"},
+ {__NR_fstat, "fstat"},
+ {__NR_fstatfs, "fstatfs"},
+ {__NR_fsync, "fsync"},
+ {__NR_ftruncate, "ftruncate"},
+ {__NR_futex, "futex"},
+ {__NR_futimesat, "futimesat"},
+ {__NR_getcpu, "getcpu"},
+ {__NR_getcwd, "getcwd"},
+ {__NR_getdents, "getdents"},
+ {__NR_getdents64, "getdents64"},
+ {__NR_getegid, "getegid"},
+ {__NR_geteuid, "geteuid"},
+ {__NR_getgid, "getgid"},
+ {__NR_getgroups, "getgroups"},
+ {__NR_getitimer, "getitimer"},
+ {__NR_get_kernel_syms, "get_kernel_syms"},
+ {__NR_get_mempolicy, "get_mempolicy"},
+ {__NR_getpeername, "getpeername"},
+ {__NR_getpgid, "getpgid"},
+ {__NR_getpgrp, "getpgrp"},
+ {__NR_getpid, "getpid"},
+ {__NR_getpmsg, "getpmsg"},
+ {__NR_getppid, "getppid"},
+ {__NR_getpriority, "getpriority"},
+ {__NR_getrandom, "getrandom"},
+ {__NR_getresgid, "getresgid"},
+ {__NR_getresuid, "getresuid"},
+ {__NR_getrlimit, "getrlimit"},
+ {__NR_get_robust_list, "get_robust_list"},
+ {__NR_getrusage, "getrusage"},
+ {__NR_getsid, "getsid"},
+ {__NR_getsockname, "getsockname"},
+ {__NR_getsockopt, "getsockopt"},
+ {__NR_get_thread_area, "get_thread_area"},
+ {__NR_gettid, "gettid"},
+ {__NR_gettimeofday, "gettimeofday"},
+ {__NR_getuid, "getuid"},
+ {__NR_getxattr, "getxattr"},
+ {__NR_init_module, "init_module"},
+ {__NR_inotify_add_watch, "inotify_add_watch"},
+ {__NR_inotify_init, "inotify_init"},
+ {__NR_inotify_init1, "inotify_init1"},
+ {__NR_inotify_rm_watch, "inotify_rm_watch"},
+ {__NR_io_cancel, "io_cancel"},
+ {__NR_ioctl, "ioctl"},
+ {__NR_io_destroy, "io_destroy"},
+ {__NR_io_getevents, "io_getevents"},
+ {__NR_ioperm, "ioperm"},
+ {__NR_iopl, "iopl"},
+ {__NR_ioprio_get, "ioprio_get"},
+ {__NR_ioprio_set, "ioprio_set"},
+ {__NR_io_setup, "io_setup"},
+ {__NR_io_submit, "io_submit"},
+ {__NR_kcmp, "kcmp"},
+ {__NR_kexec_file_load, "kexec_file_load"},
+ {__NR_kexec_load, "kexec_load"},
+ {__NR_keyctl, "keyctl"},
+ {__NR_kill, "kill"},
+ {__NR_lchown, "lchown"},
+ {__NR_lgetxattr, "lgetxattr"},
+ {__NR_link, "link"},
+ {__NR_linkat, "linkat"},
+ {__NR_listen, "listen"},
+ {__NR_listxattr, "listxattr"},
+ {__NR_llistxattr, "llistxattr"},
+ {__NR_lookup_dcookie, "lookup_dcookie"},
+ {__NR_lremovexattr, "lremovexattr"},
+ {__NR_lseek, "lseek"},
+ {__NR_lsetxattr, "lsetxattr"},
+ {__NR_lstat, "lstat"},
+ {__NR_madvise, "madvise"},
+ {__NR_mbind, "mbind"},
+ {__NR_membarrier, "membarrier"},
+ {__NR_memfd_create, "memfd_create"},
+ {__NR_migrate_pages, "migrate_pages"},
+ {__NR_mincore, "mincore"},
+ {__NR_mkdir, "mkdir"},
+ {__NR_mkdirat, "mkdirat"},
+ {__NR_mknod, "mknod"},
+ {__NR_mknodat, "mknodat"},
+ {__NR_mlock, "mlock"},
+ {__NR_mlock2, "mlock2"},
+ {__NR_mlockall, "mlockall"},
+ {__NR_mmap, "mmap"},
+ {__NR_modify_ldt, "modify_ldt"},
+ {__NR_mount, "mount"},
+ {__NR_move_pages, "move_pages"},
+ {__NR_mprotect, "mprotect"},
+ {__NR_mq_getsetattr, "mq_getsetattr"},
+ {__NR_mq_notify, "mq_notify"},
+ {__NR_mq_open, "mq_open"},
+ {__NR_mq_timedreceive, "mq_timedreceive"},
+ {__NR_mq_timedsend, "mq_timedsend"},
+ {__NR_mq_unlink, "mq_unlink"},
+ {__NR_mremap, "mremap"},
+ {__NR_msgctl, "msgctl"},
+ {__NR_msgget, "msgget"},
+ {__NR_msgrcv, "msgrcv"},
+ {__NR_msgsnd, "msgsnd"},
+ {__NR_msync, "msync"},
+ {__NR_munlock, "munlock"},
+ {__NR_munlockall, "munlockall"},
+ {__NR_munmap, "munmap"},
+ {__NR_name_to_handle_at, "name_to_handle_at"},
+ {__NR_nanosleep, "nanosleep"},
+ {__NR_newfstatat, "newfstatat"},
+ {__NR_nfsservctl, "nfsservctl"},
+ {__NR_open, "open"},
+ {__NR_openat, "openat"},
+ {__NR_open_by_handle_at, "open_by_handle_at"},
+ {__NR_pause, "pause"},
+ {__NR_perf_event_open, "perf_event_open"},
+ {__NR_personality, "personality"},
+ {__NR_pipe, "pipe"},
+ {__NR_pipe2, "pipe2"},
+ {__NR_pivot_root, "pivot_root"},
+#ifdef __NR_pkey_alloc
+ {__NR_pkey_alloc, "pkey_alloc"},
+#endif
+#ifdef __NR_pkey_free
+ {__NR_pkey_free, "pkey_free"},
+#endif
+#ifdef __NR_pkey_mprotect
+ {__NR_pkey_mprotect, "pkey_mprotect"},
+#endif
+ {__NR_poll, "poll"},
+ {__NR_ppoll, "ppoll"},
+ {__NR_prctl, "prctl"},
+ {__NR_pread64, "pread64"},
+ {__NR_preadv, "preadv"},
+#ifdef __NR_preadv2
+ {__NR_preadv2, "preadv2"},
+#endif
+ {__NR_prlimit64, "prlimit64"},
+ {__NR_process_vm_readv, "process_vm_readv"},
+ {__NR_process_vm_writev, "process_vm_writev"},
+ {__NR_pselect6, "pselect6"},
+ {__NR_ptrace, "ptrace"},
+ {__NR_putpmsg, "putpmsg"},
+ {__NR_pwrite64, "pwrite64"},
+ {__NR_pwritev, "pwritev"},
+#ifdef __NR_pwritev2
+ {__NR_pwritev2, "pwritev2"},
+#endif
+ {__NR_query_module, "query_module"},
+ {__NR_quotactl, "quotactl"},
+ {__NR_read, "read"},
+ {__NR_readahead, "readahead"},
+ {__NR_readlink, "readlink"},
+ {__NR_readlinkat, "readlinkat"},
+ {__NR_readv, "readv"},
+ {__NR_reboot, "reboot"},
+ {__NR_recvfrom, "recvfrom"},
+ {__NR_recvmmsg, "recvmmsg"},
+ {__NR_recvmsg, "recvmsg"},
+ {__NR_remap_file_pages, "remap_file_pages"},
+ {__NR_removexattr, "removexattr"},
+ {__NR_rename, "rename"},
+ {__NR_renameat, "renameat"},
+ {__NR_renameat2, "renameat2"},
+ {__NR_request_key, "request_key"},
+ {__NR_restart_syscall, "restart_syscall"},
+ {__NR_rmdir, "rmdir"},
+ {__NR_rt_sigaction, "rt_sigaction"},
+ {__NR_rt_sigpending, "rt_sigpending"},
+ {__NR_rt_sigprocmask, "rt_sigprocmask"},
+ {__NR_rt_sigqueueinfo, "rt_sigqueueinfo"},
+ {__NR_rt_sigreturn, "rt_sigreturn"},
+ {__NR_rt_sigsuspend, "rt_sigsuspend"},
+ {__NR_rt_sigtimedwait, "rt_sigtimedwait"},
+ {__NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo"},
+ {__NR_sched_getaffinity, "sched_getaffinity"},
+ {__NR_sched_getattr, "sched_getattr"},
+ {__NR_sched_getparam, "sched_getparam"},
+ {__NR_sched_get_priority_max, "sched_get_priority_max"},
+ {__NR_sched_get_priority_min, "sched_get_priority_min"},
+ {__NR_sched_getscheduler, "sched_getscheduler"},
+ {__NR_sched_rr_get_interval, "sched_rr_get_interval"},
+ {__NR_sched_setaffinity, "sched_setaffinity"},
+ {__NR_sched_setattr, "sched_setattr"},
+ {__NR_sched_setparam, "sched_setparam"},
+ {__NR_sched_setscheduler, "sched_setscheduler"},
+ {__NR_sched_yield, "sched_yield"},
+ {__NR_seccomp, "seccomp"},
+ {__NR_security, "security"},
+ {__NR_select, "select"},
+ {__NR_semctl, "semctl"},
+ {__NR_semget, "semget"},
+ {__NR_semop, "semop"},
+ {__NR_semtimedop, "semtimedop"},
+ {__NR_sendfile, "sendfile"},
+ {__NR_sendmmsg, "sendmmsg"},
+ {__NR_sendmsg, "sendmsg"},
+ {__NR_sendto, "sendto"},
+ {__NR_setdomainname, "setdomainname"},
+ {__NR_setfsgid, "setfsgid"},
+ {__NR_setfsuid, "setfsuid"},
+ {__NR_setgid, "setgid"},
+ {__NR_setgroups, "setgroups"},
+ {__NR_sethostname, "sethostname"},
+ {__NR_setitimer, "setitimer"},
+ {__NR_set_mempolicy, "set_mempolicy"},
+ {__NR_setns, "setns"},
+ {__NR_setpgid, "setpgid"},
+ {__NR_setpriority, "setpriority"},
+ {__NR_setregid, "setregid"},
+ {__NR_setresgid, "setresgid"},
+ {__NR_setresuid, "setresuid"},
+ {__NR_setreuid, "setreuid"},
+ {__NR_setrlimit, "setrlimit"},
+ {__NR_set_robust_list, "set_robust_list"},
+ {__NR_setsid, "setsid"},
+ {__NR_setsockopt, "setsockopt"},
+ {__NR_set_thread_area, "set_thread_area"},
+ {__NR_set_tid_address, "set_tid_address"},
+ {__NR_settimeofday, "settimeofday"},
+ {__NR_setuid, "setuid"},
+ {__NR_setxattr, "setxattr"},
+ {__NR_shmat, "shmat"},
+ {__NR_shmctl, "shmctl"},
+ {__NR_shmdt, "shmdt"},
+ {__NR_shmget, "shmget"},
+ {__NR_shutdown, "shutdown"},
+ {__NR_sigaltstack, "sigaltstack"},
+ {__NR_signalfd, "signalfd"},
+ {__NR_signalfd4, "signalfd4"},
+ {__NR_socket, "socket"},
+ {__NR_socketpair, "socketpair"},
+ {__NR_splice, "splice"},
+ {__NR_stat, "stat"},
+ {__NR_statfs, "statfs"},
+ {__NR_statx, "statx"},
+ {__NR_swapoff, "swapoff"},
+ {__NR_swapon, "swapon"},
+ {__NR_symlink, "symlink"},
+ {__NR_symlinkat, "symlinkat"},
+ {__NR_sync, "sync"},
+ {__NR_sync_file_range, "sync_file_range"},
+ {__NR_syncfs, "syncfs"},
+ {__NR__sysctl, "_sysctl"},
+ {__NR_sysfs, "sysfs"},
+ {__NR_sysinfo, "sysinfo"},
+ {__NR_syslog, "syslog"},
+ {__NR_tee, "tee"},
+ {__NR_tgkill, "tgkill"},
+ {__NR_time, "time"},
+ {__NR_timer_create, "timer_create"},
+ {__NR_timer_delete, "timer_delete"},
+ {__NR_timerfd_create, "timerfd_create"},
+ {__NR_timerfd_gettime, "timerfd_gettime"},
+ {__NR_timerfd_settime, "timerfd_settime"},
+ {__NR_timer_getoverrun, "timer_getoverrun"},
+ {__NR_timer_gettime, "timer_gettime"},
+ {__NR_timer_settime, "timer_settime"},
+ {__NR_times, "times"},
+ {__NR_tkill, "tkill"},
+ {__NR_truncate, "truncate"},
+ {__NR_tuxcall, "tuxcall"},
+ {__NR_umask, "umask"},
+ {__NR_umount2, "umount2"},
+ {__NR_uname, "uname"},
+ {__NR_unlink, "unlink"},
+ {__NR_unlinkat, "unlinkat"},
+ {__NR_unshare, "unshare"},
+ {__NR_uselib, "uselib"},
+ {__NR_userfaultfd, "userfaultfd"},
+ {__NR_ustat, "ustat"},
+ {__NR_utime, "utime"},
+ {__NR_utimensat, "utimensat"},
+ {__NR_utimes, "utimes"},
+ {__NR_vfork, "vfork"},
+ {__NR_vhangup, "vhangup"},
+ {__NR_vmsplice, "vmsplice"},
+ {__NR_vserver, "vserver"},
+ {__NR_wait4, "wait4"},
+ {__NR_waitid, "waitid"},
+ {__NR_write, "write"},
+ {__NR_writev, "writev"},
+};
+
+std::string GetLinuxSyscallName(uint32_t syscall_number)
+{
+ const auto element = LINUX_SYSCALLS.find(syscall_number);
+ if (element != LINUX_SYSCALLS.end()) {
+ return element->second;
+ }
+ return "*unknown*";
+}
+
+// See Linux kernel developer Kees Cook's seccomp guide at <https://outflux.net/teach-seccomp/> for
+// an accessible introduction to using seccomp.
+//
+// This function largely follows <https://outflux.net/teach-seccomp/step-3/syscall-reporter.c> and
+// <https://outflux.net/teach-seccomp/step-3/seccomp-bpf.h>.
+//
+// Seccomp BPF resources:
+// * Seccomp BPF documentation: <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html>
+// * seccomp(2) manual page: <https://www.kernel.org/doc/man-pages/online/pages/man2/seccomp.2.html>
+// * Seccomp BPF demo code samples: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/samples/seccomp>
+void SyscallSandboxDebugSignalHandler(int, siginfo_t* signal_info, void* void_signal_context)
+{
+ // The si_code field inside the siginfo_t argument that is passed to a SA_SIGINFO signal handler
+ // is a value indicating why the signal was sent.
+ //
+ // The following value can be placed in si_code for a SIGSYS signal:
+ // * SYS_SECCOMP (since Linux 3.5): Triggered by a seccomp(2) filter rule.
+ constexpr int32_t SYS_SECCOMP_SI_CODE{1};
+ assert(signal_info->si_code == SYS_SECCOMP_SI_CODE);
+
+ // The ucontext_t structure contains signal context information that was saved on the user-space
+ // stack by the kernel.
+ const ucontext_t* signal_context = static_cast<ucontext_t*>(void_signal_context);
+ assert(signal_context != nullptr);
+
+ std::set_new_handler(std::terminate);
+ // Portability note: REG_RAX is Linux x86_64 specific.
+ const uint32_t syscall_number = static_cast<uint32_t>(signal_context->uc_mcontext.gregs[REG_RAX]);
+ const std::string syscall_name = GetLinuxSyscallName(syscall_number);
+ const std::string thread_name = !util::ThreadGetInternalName().empty() ? util::ThreadGetInternalName() : "*unnamed*";
+ const std::string error_message = strprintf("ERROR: The syscall \"%s\" (syscall number %d) is not allowed by the syscall sandbox in thread \"%s\". Please report.", syscall_name, syscall_number, thread_name);
+ tfm::format(std::cerr, "%s\n", error_message);
+ LogPrintf("%s\n", error_message);
+ std::terminate();
+}
+
+// This function largely follows install_syscall_reporter from Kees Cook's seccomp guide:
+// <https://outflux.net/teach-seccomp/step-3/syscall-reporter.c>
+bool SetupSyscallSandboxDebugHandler()
+{
+ struct sigaction action = {};
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGSYS);
+ action.sa_sigaction = &SyscallSandboxDebugSignalHandler;
+ action.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGSYS, &action, nullptr) < 0) {
+ return false;
+ }
+ if (sigprocmask(SIG_UNBLOCK, &mask, nullptr)) {
+ return false;
+ }
+ return true;
+}
+
+enum class SyscallSandboxAction {
+ KILL_PROCESS,
+ INVOKE_SIGNAL_HANDLER,
+};
+
+class SeccompPolicyBuilder
+{
+ std::set<uint32_t> allowed_syscalls;
+
+public:
+ SeccompPolicyBuilder()
+ {
+ // Allowed by default.
+ AllowAddressSpaceAccess();
+ AllowEpoll();
+ AllowEventFd();
+ AllowFutex();
+ AllowGeneralIo();
+ AllowGetRandom();
+ AllowGetSimpleId();
+ AllowGetTime();
+ AllowGlobalProcessEnvironment();
+ AllowGlobalSystemStatus();
+ AllowKernelInternalApi();
+ AllowNetworkSocketInformation();
+ AllowOperationOnExistingFileDescriptor();
+ AllowPipe();
+ AllowPrctl();
+ AllowProcessStartOrDeath();
+ AllowScheduling();
+ AllowSignalHandling();
+ AllowSleep();
+ AllowUmask();
+ }
+
+ void AllowAddressSpaceAccess()
+ {
+ allowed_syscalls.insert(__NR_brk); // change data segment size
+ allowed_syscalls.insert(__NR_madvise); // give advice about use of memory
+ allowed_syscalls.insert(__NR_membarrier); // issue memory barriers on a set of threads
+ allowed_syscalls.insert(__NR_mincore); // check if virtual memory is in RAM
+ allowed_syscalls.insert(__NR_mlock); // lock memory
+ allowed_syscalls.insert(__NR_mmap); // map files or devices into memory
+ allowed_syscalls.insert(__NR_mprotect); // set protection on a region of memory
+ allowed_syscalls.insert(__NR_mremap); // remap a file in memory
+ allowed_syscalls.insert(__NR_munlock); // unlock memory
+ allowed_syscalls.insert(__NR_munmap); // unmap files or devices into memory
+ }
+
+ void AllowEpoll()
+ {
+ allowed_syscalls.insert(__NR_epoll_create1); // open an epoll file descriptor
+ allowed_syscalls.insert(__NR_epoll_ctl); // control interface for an epoll file descriptor
+ allowed_syscalls.insert(__NR_epoll_pwait); // wait for an I/O event on an epoll file descriptor
+ allowed_syscalls.insert(__NR_epoll_wait); // wait for an I/O event on an epoll file descriptor
+ }
+
+ void AllowEventFd()
+ {
+ allowed_syscalls.insert(__NR_eventfd2); // create a file descriptor for event notification
+ }
+
+ void AllowFileSystem()
+ {
+ allowed_syscalls.insert(__NR_access); // check user's permissions for a file
+ allowed_syscalls.insert(__NR_chdir); // change working directory
+ allowed_syscalls.insert(__NR_chmod); // change permissions of a file
+ allowed_syscalls.insert(__NR_copy_file_range); // copy a range of data from one file to another
+ allowed_syscalls.insert(__NR_fallocate); // manipulate file space
+ allowed_syscalls.insert(__NR_fchmod); // change permissions of a file
+ allowed_syscalls.insert(__NR_fchown); // change ownership of a file
+ allowed_syscalls.insert(__NR_fdatasync); // synchronize a file's in-core state with storage device
+ allowed_syscalls.insert(__NR_flock); // apply or remove an advisory lock on an open file
+ allowed_syscalls.insert(__NR_fstat); // get file status
+ allowed_syscalls.insert(__NR_newfstatat); // get file status
+ allowed_syscalls.insert(__NR_fsync); // synchronize a file's in-core state with storage device
+ allowed_syscalls.insert(__NR_ftruncate); // truncate a file to a specified length
+ allowed_syscalls.insert(__NR_getcwd); // get current working directory
+ allowed_syscalls.insert(__NR_getdents); // get directory entries
+ allowed_syscalls.insert(__NR_getdents64); // get directory entries
+ allowed_syscalls.insert(__NR_lstat); // get file status
+ allowed_syscalls.insert(__NR_mkdir); // create a directory
+ allowed_syscalls.insert(__NR_open); // open and possibly create a file
+ allowed_syscalls.insert(__NR_openat); // open and possibly create a file
+ allowed_syscalls.insert(__NR_readlink); // read value of a symbolic link
+ allowed_syscalls.insert(__NR_rename); // change the name or location of a file
+ allowed_syscalls.insert(__NR_rmdir); // delete a directory
+ allowed_syscalls.insert(__NR_stat); // get file status
+ allowed_syscalls.insert(__NR_statfs); // get filesystem statistics
+ allowed_syscalls.insert(__NR_statx); // get file status (extended)
+ allowed_syscalls.insert(__NR_unlink); // delete a name and possibly the file it refers to
+ }
+
+ void AllowFutex()
+ {
+ allowed_syscalls.insert(__NR_futex); // fast user-space locking
+ allowed_syscalls.insert(__NR_set_robust_list); // set list of robust futexes
+ }
+
+ void AllowGeneralIo()
+ {
+ allowed_syscalls.insert(__NR_ioctl); // control device
+ allowed_syscalls.insert(__NR_lseek); // reposition read/write file offset
+ allowed_syscalls.insert(__NR_poll); // wait for some event on a file descriptor
+ allowed_syscalls.insert(__NR_ppoll); // wait for some event on a file descriptor
+ allowed_syscalls.insert(__NR_pread64); // read from a file descriptor at a given offset
+ allowed_syscalls.insert(__NR_pwrite64); // write to a file descriptor at a given offset
+ allowed_syscalls.insert(__NR_read); // read from a file descriptor
+ allowed_syscalls.insert(__NR_readv); // read data into multiple buffers
+ allowed_syscalls.insert(__NR_recvfrom); // receive a message from a socket
+ allowed_syscalls.insert(__NR_recvmsg); // receive a message from a socket
+ allowed_syscalls.insert(__NR_select); // synchronous I/O multiplexing
+ allowed_syscalls.insert(__NR_sendmmsg); // send multiple messages on a socket
+ allowed_syscalls.insert(__NR_sendmsg); // send a message on a socket
+ allowed_syscalls.insert(__NR_sendto); // send a message on a socket
+ allowed_syscalls.insert(__NR_write); // write to a file descriptor
+ allowed_syscalls.insert(__NR_writev); // write data into multiple buffers
+ }
+
+ void AllowGetRandom()
+ {
+ allowed_syscalls.insert(__NR_getrandom); // obtain a series of random bytes
+ }
+
+ void AllowGetSimpleId()
+ {
+ allowed_syscalls.insert(__NR_getegid); // get group identity
+ allowed_syscalls.insert(__NR_geteuid); // get user identity
+ allowed_syscalls.insert(__NR_getgid); // get group identity
+ allowed_syscalls.insert(__NR_getpgid); // get process group
+ allowed_syscalls.insert(__NR_getpid); // get process identification
+ allowed_syscalls.insert(__NR_getppid); // get process identification
+ allowed_syscalls.insert(__NR_getresgid); // get real, effective and saved group IDs
+ allowed_syscalls.insert(__NR_getresuid); // get real, effective and saved user IDs
+ allowed_syscalls.insert(__NR_getsid); // get session ID
+ allowed_syscalls.insert(__NR_gettid); // get thread identification
+ allowed_syscalls.insert(__NR_getuid); // get user identity
+ }
+
+ void AllowGetTime()
+ {
+ allowed_syscalls.insert(__NR_clock_getres); // find the resolution (precision) of the specified clock
+ allowed_syscalls.insert(__NR_clock_gettime); // retrieve the time of the specified clock
+ }
+
+ void AllowGlobalProcessEnvironment()
+ {
+ allowed_syscalls.insert(__NR_getrlimit); // get resource limits
+ allowed_syscalls.insert(__NR_getrusage); // get resource usage
+ allowed_syscalls.insert(__NR_prlimit64); // get/set resource limits
+ }
+
+ void AllowGlobalSystemStatus()
+ {
+ allowed_syscalls.insert(__NR_sysinfo); // return system information
+ allowed_syscalls.insert(__NR_uname); // get name and information about current kernel
+ }
+
+ void AllowKernelInternalApi()
+ {
+ allowed_syscalls.insert(__NR_restart_syscall); // restart a system call after interruption by a stop signal
+ }
+
+ void AllowNetwork()
+ {
+ allowed_syscalls.insert(__NR_accept); // accept a connection on a socket
+ allowed_syscalls.insert(__NR_accept4); // accept a connection on a socket
+ allowed_syscalls.insert(__NR_bind); // bind a name to a socket
+ allowed_syscalls.insert(__NR_connect); // initiate a connection on a socket
+ allowed_syscalls.insert(__NR_listen); // listen for connections on a socket
+ allowed_syscalls.insert(__NR_setsockopt); // set options on sockets
+ allowed_syscalls.insert(__NR_socket); // create an endpoint for communication
+ allowed_syscalls.insert(__NR_socketpair); // create a pair of connected sockets
+ }
+
+ void AllowNetworkSocketInformation()
+ {
+ allowed_syscalls.insert(__NR_getpeername); // get name of connected peer socket
+ allowed_syscalls.insert(__NR_getsockname); // get socket name
+ allowed_syscalls.insert(__NR_getsockopt); // get options on sockets
+ }
+
+ void AllowOperationOnExistingFileDescriptor()
+ {
+ allowed_syscalls.insert(__NR_close); // close a file descriptor
+ allowed_syscalls.insert(__NR_dup); // duplicate a file descriptor
+ allowed_syscalls.insert(__NR_dup2); // duplicate a file descriptor
+ allowed_syscalls.insert(__NR_fcntl); // manipulate file descriptor
+ allowed_syscalls.insert(__NR_shutdown); // shut down part of a full-duplex connection
+ }
+
+ void AllowPipe()
+ {
+ allowed_syscalls.insert(__NR_pipe); // create pipe
+ allowed_syscalls.insert(__NR_pipe2); // create pipe
+ }
+
+ void AllowPrctl()
+ {
+ allowed_syscalls.insert(__NR_arch_prctl); // set architecture-specific thread state
+ allowed_syscalls.insert(__NR_prctl); // operations on a process
+ }
+
+ void AllowProcessStartOrDeath()
+ {
+ allowed_syscalls.insert(__NR_clone); // create a child process
+ allowed_syscalls.insert(__NR_clone3); // create a child process
+ allowed_syscalls.insert(__NR_exit); // terminate the calling process
+ allowed_syscalls.insert(__NR_exit_group); // exit all threads in a process
+ allowed_syscalls.insert(__NR_fork); // create a child process
+ allowed_syscalls.insert(__NR_tgkill); // send a signal to a thread
+ allowed_syscalls.insert(__NR_wait4); // wait for process to change state, BSD style
+ }
+
+ void AllowScheduling()
+ {
+ allowed_syscalls.insert(__NR_sched_getaffinity); // set a thread's CPU affinity mask
+ allowed_syscalls.insert(__NR_sched_getparam); // get scheduling parameters
+ allowed_syscalls.insert(__NR_sched_getscheduler); // get scheduling policy/parameters
+ allowed_syscalls.insert(__NR_sched_setscheduler); // set scheduling policy/parameters
+ allowed_syscalls.insert(__NR_sched_yield); // yield the processor
+ }
+
+ void AllowSignalHandling()
+ {
+ allowed_syscalls.insert(__NR_rt_sigaction); // examine and change a signal action
+ allowed_syscalls.insert(__NR_rt_sigprocmask); // examine and change blocked signals
+ allowed_syscalls.insert(__NR_rt_sigreturn); // return from signal handler and cleanup stack frame
+ allowed_syscalls.insert(__NR_sigaltstack); // set and/or get signal stack context
+ }
+
+ void AllowSleep()
+ {
+ allowed_syscalls.insert(__NR_clock_nanosleep); // high-resolution sleep with specifiable clock
+ allowed_syscalls.insert(__NR_nanosleep); // high-resolution sleep
+ }
+
+ void AllowUmask()
+ {
+ allowed_syscalls.insert(__NR_umask); // set file mode creation mask
+ }
+
+ // See Linux kernel developer Kees Cook's seccomp guide at <https://outflux.net/teach-seccomp/>
+ // for an accessible introduction to using seccomp.
+ //
+ // This function largely follows <https://outflux.net/teach-seccomp/step-3/seccomp-bpf.h>.
+ std::vector<sock_filter> BuildFilter(SyscallSandboxAction default_action)
+ {
+ std::vector<sock_filter> bpf_policy;
+ // See VALIDATE_ARCHITECTURE in seccomp-bpf.h referenced above.
+ bpf_policy.push_back(BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, arch)));
+ // Portability note: AUDIT_ARCH_X86_64 is Linux x86_64 specific.
+ bpf_policy.push_back(BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, AUDIT_ARCH_X86_64, 1, 0));
+ bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS));
+ // See EXAMINE_SYSCALL in seccomp-bpf.h referenced above.
+ bpf_policy.push_back(BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, nr)));
+ for (const uint32_t allowed_syscall : allowed_syscalls) {
+ // See ALLOW_SYSCALL in seccomp-bpf.h referenced above.
+ bpf_policy.push_back(BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, allowed_syscall, 0, 1));
+ bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW));
+ }
+ switch (default_action) {
+ case SyscallSandboxAction::KILL_PROCESS:
+ // Disallow syscall and kill the process.
+ //
+ // See KILL_PROCESS in seccomp-bpf.h referenced above.
+ //
+ // Note that we're using SECCOMP_RET_KILL_PROCESS (kill the process) instead
+ // of SECCOMP_RET_KILL_THREAD (kill the thread). The SECCOMP_RET_KILL_PROCESS
+ // action was introduced in Linux 4.14.
+ //
+ // SECCOMP_RET_KILL_PROCESS: Results in the entire process exiting immediately without
+ // executing the system call.
+ //
+ // SECCOMP_RET_KILL_PROCESS documentation:
+ // <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html>
+ bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS));
+ break;
+ case SyscallSandboxAction::INVOKE_SIGNAL_HANDLER:
+ // Disallow syscall and force a SIGSYS to trigger syscall debug reporter.
+ //
+ // SECCOMP_RET_TRAP: Results in the kernel sending a SIGSYS signal to the triggering
+ // task without executing the system call.
+ //
+ // SECCOMP_RET_TRAP documentation:
+ // <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html>
+ bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRAP));
+ break;
+ }
+ return bpf_policy;
+ }
+};
+} // namespace
+
+bool SetupSyscallSandbox(bool log_syscall_violation_before_terminating)
+{
+ assert(!g_syscall_sandbox_enabled && "SetupSyscallSandbox(...) should only be called once.");
+ g_syscall_sandbox_enabled = true;
+ g_syscall_sandbox_log_violation_before_terminating = log_syscall_violation_before_terminating;
+ if (log_syscall_violation_before_terminating) {
+ if (!SetupSyscallSandboxDebugHandler()) {
+ return false;
+ }
+ }
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION);
+ return true;
+}
+
+void TestDisallowedSandboxCall()
+{
+ // The getgroups syscall is assumed NOT to be allowed by the syscall sandbox policy.
+ std::array<gid_t, 1> groups;
+ [[maybe_unused]] int32_t ignored = getgroups(groups.size(), groups.data());
+}
+#endif // defined(USE_SYSCALL_SANDBOX)
+
+void SetSyscallSandboxPolicy(SyscallSandboxPolicy syscall_policy)
+{
+#if defined(USE_SYSCALL_SANDBOX)
+ if (!g_syscall_sandbox_enabled) {
+ return;
+ }
+ SeccompPolicyBuilder seccomp_policy_builder;
+ switch (syscall_policy) {
+ case SyscallSandboxPolicy::INITIALIZATION: // Thread: main thread (state: init)
+ // SyscallSandboxPolicy::INITIALIZATION is the first policy loaded.
+ //
+ // Subsequently loaded policies can reduce the abilities further, but
+ // abilities can never be regained.
+ //
+ // SyscallSandboxPolicy::INITIALIZATION must thus be a superset of all
+ // other policies.
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::INITIALIZATION_DNS_SEED: // Thread: dnsseed
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::INITIALIZATION_LOAD_BLOCKS: // Thread: loadblk
+ seccomp_policy_builder.AllowFileSystem();
+ break;
+ case SyscallSandboxPolicy::INITIALIZATION_MAP_PORT: // Thread: mapport
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::MESSAGE_HANDLER: // Thread: msghand
+ seccomp_policy_builder.AllowFileSystem();
+ break;
+ case SyscallSandboxPolicy::NET: // Thread: net
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::NET_ADD_CONNECTION: // Thread: addcon
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::NET_HTTP_SERVER: // Thread: http
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::NET_HTTP_SERVER_WORKER: // Thread: httpworker.<N>
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::NET_OPEN_CONNECTION: // Thread: opencon
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::SCHEDULER: // Thread: scheduler
+ seccomp_policy_builder.AllowFileSystem();
+ break;
+ case SyscallSandboxPolicy::TOR_CONTROL: // Thread: torcontrol
+ seccomp_policy_builder.AllowFileSystem();
+ seccomp_policy_builder.AllowNetwork();
+ break;
+ case SyscallSandboxPolicy::TX_INDEX: // Thread: txindex
+ seccomp_policy_builder.AllowFileSystem();
+ break;
+ case SyscallSandboxPolicy::VALIDATION_SCRIPT_CHECK: // Thread: scriptch.<N>
+ break;
+ case SyscallSandboxPolicy::SHUTOFF: // Thread: main thread (state: shutoff)
+ seccomp_policy_builder.AllowFileSystem();
+ break;
+ }
+
+ const SyscallSandboxAction default_action = g_syscall_sandbox_log_violation_before_terminating ? SyscallSandboxAction::INVOKE_SIGNAL_HANDLER : SyscallSandboxAction::KILL_PROCESS;
+ std::vector<sock_filter> filter = seccomp_policy_builder.BuildFilter(default_action);
+ const sock_fprog prog = {
+ .len = static_cast<uint16_t>(filter.size()),
+ .filter = filter.data(),
+ };
+ // Do not allow abilities to be regained after being dropped.
+ //
+ // PR_SET_NO_NEW_PRIVS documentation: <https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html>
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+ throw std::runtime_error("Syscall sandbox enforcement failed: prctl(PR_SET_NO_NEW_PRIVS)");
+ }
+ // Install seccomp-bpf syscall filter.
+ //
+ // PR_SET_SECCOMP documentation: <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html>
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) {
+ throw std::runtime_error("Syscall sandbox enforcement failed: prctl(PR_SET_SECCOMP)");
+ }
+
+ const std::string thread_name = !util::ThreadGetInternalName().empty() ? util::ThreadGetInternalName() : "*unnamed*";
+ LogPrint(BCLog::UTIL, "Syscall filter installed for thread \"%s\"\n", thread_name);
+#endif // defined(USE_SYSCALL_SANDBOX)
+}
diff --git a/src/util/syscall_sandbox.h b/src/util/syscall_sandbox.h
new file mode 100644
index 0000000000..0a0c964f94
--- /dev/null
+++ b/src/util/syscall_sandbox.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_SYSCALL_SANDBOX_H
+#define BITCOIN_UTIL_SYSCALL_SANDBOX_H
+
+enum class SyscallSandboxPolicy {
+ // 1. Initialization
+ INITIALIZATION,
+ INITIALIZATION_DNS_SEED,
+ INITIALIZATION_LOAD_BLOCKS,
+ INITIALIZATION_MAP_PORT,
+
+ // 2. Steady state (non-initialization, non-shutdown)
+ MESSAGE_HANDLER,
+ NET,
+ NET_ADD_CONNECTION,
+ NET_HTTP_SERVER,
+ NET_HTTP_SERVER_WORKER,
+ NET_OPEN_CONNECTION,
+ SCHEDULER,
+ TOR_CONTROL,
+ TX_INDEX,
+ VALIDATION_SCRIPT_CHECK,
+
+ // 3. Shutdown
+ SHUTOFF,
+};
+
+//! Force the current thread (and threads created from the current thread) into a restricted-service
+//! operating mode where only a subset of all syscalls are available.
+//!
+//! Subsequent calls to this function can reduce the abilities further, but abilities can never be
+//! regained.
+//!
+//! This function is a no-op unless SetupSyscallSandbox(...) has been called.
+//!
+//! SetupSyscallSandbox(...) is called during bitcoind initialization if Bitcoin Core was compiled
+//! with seccomp-bpf support (--with-seccomp) *and* the parameter -sandbox=<mode> was passed to
+//! bitcoind.
+//!
+//! This experimental feature is available under Linux x86_64 only.
+void SetSyscallSandboxPolicy(SyscallSandboxPolicy syscall_policy);
+
+#if defined(USE_SYSCALL_SANDBOX)
+//! Setup and enable the experimental syscall sandbox for the running process.
+//!
+//! SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION) is called as part of
+//! SetupSyscallSandbox(...).
+[[nodiscard]] bool SetupSyscallSandbox(bool log_syscall_violation_before_terminating);
+
+//! Invoke a disallowed syscall. Use for testing purposes.
+void TestDisallowedSandboxCall();
+#endif // defined(USE_SYSCALL_SANDBOX)
+
+#endif // BITCOIN_UTIL_SYSCALL_SANDBOX_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 08f62f1da7..79c08816fa 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -158,16 +158,14 @@ std::streampos GetFileSize(const char* path, std::streamsize max) {
/**
* Interpret a string argument as a boolean.
*
- * The definition of atoi() requires that non-numeric string values like "foo",
- * return 0. This means that if a user unintentionally supplies a non-integer
- * argument here, the return value is always false. This means that -foo=false
- * does what the user probably expects, but -foo=true is well defined but does
- * not do what they probably expected.
+ * The definition of LocaleIndependentAtoi<int>() requires that non-numeric string values
+ * like "foo", return 0. This means that if a user unintentionally supplies a
+ * non-integer argument here, the return value is always false. This means that
+ * -foo=false does what the user probably expects, but -foo=true is well defined
+ * but does not do what they probably expected.
*
- * The return value of atoi() is undefined when given input not representable as
- * an int. On most systems this means string value between "-2147483648" and
- * "2147483647" are well defined (this method will return true). Setting
- * -txindex=2147483648 on most systems, however, is probably undefined.
+ * The return value of LocaleIndependentAtoi<int>(...) is zero when given input not
+ * representable as an int.
*
* For a more extensive discussion of this topic (and a wide range of opinions
* on the Right Way to change this code), see PR12713.
@@ -176,7 +174,7 @@ static bool InterpretBool(const std::string& strValue)
{
if (strValue.empty())
return true;
- return (atoi(strValue) != 0);
+ return (LocaleIndependentAtoi<int>(strValue) != 0);
}
static std::string SettingName(const std::string& arg)
@@ -591,10 +589,10 @@ std::string ArgsManager::GetArg(const std::string& strArg, const std::string& st
return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
}
-int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
+int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
- return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str());
+ return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : LocaleIndependentAtoi<int64_t>(value.get_str());
}
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
diff --git a/src/util/system.h b/src/util/system.h
index 3c1399629c..2e217f6f90 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -327,7 +327,7 @@ protected:
* @param nDefault (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
- int64_t GetArg(const std::string& strArg, int64_t nDefault) const;
+ int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const;
/**
* Return boolean argument or default value
diff --git a/src/validation.cpp b/src/validation.cpp
index cc87f98913..14dcd2c24b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -9,6 +9,7 @@
#include <chain.h>
#include <chainparams.h>
#include <checkqueue.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_check.h>
@@ -24,6 +25,7 @@
#include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/ui_interface.h>
+#include <node/utxo_snapshot.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <policy/settings.h>
@@ -372,8 +374,8 @@ void CChainState::MaybeUpdateMempoolForReorg(
LimitMempoolSize(
*m_mempool,
this->CoinsTip(),
- gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
- std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
+ std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
}
/**
@@ -424,10 +426,10 @@ class MemPoolAccept
{
public:
explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
- m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
- m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
- m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
- m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
+ m_limit_ancestors(gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
+ m_limit_ancestor_size(gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
+ m_limit_descendants(gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
+ m_limit_descendant_size(gArgs.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -472,7 +474,6 @@ private:
std::unique_ptr<CTxMemPoolEntry> m_entry;
std::list<CTransactionRef> m_replaced_transactions;
- bool m_replacement_transaction;
CAmount m_base_fees;
CAmount m_modified_fees;
/** Total modified fees of all transactions being replaced. */
@@ -509,7 +510,7 @@ private:
// Compare a package's feerate against minimum allowed.
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
{
- CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
+ CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
}
@@ -554,7 +555,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
- bool& fReplacementTransaction = ws.m_replacement_transaction;
CAmount& nModifiedFees = ws.m_modified_fees;
CAmount& nConflictingFees = ws.m_conflicting_fees;
size_t& nConflictingSize = ws.m_conflicting_size;
@@ -771,39 +771,42 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// pathological case by making sure setConflicts and setAncestors don't
// intersect.
if (const auto err_string{EntriesAndTxidsDisjoint(setAncestors, setConflicts, hash)}) {
+ // We classify this as a consensus error because a transaction depending on something it
+ // conflicts with would be inconsistent.
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
}
- // If we don't hold the lock allConflicting might be incomplete; the
- // subsequent RemoveStaged() and addUnchecked() calls don't guarantee
- // mempool consistency for us.
- fReplacementTransaction = setConflicts.size();
- if (fReplacementTransaction)
- {
+ if (!setConflicts.empty()) {
CFeeRate newFeeRate(nModifiedFees, nSize);
+ // It's possible that the replacement pays more fees than its direct conflicts but not more
+ // than all conflicts (i.e. the direct conflicts have high-fee descendants). However, if the
+ // replacement doesn't pay more fees than its direct conflicts, then we can be sure it's not
+ // more economically rational to mine. Before we go digging through the mempool for all
+ // transactions that would need to be removed (direct conflicts and all descendants), check
+ // that the replacement transaction pays more than its direct conflicts.
if (const auto err_string{PaysMoreThanConflicts(setIterConflicting, newFeeRate, hash)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
- // Calculate all conflicting entries and enforce Rule #5.
+ // Calculate all conflicting entries and enforce BIP125 Rule #5.
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, setIterConflicting, allConflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"too many potential replacements", *err_string);
}
- // Enforce Rule #2.
+ // Enforce BIP125 Rule #2.
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, setIterConflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"replacement-adds-unconfirmed", *err_string);
}
- // Check if it's economically rational to mine this transaction rather
- // than the ones it replaces. Enforce Rules #3 and #4.
+ // Check if it's economically rational to mine this transaction rather than the ones it
+ // replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
for (CTxMemPool::txiter it : allConflicting) {
nConflictingFees += it->GetModifiedFee();
nConflictingSize += it->GetTxSize();
}
- if (const auto err_string{PaysForRBF(nConflictingFees, nModifiedFees, nSize, hash)}) {
+ if (const auto err_string{PaysForRBF(nConflictingFees, nModifiedFees, nSize, ::incrementalRelayFee, hash)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
}
@@ -879,7 +882,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
const CAmount& nModifiedFees = ws.m_modified_fees;
const CAmount& nConflictingFees = ws.m_conflicting_fees;
const size_t& nConflictingSize = ws.m_conflicting_size;
- const bool fReplacementTransaction = ws.m_replacement_transaction;
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
// Remove conflicting transactions from the mempool
@@ -895,18 +897,17 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
// This transaction should only count for fee estimation if:
- // - it isn't a BIP 125 replacement transaction (may not be widely supported)
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
// - the node is not behind
// - the transaction is not dependent on any other transactions in the mempool
- bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
+ bool validForFeeEstimation = !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
// Store transaction in memory
m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation);
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
- LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
}
@@ -1090,10 +1091,15 @@ void CoinsViews::InitCache()
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(CTxMemPool* mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
+CChainState::CChainState(
+ CTxMemPool* mempool,
+ BlockManager& blockman,
+ ChainstateManager& chainman,
+ std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
m_params(::Params()),
m_blockman(blockman),
+ m_chainman(chainman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(
@@ -1263,7 +1269,7 @@ void InitScriptExecutionCache() {
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
- size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
size_t nElems = g_scriptExecutionCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
@@ -1567,7 +1573,6 @@ static int64_t nTimeForks = 0;
static int64_t nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
-static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
static int64_t nBlocksTotal = 0;
@@ -1872,9 +1877,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
- int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
- LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal);
-
TRACE7(validation, block_connected,
block.GetHash().ToString().c_str(),
pindex->nHeight,
@@ -1892,7 +1894,7 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
{
return this->GetCoinsCacheSizeState(
m_coinstip_cache_size_bytes,
- gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
@@ -2089,8 +2091,42 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
res += warn;
}
+static void UpdateTipLog(
+ const CCoinsViewCache& coins_tip,
+ const CBlockIndex* tip,
+ const CChainParams& params,
+ const std::string& func_name,
+ const std::string& prefix,
+ const std::string& warning_messages) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
+
+ AssertLockHeld(::cs_main);
+ LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",
+ prefix, func_name,
+ tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
+ log(tip->nChainWork.getdouble()) / log(2.0), (unsigned long)tip->nChainTx,
+ FormatISO8601DateTime(tip->GetBlockTime()),
+ GuessVerificationProgress(params.TxData(), tip),
+ coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
+ coins_tip.GetCacheSize(),
+ !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages) : "");
+}
+
void CChainState::UpdateTip(const CBlockIndex* pindexNew)
{
+ const auto& coins_tip = this->CoinsTip();
+
+ // The remainder of the function isn't relevant if we are not acting on
+ // the active chainstate, so return if need be.
+ if (this != &m_chainman.ActiveChainstate()) {
+ // Only log every so often so that we don't bury log messages at the tip.
+ constexpr int BACKGROUND_LOG_INTERVAL = 2000;
+ if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {
+ UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "[background validation] ", "");
+ }
+ return;
+ }
+
// New best block
if (m_mempool) {
m_mempool->AddTransactionsUpdated(1);
@@ -2118,12 +2154,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
}
}
}
- 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,
- FormatISO8601DateTime(pindexNew->GetBlockTime()),
- GuessVerificationProgress(m_params.TxData(), pindexNew), this->CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), this->CoinsTip().GetCacheSize(),
- !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
+ UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
}
/** Disconnect m_chain's tip.
@@ -2517,7 +2548,7 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
- int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
+ int nStopAtHeight = gArgs.GetIntArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
@@ -3620,7 +3651,9 @@ bool BlockManager::LoadBlockIndex(
pindex->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindex);
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) {
+ if (pindex->IsAssumedValid() ||
+ (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
+ (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
block_index_candidates.insert(pindex);
}
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
@@ -3703,7 +3736,7 @@ bool BlockManager::LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkCompar
void CChainState::LoadMempool(const ArgsManager& args)
{
if (!m_mempool) return;
- if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (args.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
::LoadMempool(*m_mempool, *this);
}
m_mempool->SetIsLoaded(!ShutdownRequested());
@@ -4199,12 +4232,33 @@ void CChainState::CheckBlockIndex()
while (pindex != nullptr) {
nNodes++;
if (pindexFirstInvalid == nullptr && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex;
- if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex;
+ // Assumed-valid index entries will not have data since we haven't downloaded the
+ // full block yet.
+ if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA) && !pindex->IsAssumedValid()) {
+ pindexFirstMissing = pindex;
+ }
if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) pindexFirstNeverProcessed = pindex;
if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex;
- if (pindex->pprev != nullptr && pindexFirstNotTransactionsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex;
- if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex;
- if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex;
+
+ if (pindex->pprev != nullptr && !pindex->IsAssumedValid()) {
+ // Skip validity flag checks for BLOCK_ASSUMED_VALID index entries, since these
+ // *_VALID_MASK flags will not be present for index entries we are temporarily assuming
+ // valid.
+ if (pindexFirstNotTransactionsValid == nullptr &&
+ (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) {
+ pindexFirstNotTransactionsValid = pindex;
+ }
+
+ if (pindexFirstNotChainValid == nullptr &&
+ (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) {
+ pindexFirstNotChainValid = pindex;
+ }
+
+ if (pindexFirstNotScriptsValid == nullptr &&
+ (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) {
+ pindexFirstNotScriptsValid = pindex;
+ }
+ }
// Begin: actual consistency checks.
if (pindex->pprev == nullptr) {
@@ -4215,7 +4269,9 @@ void CChainState::CheckBlockIndex()
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
- if (!fHavePruned) {
+ // Unless these indexes are assumed valid and pending block download on a
+ // background chainstate.
+ if (!fHavePruned && !pindex->IsAssumedValid()) {
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
assert(pindexFirstMissing == pindexFirstNeverProcessed);
@@ -4224,7 +4280,16 @@ void CChainState::CheckBlockIndex()
if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0);
}
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
- assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
+ if (pindex->IsAssumedValid()) {
+ // Assumed-valid blocks should have some nTx value.
+ assert(pindex->nTx > 0);
+ // Assumed-valid blocks should connect to the main chain.
+ assert((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE);
+ } else {
+ // Otherwise there should only be an nTx value if we have
+ // actually seen a block's transactions.
+ assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
+ }
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
@@ -4241,11 +4306,17 @@ void CChainState::CheckBlockIndex()
}
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
if (pindexFirstInvalid == nullptr) {
+ const bool is_active = this == &m_chainman.ActiveChainstate();
+
// If this block sorts at least as good as the current tip and
// is valid and we have all data for its parents, it must be in
// setBlockIndexCandidates. m_chain.Tip() must also be there
// even if some data has been pruned.
- if (pindexFirstMissing == nullptr || pindex == m_chain.Tip()) {
+ //
+ // Don't perform this check for the background chainstate since
+ // its setBlockIndexCandidates shouldn't have some entries (i.e. those past the
+ // snapshot block) which do exist in the block index for the active chainstate.
+ if (is_active && (pindexFirstMissing == nullptr || pindex == m_chain.Tip())) {
assert(setBlockIndexCandidates.count(pindex));
}
// If some parent is missing, then it could be that this block was in
@@ -4383,7 +4454,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function)
{
const CChainParams& chainparams = Params();
- int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
+ int64_t nExpiryTimeout = gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat", "rb")};
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
@@ -4580,7 +4651,7 @@ CChainState& ChainstateManager::InitializeChainstate(
if (to_modify) {
throw std::logic_error("should not be overwriting a chainstate");
}
- to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
+ to_modify.reset(new CChainState(mempool, m_blockman, *this, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
@@ -4649,8 +4720,9 @@ bool ChainstateManager::ActivateSnapshot(
static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC));
}
- auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
- /* mempool */ nullptr, m_blockman, base_blockhash));
+ auto snapshot_chainstate = WITH_LOCK(::cs_main,
+ return std::make_unique<CChainState>(
+ /* mempool */ nullptr, m_blockman, *this, base_blockhash));
{
LOCK(::cs_main);
@@ -4853,11 +4925,25 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// Fake nChainTx so that GuessVerificationProgress reports accurately
index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
+ // Mark unvalidated block index entries beneath the snapshot base block as assumed-valid.
+ if (!index->IsValid(BLOCK_VALID_SCRIPTS)) {
+ // This flag will be removed once the block is fully validated by a
+ // background chainstate.
+ index->nStatus |= BLOCK_ASSUMED_VALID;
+ }
+
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
// won't ask to rewind the entire assumed-valid chain on startup.
if (index->pprev && DeploymentActiveAt(*index, ::Params().GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
+
+ setDirtyBlockIndex.insert(index);
+ // Changes to the block index will be flushed to disk after this call
+ // returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
+ // called, since we've added a snapshot chainstate and therefore will
+ // have to downsize the IBD chainstate, which will result in a call to
+ // `FlushStateToDisk(ALWAYS)`.
}
assert(index);
@@ -4882,22 +4968,6 @@ bool ChainstateManager::IsSnapshotActive() const
return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
}
-CChainState& ChainstateManager::ValidatedChainstate() const
-{
- LOCK(::cs_main);
- if (m_snapshot_chainstate && IsSnapshotValidated()) {
- return *m_snapshot_chainstate.get();
- }
- assert(m_ibd_chainstate);
- return *m_ibd_chainstate.get();
-}
-
-bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
-{
- LOCK(::cs_main);
- return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
-}
-
void ChainstateManager::Unload()
{
for (CChainState* chainstate : this->GetAll()) {
diff --git a/src/validation.h b/src/validation.h
index 078b988052..b10050f931 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -10,20 +10,14 @@
#include <config/bitcoin-config.h>
#endif
-#include <amount.h>
#include <arith_uint256.h>
#include <attributes.h>
#include <chain.h>
-#include <coins.h>
-#include <consensus/validation.h>
-#include <crypto/common.h> // for ReadLE64
+#include <consensus/amount.h>
#include <fs.h>
-#include <node/utxo_snapshot.h>
#include <policy/feerate.h>
#include <policy/packages.h>
-#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
-#include <serialize.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h> // For CTxMemPool::cs
@@ -44,18 +38,13 @@
#include <vector>
class CChainState;
-class BlockValidationState;
class CBlockTreeDB;
-class CBlockUndo;
class CChainParams;
struct CCheckpointData;
-class CInv;
-class CConnman;
-class CScriptCheck;
class CTxMemPool;
class ChainstateManager;
+class SnapshotMetadata;
struct ChainTxData;
-
struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
@@ -111,6 +100,7 @@ extern RecursiveMutex cs_main;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
+/** Used to notify getblocktemplate RPC of new tips. */
extern uint256 g_best_block;
/** Whether there are dedicated script-checking threads running.
* False indicates all script checking is done on the main threadMessageHandler thread.
@@ -595,9 +585,15 @@ public:
//! CChainState instances.
BlockManager& m_blockman;
+ //! The chainstate manager that owns this chainstate. The reference is
+ //! necessary so that this instance can check whether it is the active
+ //! chainstate within deeply nested method calls.
+ ChainstateManager& m_chainman;
+
explicit CChainState(
CTxMemPool* mempool,
BlockManager& blockman,
+ ChainstateManager& chainman,
std::optional<uint256> from_snapshot_blockhash = std::nullopt);
/**
@@ -634,9 +630,10 @@ public:
const std::optional<uint256> m_from_snapshot_blockhash;
/**
- * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
- * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
- * missing the data for the block.
+ * The set of all CBlockIndex entries with either BLOCK_VALID_TRANSACTIONS (for
+ * itself and all ancestors) *or* BLOCK_ASSUMED_VALID (if using background
+ * chainstates) and as good as our current tip or better. Entries may be failed,
+ * though, and pruning nodes may be missing the data for the block.
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
@@ -848,12 +845,6 @@ private:
* *Background IBD chainstate*: an IBD chainstate for which the
* IBD process is happening in the background while use of the
* active (snapshot) chainstate allows the rest of the system to function.
- *
- * *Validated chainstate*: the most-work chainstate which has been validated
- * locally via initial block download. This will be the snapshot chainstate
- * if a snapshot was loaded and all blocks up to the snapshot starting point
- * have been downloaded and validated (via background validation), otherwise
- * it will be the IBD chainstate.
*/
class ChainstateManager
{
@@ -972,19 +963,6 @@ public:
//! Is there a snapshot in use and has it been fully validated?
bool IsSnapshotValidated() const { return m_snapshot_validated; }
- //! @returns true if this chainstate is being used to validate an active
- //! snapshot in the background.
- bool IsBackgroundIBD(CChainState* chainstate) const;
-
- //! Return the most-work chainstate that has been fully validated.
- //!
- //! During background validation of a snapshot, this is the IBD chain. After
- //! background validation has completed, this is the snapshot chain.
- CChainState& ValidatedChainstate() const;
-
- CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
- CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
-
/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 1dc23374e3..2eb4d3106c 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -375,7 +375,7 @@ void BerkeleyBatch::Flush()
nMinutes = 1;
if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
- env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetIntArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
}
}
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 85cbec76b7..edd81e590f 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -9,9 +9,14 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <primitives/transaction.h>
+#include <script/keyorigin.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <optional>
+#include <algorithm>
+#include <map>
+#include <set>
const int DEFAULT_MIN_DEPTH = 0;
const int DEFAULT_MAX_DEPTH = 9999999;
@@ -53,6 +58,8 @@ public:
int m_min_depth = DEFAULT_MIN_DEPTH;
//! Maximum chain depth value for coin availability
int m_max_depth = DEFAULT_MAX_DEPTH;
+ //! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
+ FlatSigningProvider m_external_provider;
CCoinControl();
@@ -66,11 +73,32 @@ public:
return (setSelected.count(output) > 0);
}
+ bool IsExternalSelected(const COutPoint& output) const
+ {
+ return (m_external_txouts.count(output) > 0);
+ }
+
+ bool GetExternalOutput(const COutPoint& outpoint, CTxOut& txout) const
+ {
+ const auto ext_it = m_external_txouts.find(outpoint);
+ if (ext_it == m_external_txouts.end()) {
+ return false;
+ }
+ txout = ext_it->second;
+ return true;
+ }
+
void Select(const COutPoint& output)
{
setSelected.insert(output);
}
+ void SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
+ {
+ setSelected.insert(outpoint);
+ m_external_txouts.emplace(outpoint, txout);
+ }
+
void UnSelect(const COutPoint& output)
{
setSelected.erase(output);
@@ -88,6 +116,7 @@ public:
private:
std::set<COutPoint> setSelected;
+ std::map<COutPoint, CTxOut> m_external_txouts;
};
#endif // BITCOIN_WALLET_COINCONTROL_H
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 1699424657..e1ca3fb379 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -4,10 +4,13 @@
#include <wallet/coinselection.h>
+#include <consensus/amount.h>
#include <policy/feerate.h>
+#include <util/check.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <numeric>
#include <optional>
// Descending order comparator
@@ -168,6 +171,30 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selectio
return true;
}
+std::optional<std::pair<std::set<CInputCoin>, CAmount>> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value)
+{
+ std::set<CInputCoin> out_set;
+ CAmount value_ret = 0;
+
+ std::vector<size_t> indexes;
+ indexes.resize(utxo_pool.size());
+ std::iota(indexes.begin(), indexes.end(), 0);
+ Shuffle(indexes.begin(), indexes.end(), FastRandomContext());
+
+ CAmount selected_eff_value = 0;
+ for (const size_t i : indexes) {
+ const OutputGroup& group = utxo_pool.at(i);
+ Assume(group.GetSelectionAmount() > 0);
+ selected_eff_value += group.GetSelectionAmount();
+ value_ret += group.m_value;
+ util::insert(out_set, group.m_outputs);
+ if (selected_eff_value >= target_value) {
+ return std::make_pair(out_set, value_ret);
+ }
+ }
+ return std::nullopt;
+}
+
static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
@@ -279,13 +306,13 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
}
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
- LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */
+ std::string log_message{"Coin selection best subset: "};
for (unsigned int i = 0; i < applicable_groups.size(); i++) {
if (vfBest[i]) {
- LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(applicable_groups[i].m_value)); /* Continued */
+ log_message += strprintf("%s ", FormatMoney(applicable_groups[i].m_value));
}
}
- LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
+ LogPrint(BCLog::SELECTCOINS, "%stotal %s\n", log_message, FormatMoney(nBest));
}
}
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 35617d455b..e7d467660f 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -5,11 +5,13 @@
#ifndef BITCOIN_WALLET_COINSELECTION_H
#define BITCOIN_WALLET_COINSELECTION_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <random.h>
+#include <optional>
+
//! target minimum change amount
static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
@@ -35,6 +37,18 @@ public:
m_input_bytes = input_bytes;
}
+ CInputCoin(const COutPoint& outpoint_in, const CTxOut& txout_in)
+ {
+ outpoint = outpoint_in;
+ txout = txout_in;
+ effective_value = txout.nValue;
+ }
+
+ CInputCoin(const COutPoint& outpoint_in, const CTxOut& txout_in, int input_bytes) : CInputCoin(outpoint_in, txout_in)
+ {
+ m_input_bytes = input_bytes;
+ }
+
COutPoint outpoint;
CTxOut txout;
CAmount effective_value;
@@ -174,7 +188,9 @@ struct OutputGroup
* change_cost = effective_feerate * change_output_size + long_term_feerate * change_spend_size
*
* @param[in] inputs The selected inputs
- * @param[in] change_cost The cost of creating change and spending it in the future. Only used if there is change. Must be 0 if there is no change.
+ * @param[in] change_cost The cost of creating change and spending it in the future.
+ * Only used if there is change, in which case it must be positive.
+ * Must be 0 if there is no change.
* @param[in] target The amount targeted by the coin selection algorithm.
* @param[in] use_effective_value Whether to use the input's effective value (when true) or the real value (when false).
* @return The waste
@@ -183,6 +199,15 @@ struct OutputGroup
bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret);
+/** Select coins by Single Random Draw. OutputGroups are selected randomly from the eligible
+ * outputs until the target is satisfied
+ *
+ * @param[in] utxo_pool The positive effective value OutputGroups eligible for selection
+ * @param[in] target_value The target value to select for
+ * @returns If successful, a pair of set of outputs and total selected value, otherwise, std::nullopt
+ */
+std::optional<std::pair<std::set<CInputCoin>, CAmount>> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value);
+
// Original coin selection algorithm as a fallback
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
diff --git a/src/wallet/fees.h b/src/wallet/fees.h
index 434f211dc2..d6d625d9c1 100644
--- a/src/wallet/fees.h
+++ b/src/wallet/fees.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_WALLET_FEES_H
#define BITCOIN_WALLET_FEES_H
-#include <amount.h>
+#include <consensus/amount.h>
class CCoinControl;
class CFeeRate;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 7abdbb0e55..59a59f9794 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -62,7 +62,6 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-paytxfee=<amt>", strprintf("Fee rate (in %s/kvB) to add to transactions you send (default: %s)",
CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#ifdef ENABLE_EXTERNAL_SIGNER
argsman.AddArg("-signer=<cmd>", "External signing tool, see doc/external-signer.md", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 9a8c1e3c02..7d0cdb6934 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -4,7 +4,7 @@
#include <interfaces/wallet.h>
-#include <amount.h>
+#include <consensus/amount.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <policy/fees.h>
@@ -214,15 +214,17 @@ public:
LOCK(m_wallet->cs_wallet);
return m_wallet->DisplayAddress(dest);
}
- void lockCoin(const COutPoint& output) override
+ bool lockCoin(const COutPoint& output, const bool write_to_db) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->LockCoin(output);
+ std::unique_ptr<WalletBatch> batch = write_to_db ? std::make_unique<WalletBatch>(m_wallet->GetDatabase()) : nullptr;
+ return m_wallet->LockCoin(output, batch.get());
}
- void unlockCoin(const COutPoint& output) override
+ bool unlockCoin(const COutPoint& output) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->UnlockCoin(output);
+ std::unique_ptr<WalletBatch> batch = std::make_unique<WalletBatch>(m_wallet->GetDatabase());
+ return m_wallet->UnlockCoin(output, batch.get());
}
bool isLockedCoin(const COutPoint& output) override
{
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
index 98706dcdf8..2fb274b55f 100644
--- a/src/wallet/receive.cpp
+++ b/src/wallet/receive.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <wallet/receive.h>
#include <wallet/transaction.h>
diff --git a/src/wallet/receive.h b/src/wallet/receive.h
index b4b311636b..f659955fc6 100644
--- a/src/wallet/receive.h
+++ b/src/wallet/receive.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_WALLET_RECEIVE_H
#define BITCOIN_WALLET_RECEIVE_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <wallet/ismine.h>
#include <wallet/transaction.h>
#include <wallet/wallet.h>
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 72c60c8fe2..4d7fb2d38c 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1439,7 +1439,7 @@ RPCHelpMan importmulti()
"and coins using this key may not appear in the wallet. This error could be "
"caused by pruning or data corruption (see bitcoind log for details) and could "
"be dealt with by downloading and rescanning the relevant blocks (see -reindex "
- "and -rescan options).",
+ "option and rescanblockchain RPC).",
GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
response.push_back(std::move(result));
}
@@ -1488,7 +1488,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
} else {
warnings.push_back("Range not given, using default keypool range");
range_start = 0;
- range_end = gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE);
+ range_end = gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE);
}
next_index = range_start;
@@ -1744,7 +1744,7 @@ RPCHelpMan importdescriptors()
"and coins using this desc may not appear in the wallet. This error could be "
"caused by pruning or data corruption (see bitcoind log for details) and could "
"be dealt with by downloading and rescanning the relevant blocks (see -reindex "
- "and -rescan options).",
+ "option and rescanblockchain RPC).",
GetImportTimestamp(request, now), scanned_time - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
response.push_back(std::move(result));
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index e922f4ede9..8b481bc29c 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3,7 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
+#include <consensus/amount.h>
#include <core_io.h>
#include <interfaces/chain.h>
#include <key_io.h>
@@ -43,6 +43,7 @@
#include <univalue.h>
+#include <map>
using interfaces::FoundBlock;
@@ -472,7 +473,7 @@ static RPCHelpMan sendtoaddress()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
- {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
},
},
},
@@ -899,7 +900,7 @@ static RPCHelpMan sendmany()
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
"the number of addresses."},
- {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
},
},
},
@@ -1213,7 +1214,7 @@ static RPCHelpMan listreceivedbyaddress()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction"},
{RPCResult::Type::STR, "address", "The receiving address"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
@@ -1261,7 +1262,7 @@ static RPCHelpMan listreceivedbylabel()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
{RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
@@ -1388,20 +1389,24 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{
return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
"transaction conflicted that many blocks ago."},
- {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."},
- {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."},
- {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."},
- {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."},
- {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."},
- {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::BOOL, "generated", /* optional */ true, "Only present if transaction only input is a coinbase one."},
+ {RPCResult::Type::BOOL, "trusted", /* optional */ true, "Only present if we consider transaction to be trusted and so safe to spend from."},
+ {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash containing the transaction."},
+ {RPCResult::Type::NUM, "blockheight", /* optional */ true, "The block height containing the transaction."},
+ {RPCResult::Type::NUM, "blockindex", /* optional */ true, "The index of the transaction in the block that includes it."},
+ {RPCResult::Type::NUM_TIME, "blocktime", /* optional */ true, "The block time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
{RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
}},
+ {RPCResult::Type::STR_HEX, "replaced_by_txid", /* optional */ true, "The txid if this tx was replaced."},
+ {RPCResult::Type::STR_HEX, "replaces_txid", /* optional */ true, "The txid if the tx replaces one."},
+ {RPCResult::Type::STR, "comment", /* optional */ true, ""},
+ {RPCResult::Type::STR, "to", /* optional */ true, "If a comment to is associated with the transaction."},
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
- {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."},
+ {RPCResult::Type::STR, "comment", /* optional */ true, "If a comment is associated with the transaction, only present if not empty."},
{RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
"may be unknown for unconfirmed transactions not in the mempool"}};
}
@@ -1423,7 +1428,7 @@ static RPCHelpMan listtransactions()
{
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction."},
{RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
{RPCResult::Type::STR, "category", "The transaction category.\n"
"\"send\" Transactions sent.\n"
@@ -1433,14 +1438,14 @@ static RPCHelpMan listtransactions()
"\"orphan\" Orphaned coinbase transactions received."},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
"for all other categories"},
- {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "A comment for the address/transaction, if any"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
"'send' category of transactions."},
},
TransactionDescriptionString()),
{
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ {RPCResult::Type::BOOL, "abandoned", /* optional */ true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
})},
}
@@ -1537,7 +1542,7 @@ static RPCHelpMan listsinceblock()
{
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction."},
{RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
{RPCResult::Type::STR, "category", "The transaction category.\n"
"\"send\" Transactions sent.\n"
@@ -1548,18 +1553,17 @@ static RPCHelpMan listsinceblock()
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
"for all other categories"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
"'send' category of transactions."},
},
TransactionDescriptionString()),
{
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ {RPCResult::Type::BOOL, "abandoned", /* optional */ true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
- {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
- {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."},
+ {RPCResult::Type::STR, "label", /* optional */ true, "A comment for the address/transaction, if any"},
})},
}},
- {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
+ {RPCResult::Type::ARR, "removed", /* optional */ true, "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
"Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
, {{RPCResult::Type::ELISION, "", ""},}},
{RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain, or the genesis hash if the referenced block does not exist yet. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
@@ -1672,7 +1676,7 @@ static RPCHelpMan gettransaction()
RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
{
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
"'send' category of transactions."},
},
TransactionDescriptionString()),
@@ -1681,8 +1685,8 @@ static RPCHelpMan gettransaction()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
- {RPCResult::Type::STR, "address", "The bitcoin address involved in the transaction."},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address involved in the transaction."},
{RPCResult::Type::STR, "category", "The transaction category.\n"
"\"send\" Transactions sent.\n"
"\"receive\" Non-coinbase transactions received.\n"
@@ -1690,16 +1694,16 @@ static RPCHelpMan gettransaction()
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
"\"orphan\" Orphaned coinbase transactions received."},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
- {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "A comment for the address/transaction, if any"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
"'send' category of transactions."},
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ {RPCResult::Type::BOOL, "abandoned", /* optional */ true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
}},
}},
{RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
- {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)",
+ {RPCResult::Type::OBJ, "decoded", /* optional */ true, "The decoded transaction (only present when `verbose` is passed)",
{
{RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
}},
@@ -1759,7 +1763,7 @@ static RPCHelpMan gettransaction()
if (verbose) {
UniValue decoded(UniValue::VOBJ);
- TxToUniv(*wtx.tx, uint256(), pwallet->chain().rpcEnableDeprecated("addresses"), decoded, false);
+ TxToUniv(*wtx.tx, uint256(), decoded, false);
entry.pushKV("decoded", decoded);
}
@@ -2137,8 +2141,9 @@ static RPCHelpMan lockunspent()
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
"Manually selected coins are automatically unlocked.\n"
- "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
- "is always cleared (by virtue of process exit) when a node stops or fails.\n"
+ "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
+ "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
+ "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
"Also see the listunspent call\n",
{
{"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
@@ -2152,6 +2157,7 @@ static RPCHelpMan lockunspent()
},
},
},
+ {"persistent", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to write/erase this lock in the wallet database, or keep the change in memory only. Ignored for unlocking."},
},
RPCResult{
RPCResult::Type::BOOL, "", "Whether the command was successful or not"
@@ -2165,6 +2171,8 @@ static RPCHelpMan lockunspent()
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
+ "\nLock the transaction persistently in the wallet database\n"
+ + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
},
@@ -2183,9 +2191,13 @@ static RPCHelpMan lockunspent()
bool fUnlock = request.params[0].get_bool();
+ const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
+
if (request.params[1].isNull()) {
- if (fUnlock)
- pwallet->UnlockAllCoins();
+ if (fUnlock) {
+ if (!pwallet->UnlockAllCoins())
+ throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
+ }
return true;
}
@@ -2236,17 +2248,24 @@ static RPCHelpMan lockunspent()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
}
- if (!fUnlock && is_locked) {
+ if (!fUnlock && is_locked && !persistent) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
}
outputs.push_back(outpt);
}
+ std::unique_ptr<WalletBatch> batch = nullptr;
+ // Unlock is always persistent
+ if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
+
// Atomically set (un)locked status for the outputs.
for (const COutPoint& outpt : outputs) {
- if (fUnlock) pwallet->UnlockCoin(outpt);
- else pwallet->LockCoin(outpt);
+ if (fUnlock) {
+ if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
+ } else {
+ if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
+ }
}
return true;
@@ -2362,9 +2381,9 @@ static RPCHelpMan getbalances()
{RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
- {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
+ {RPCResult::Type::STR_AMOUNT, "used", /* optional */ true, "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
}},
- {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
+ {RPCResult::Type::OBJ, "watchonly", /* optional */ true, "watchonly balances (not present if wallet does not watch anything)",
{
{RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
@@ -2431,9 +2450,9 @@ static RPCHelpMan getwalletinfo()
{RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
{RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
{RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
- {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
+ {RPCResult::Type::NUM_TIME, "keypoololdest", /* optional */ true, "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
{RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
- {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
+ {RPCResult::Type::NUM, "keypoolsize_hd_internal", /* optional */ true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
{RPCResult::Type::NUM_TIME, "unlocked_until", /* optional */ true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
{RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"},
{RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"},
@@ -2614,7 +2633,7 @@ static RPCHelpMan loadwallet()
return RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting bitcoind will be"
- "\napplied to the new wallet (eg -rescan, etc).\n",
+ "\napplied to the new wallet.\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
{"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
@@ -2955,17 +2974,20 @@ static RPCHelpMan listunspent()
{
{RPCResult::Type::STR_HEX, "txid", "the transaction id"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR, "address", "the bitcoin address"},
- {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "the bitcoin address"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "The associated label, or \"\" for the default label"},
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
- {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
- {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
+ {RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
+ {RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "ancestorfees", /* optional */ true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
+ {RPCResult::Type::STR_HEX, "redeemScript", /* optional */ true, "The redeemScript if scriptPubKey is P2SH"},
+ {RPCResult::Type::STR, "witnessScript", /* optional */ true, "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
{RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
- {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
- {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
+ {RPCResult::Type::BOOL, "reused", /* optional */ true, "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
+ {RPCResult::Type::STR, "desc", /* optional */ true, "(only when solvable) A descriptor for spending this output"},
{RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
"from outside keys and unconfirmed replacement transactions are considered unsafe\n"
"and are not eligible for spending by fundrawtransaction and sendtoaddress."},
@@ -3126,6 +3148,16 @@ static RPCHelpMan listunspent()
entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
entry.pushKV("confirmations", out.nDepth);
+ if (!out.nDepth) {
+ size_t ancestor_count, descendant_count, ancestor_size;
+ CAmount ancestor_fees;
+ pwallet->chain().getTransactionAncestry(out.tx->GetHash(), ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
+ if (ancestor_count) {
+ entry.pushKV("ancestorcount", uint64_t(ancestor_count));
+ entry.pushKV("ancestorsize", uint64_t(ancestor_size));
+ entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
+ }
+ }
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
@@ -3145,6 +3177,34 @@ static RPCHelpMan listunspent()
};
}
+// Only includes key documentation where the key is snake_case in all RPC methods. MixedCase keys can be added later.
+static std::vector<RPCArg> FundTxDoc()
+{
+ return {
+ {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
+ {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
+ {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
+ "Allows this transaction to be replaced by a transaction with higher fees"},
+ {"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature.\n"
+ "Used for fee estimation during coin selection.",
+ {
+ {"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Public keys involved in this transaction.",
+ {
+ {"pubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A public key"},
+ }},
+ {"scripts", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Scripts involved in this transaction.",
+ {
+ {"script", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A script"},
+ }},
+ {"descriptors", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Descriptors that provide solving data for this transaction.",
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A descriptor"},
+ }},
+ }},
+ };
+}
+
void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
{
// Make sure the results are valid at least up to the most recent block
@@ -3182,6 +3242,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
{"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
{"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
{"psbt", UniValueType(UniValue::VBOOL)},
+ {"solving_data", UniValueType(UniValue::VOBJ)},
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
{"replaceable", UniValueType(UniValue::VBOOL)},
@@ -3258,6 +3319,54 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
}
+ if (options.exists("solving_data")) {
+ const UniValue solving_data = options["solving_data"].get_obj();
+ if (solving_data.exists("pubkeys")) {
+ for (const UniValue& pk_univ : solving_data["pubkeys"].get_array().getValues()) {
+ const std::string& pk_str = pk_univ.get_str();
+ if (!IsHex(pk_str)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", pk_str));
+ }
+ const std::vector<unsigned char> data(ParseHex(pk_str));
+ const CPubKey pubkey(data.begin(), data.end());
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not a valid public key", pk_str));
+ }
+ coinControl.m_external_provider.pubkeys.emplace(pubkey.GetID(), pubkey);
+ // Add witness script for pubkeys
+ const CScript wit_script = GetScriptForDestination(WitnessV0KeyHash(pubkey));
+ coinControl.m_external_provider.scripts.emplace(CScriptID(wit_script), wit_script);
+ }
+ }
+
+ if (solving_data.exists("scripts")) {
+ for (const UniValue& script_univ : solving_data["scripts"].get_array().getValues()) {
+ const std::string& script_str = script_univ.get_str();
+ if (!IsHex(script_str)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' is not hex", script_str));
+ }
+ std::vector<unsigned char> script_data(ParseHex(script_str));
+ const CScript script(script_data.begin(), script_data.end());
+ coinControl.m_external_provider.scripts.emplace(CScriptID(script), script);
+ }
+ }
+
+ if (solving_data.exists("descriptors")) {
+ for (const UniValue& desc_univ : solving_data["descriptors"].get_array().getValues()) {
+ const std::string& desc_str = desc_univ.get_str();
+ FlatSigningProvider desc_out;
+ std::string error;
+ std::vector<CScript> scripts_temp;
+ std::unique_ptr<Descriptor> desc = Parse(desc_str, desc_out, error, true);
+ if (!desc) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unable to parse descriptor '%s': %s", desc_str, error));
+ }
+ desc->Expand(0, desc_out, scripts_temp, desc_out);
+ coinControl.m_external_provider = Merge(coinControl.m_external_provider, desc_out);
+ }
+ }
+ }
+
if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
@@ -3275,6 +3384,19 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
setSubtractFeeFromOutputs.insert(pos);
}
+ // Fetch specified UTXOs from the UTXO set to get the scriptPubKeys and values of the outputs being selected
+ // and to match with the given solving_data. Only used for non-wallet outputs.
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : tx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ wallet.chain().findCoins(coins);
+ for (const auto& coin : coins) {
+ if (!coin.second.out.IsNull()) {
+ coinControl.SelectExternal(coin.first, coin.second.out);
+ }
+ }
+
bilingual_str error;
if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
@@ -3290,8 +3412,9 @@ static RPCHelpMan fundrawtransaction()
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
"The inputs added will not be signed, use signrawtransactionwithkey\n"
- " or signrawtransactionwithwallet for that.\n"
- "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
+ "or signrawtransactionwithwallet for that.\n"
+ "All existing inputs must either have their previous output transaction be in the wallet\n"
+ "or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n"
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
"You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
@@ -3299,6 +3422,7 @@ static RPCHelpMan fundrawtransaction()
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
+ Cat<std::vector<RPCArg>>(
{
{"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
@@ -3321,12 +3445,8 @@ static RPCHelpMan fundrawtransaction()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
- "Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
- " \"" + FeeModes("\"\n\"") + "\""},
},
+ FundTxDoc()),
"options"},
{"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
"If iswitness is not present, heuristic tests will be used in decoding.\n"
@@ -3429,6 +3549,10 @@ RPCHelpMan signrawtransactionwithwallet()
{
{RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
{RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::ARR, "witness", "",
+ {
+ {RPCResult::Type::STR_HEX, "witness", ""},
+ }},
{RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
{RPCResult::Type::NUM, "sequence", "Script sequence number"},
{RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
@@ -3764,7 +3888,6 @@ public:
obj.pushKV("embedded", std::move(subobj));
} else if (which_type == TxoutType::MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
- // TODO: abstract out the common functionality between this logic and ExtractDestinations.
obj.pushKV("sigsrequired", solutions_data[0][0]);
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
@@ -4132,6 +4255,7 @@ static RPCHelpMan send()
" \"" + FeeModes("\"\n\"") + "\""},
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
+ Cat<std::vector<RPCArg>>(
{
{"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
@@ -4141,9 +4265,6 @@ static RPCHelpMan send()
{"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
{"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
- {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
- " \"" + FeeModes("\"\n\"") + "\""},
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
@@ -4166,18 +4287,17 @@ static RPCHelpMan send()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
- "Allows this transaction to be replaced by a transaction with higher fees"},
},
+ FundTxDoc()),
"options"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
- {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
- {RPCResult::Type::STR_HEX, "hex", "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
- {RPCResult::Type::STR, "psbt", "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
+ {RPCResult::Type::STR_HEX, "txid", /* optional */ true, "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
+ {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::STR, "psbt", /* optional */ true, "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
}
},
RPCExamples{""
@@ -4385,7 +4505,7 @@ static RPCHelpMan walletprocesspsbt()
HELP_REQUIRING_PASSPHRASE,
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
- {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
+ {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)"},
{"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"
@@ -4432,6 +4552,9 @@ static RPCHelpMan walletprocesspsbt()
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool complete = true;
+
+ if (sign) EnsureWalletIsUnlocked(*pwallet);
+
const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs)};
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
@@ -4452,7 +4575,9 @@ static RPCHelpMan walletcreatefundedpsbt()
{
return RPCHelpMan{"walletcreatefundedpsbt",
"\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
- "Implements the Creator and Updater roles.\n",
+ "Implements the Creator and Updater roles.\n"
+ "All existing inputs must either have their previous output transaction be in the wallet\n"
+ "or be in the UTXO set. Solving data must be provided for non-wallet inputs.\n",
{
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
{
@@ -4484,6 +4609,7 @@ static RPCHelpMan walletcreatefundedpsbt()
},
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
+ Cat<std::vector<RPCArg>>(
{
{"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{false}, "If inputs are specified, automatically include more if they are not enough."},
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
@@ -4504,12 +4630,8 @@ static RPCHelpMan walletcreatefundedpsbt()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
- {"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
- "Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
- {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
- " \"" + FeeModes("\"\n\"") + "\""},
},
+ FundTxDoc()),
"options"},
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
},
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index ea045eb6d8..4151099c1f 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -45,7 +45,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
// Call Salvage with fAggressive=true to
// get as much data as possible.
// Rewrite salvaged data to fresh wallet file
- // Set -rescan so any missing transactions will be
+ // Rescan so any missing transactions will be
// found.
int64_t now = GetTime();
std::string newFilename = strprintf("%s.%d.bak", filename, now);
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index fe41f9b8cc..fdfb36bb0a 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -331,7 +331,7 @@ bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t i
CHDChain& chain = it->second;
// Top up key pool
- int64_t target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
+ int64_t target_size = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
// "size" of the keypools. Not really the size, actually the difference between index and the chain counter
// Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1.
@@ -1259,7 +1259,7 @@ bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize)
if (kpSize > 0)
nTargetSize = kpSize;
else
- nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
+ nTargetSize = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
// count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user selected target (-keypool)
@@ -1764,7 +1764,7 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
if (size > 0) {
target_size = size;
} else {
- target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
+ target_size = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
}
// Calculate the new range_end
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 4a7a268982..5470177440 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -2,9 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <consensus/validation.h>
#include <interfaces/chain.h>
#include <policy/policy.h>
+#include <script/signingprovider.h>
#include <util/check.h>
#include <util/fees.h>
#include <util/moneystr.h>
@@ -31,21 +33,27 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
-int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* provider, bool use_max_sig)
{
CMutableTransaction txn;
txn.vin.push_back(CTxIn(COutPoint()));
- if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
+ if (!provider || !DummySignInput(*provider, txn.vin[0], txout, use_max_sig)) {
return -1;
}
return GetVirtualTransactionInputSize(txn.vin[0]);
}
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
+{
+ const std::unique_ptr<SigningProvider> provider = wallet->GetSolvingProvider(txout.scriptPubKey);
+ return CalculateMaximumSignedInputSize(txout, provider.get(), use_max_sig);
+}
+
// txouts needs to be in the order of tx.vin
-TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
+TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control)
{
CMutableTransaction txNew(tx);
- if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
+ if (!wallet->DummySignTx(txNew, txouts, coin_control)) {
return TxSize{-1, -1};
}
CTransaction ctx(txNew);
@@ -54,19 +62,27 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return TxSize{vsize, weight};
}
-TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
+TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const CCoinControl* coin_control)
{
std::vector<CTxOut> txouts;
+ // Look up the inputs. The inputs are either in the wallet, or in coin_control.
for (const CTxIn& input : tx.vin) {
const auto mi = wallet->mapWallet.find(input.prevout.hash);
// Can not estimate size without knowing the input details
- if (mi == wallet->mapWallet.end()) {
+ if (mi != wallet->mapWallet.end()) {
+ assert(input.prevout.n < mi->second.tx->vout.size());
+ txouts.emplace_back(mi->second.tx->vout.at(input.prevout.n));
+ } else if (coin_control) {
+ CTxOut txout;
+ if (!coin_control->GetExternalOutput(input.prevout, txout)) {
+ return TxSize{-1, -1};
+ }
+ txouts.emplace_back(txout);
+ } else {
return TxSize{-1, -1};
}
- assert(input.prevout.n < mi->second.tx->vout.size());
- txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
}
- return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
+ return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
}
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
@@ -387,6 +403,15 @@ bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const
results.emplace_back(std::make_tuple(waste, std::move(knapsack_coins), knapsack_value));
}
+ // We include the minimum final change for SRD as we do want to avoid making really small change.
+ // KnapsackSolver does not need this because it includes MIN_CHANGE internally.
+ const CAmount srd_target = nTargetValue + coin_selection_params.m_change_fee + MIN_FINAL_CHANGE;
+ auto srd_result = SelectCoinsSRD(positive_groups, srd_target);
+ if (srd_result != std::nullopt) {
+ const auto waste = GetSelectionWaste(srd_result->first, coin_selection_params.m_cost_of_change, srd_target, !coin_selection_params.m_subtract_fee_outputs);
+ results.emplace_back(std::make_tuple(waste, std::move(srd_result->first), srd_result->second));
+ }
+
if (results.size() == 0) {
// No solution found
return false;
@@ -426,32 +451,40 @@ bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCo
std::vector<COutPoint> vPresetInputs;
coin_control.ListSelected(vPresetInputs);
- for (const COutPoint& outpoint : vPresetInputs)
- {
+ for (const COutPoint& outpoint : vPresetInputs) {
+ int input_bytes = -1;
+ CTxOut txout;
std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
- if (it != wallet.mapWallet.end())
- {
+ if (it != wallet.mapWallet.end()) {
const CWalletTx& wtx = it->second;
// Clearly invalid input, fail
if (wtx.tx->vout.size() <= outpoint.n) {
return false;
}
- // Just to calculate the marginal byte size
- CInputCoin coin(wtx.tx, outpoint.n, GetTxSpendSize(wallet, wtx, outpoint.n, false));
- nValueFromPresetInputs += coin.txout.nValue;
- if (coin.m_input_bytes <= 0) {
- return false; // Not solvable, can't estimate size for fee
- }
- coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
- if (coin_selection_params.m_subtract_fee_outputs) {
- value_to_select -= coin.txout.nValue;
- } else {
- value_to_select -= coin.effective_value;
+ input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
+ txout = wtx.tx->vout.at(outpoint.n);
+ }
+ if (input_bytes == -1) {
+ // The input is external. We either did not find the tx in mapWallet, or we did but couldn't compute the input size with wallet data
+ if (!coin_control.GetExternalOutput(outpoint, txout)) {
+ // Not ours, and we don't have solving data.
+ return false;
}
- setPresetCoins.insert(coin);
+ input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true);
+ }
+
+ CInputCoin coin(outpoint, txout, input_bytes);
+ nValueFromPresetInputs += coin.txout.nValue;
+ if (coin.m_input_bytes == -1) {
+ return false; // Not solvable, can't estimate size for fee
+ }
+ coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ value_to_select -= coin.txout.nValue;
} else {
- return false; // TODO: Allow non-wallet inputs
+ value_to_select -= coin.effective_value;
}
+ setPresetCoins.insert(coin);
}
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
@@ -779,10 +812,10 @@ static bool CreateTransactionInternal(
}
// Calculate the transaction fee
- TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
+ TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
int nBytes = tx_sizes.vsize;
- if (nBytes < 0) {
- error = _("Signing transaction failed");
+ if (nBytes == -1) {
+ error = _("Missing solving data for estimating transaction size");
return false;
}
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
@@ -804,7 +837,7 @@ static bool CreateTransactionInternal(
txNew.vout.erase(change_position);
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
nBytes = tx_sizes.vsize;
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
}
diff --git a/src/wallet/spend.h b/src/wallet/spend.h
index e39f134dc3..7467dd9fa3 100644
--- a/src/wallet/spend.h
+++ b/src/wallet/spend.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_WALLET_SPEND_H
#define BITCOIN_WALLET_SPEND_H
+#include <consensus/amount.h>
#include <wallet/coinselection.h>
#include <wallet/transaction.h>
#include <wallet/wallet.h>
@@ -66,6 +67,7 @@ public:
//Get the marginal bytes of spending the specified output
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* pwallet, bool use_max_sig = false);
struct TxSize {
int64_t vsize{-1};
@@ -76,8 +78,8 @@ struct TxSize {
* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
* be AllInputsMine). */
-TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
-TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
/**
* populate vCoins with vector of available COutputs.
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 2e60aca017..815d17967c 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -212,6 +212,10 @@ void SQLiteDatabase::Open()
if (ret != SQLITE_OK) {
throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
}
+ ret = sqlite3_extended_result_codes(m_db, 1);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable extended result codes: %s\n", sqlite3_errstr(ret)));
+ }
}
if (sqlite3_db_readonly(m_db, "main") != 0) {
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 5d51809241..f80c4637b8 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <amount.h>
+#include <consensus/amount.h>
#include <node/context.h>
#include <primitives/transaction.h>
#include <random.h>
@@ -724,12 +724,25 @@ BOOST_AUTO_TEST_CASE(waste_test)
BOOST_CHECK_LT(waste_nochange2, waste_nochange1);
selection.clear();
- // 0 Waste only when fee == long term fee, no change, and no excess
+ // No Waste when fee == long_term_fee, no change, and no excess
add_coin(1 * COIN, 1, selection, fee, fee);
add_coin(2 * COIN, 2, selection, fee, fee);
- const CAmount exact_target = in_amt - 2 * fee;
- BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, 0, exact_target));
+ const CAmount exact_target{in_amt - fee * 2};
+ BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change_cost */ 0, exact_target));
+ selection.clear();
+ // No Waste when (fee - long_term_fee) == (-cost_of_change), and no excess
+ const CAmount new_change_cost{fee_diff * 2};
+ add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
+ add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
+ BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, new_change_cost, target));
+ selection.clear();
+
+ // No Waste when (fee - long_term_fee) == (-excess), no change cost
+ const CAmount new_target{in_amt - fee * 2 - fee_diff * 2};
+ add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
+ add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
+ BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index e779b2450f..becef70729 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/amount.h>
#include <policy/fees.h>
#include <validation.h>
#include <wallet/coincontrol.h>
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 5431a38bee..9938380369 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -232,8 +232,8 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"seconds of key creation, and could contain transactions pertaining to the key. As a result, "
"transactions and coins using this key may not appear in the wallet. This error could be caused "
"by pruning or data corruption (see bitcoind log for details) and could be dealt with by "
- "downloading and rescanning the relevant blocks (see -reindex and -rescan "
- "options).\"}},{\"success\":true}]",
+ "downloading and rescanning the relevant blocks (see -reindex option and rescanblockchain "
+ "RPC).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
}
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 094221adf2..6fc1bd1eed 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_WALLET_TRANSACTION_H
#define BITCOIN_WALLET_TRANSACTION_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <wallet/ismine.h>
@@ -19,25 +19,6 @@
typedef std::map<std::string, std::string> mapValue_t;
-
-static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (!mapValue.count("n"))
- {
- nOrderPos = -1; // TODO: calculate elsewhere
- return;
- }
- nOrderPos = atoi64(mapValue["n"]);
-}
-
-
-static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (nOrderPos == -1)
- return;
- mapValue["n"] = ToString(nOrderPos);
-}
-
/** Legacy class used for deserializing vtxPrev for backwards compatibility.
* vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
* but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
@@ -192,7 +173,9 @@ public:
mapValue_t mapValueCopy = mapValue;
mapValueCopy["fromaccount"] = "";
- WriteOrderPos(nOrderPos, mapValueCopy);
+ if (nOrderPos != -1) {
+ mapValueCopy["n"] = ToString(nOrderPos);
+ }
if (nTimeSmart) {
mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
}
@@ -232,8 +215,10 @@ public:
setConfirmed();
}
- ReadOrderPos(nOrderPos, mapValue);
- nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
+ const auto it_op = mapValue.find("n");
+ nOrderPos = (it_op != mapValue.end()) ? LocaleIndependentAtoi<int64_t>(it_op->second) : -1;
+ const auto it_ts = mapValue.find("timesmart");
+ nTimeSmart = (it_ts != mapValue.end()) ? static_cast<unsigned int>(LocaleIndependentAtoi<int64_t>(it_ts->second)) : 0;
mapValue.erase("fromaccount");
mapValue.erase("spent");
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 70349b2455..abfe9d7dba 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -6,6 +6,7 @@
#include <wallet/wallet.h>
#include <chain.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <external_signer.h>
@@ -589,11 +590,16 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
return false;
}
-void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
+void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch)
{
mapTxSpends.insert(std::make_pair(outpoint, wtxid));
- setLockedCoins.erase(outpoint);
+ if (batch) {
+ UnlockCoin(outpoint, batch);
+ } else {
+ WalletBatch temp_batch(GetDatabase());
+ UnlockCoin(outpoint, &temp_batch);
+ }
std::pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
@@ -601,7 +607,7 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
}
-void CWallet::AddToSpends(const uint256& wtxid)
+void CWallet::AddToSpends(const uint256& wtxid, WalletBatch* batch)
{
auto it = mapWallet.find(wtxid);
assert(it != mapWallet.end());
@@ -610,7 +616,7 @@ void CWallet::AddToSpends(const uint256& wtxid)
return;
for (const CTxIn& txin : thisTx.tx->vin)
- AddToSpends(txin.prevout, wtxid);
+ AddToSpends(txin.prevout, wtxid, batch);
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
@@ -879,7 +885,7 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
return false;
}
-CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose)
+CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose, bool rescanning_old_block)
{
LOCK(cs_wallet);
@@ -909,8 +915,8 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
wtx.nTimeReceived = chain().getAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&batch);
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
- wtx.nTimeSmart = ComputeTimeSmart(wtx);
- AddToSpends(hash);
+ wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
+ AddToSpends(hash, &batch);
}
if (!fInsertedNew)
@@ -1026,7 +1032,7 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
return true;
}
-bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate, bool rescanning_old_block)
{
const CTransaction& tx = *ptx;
{
@@ -1064,7 +1070,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
// Block disconnection override an abandoned tx as unconfirmed
// which means user may have to call abandontransaction again
- return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false);
+ return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false, rescanning_old_block);
}
}
return false;
@@ -1193,9 +1199,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx)
+void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx, bool rescanning_old_block)
{
- if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx))
+ if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx, rescanning_old_block))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1443,19 +1449,13 @@ bool CWallet::AddWalletFlags(uint64_t flags)
// Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true
-bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const
+bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig)
{
// Fill in dummy signatures for fee calculation.
const CScript& scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;
- std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
- if (!provider) {
- // We don't know about this scriptpbuKey;
- return false;
- }
-
- if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
+ if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
return false;
}
UpdateInput(tx_in, sigdata);
@@ -1463,14 +1463,21 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig
}
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
-bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig) const
+bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
{
// Fill in dummy signatures for fee calculation.
int nIn = 0;
for (const auto& txout : txouts)
{
- if (!DummySignInput(txNew.vin[nIn], txout, use_max_sig)) {
- return false;
+ CTxIn& txin = txNew.vin[nIn];
+ // Use max sig if watch only inputs were used or if this particular input is an external input
+ // to ensure a sufficient fee is attained for the requested feerate.
+ const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout));
+ const std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
+ if (!provider || !DummySignInput(*provider, txin, txout, use_max_sig)) {
+ if (!coin_control || !DummySignInput(coin_control->m_external_provider, txin, txout, use_max_sig)) {
+ return false;
+ }
}
nIn++;
@@ -1595,7 +1602,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());
fAbortRescan = false;
- ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
+ ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)
uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
uint256 end_hash = tip_hash;
if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));
@@ -1638,7 +1645,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate, /* rescanning_old_block */ true);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
@@ -2002,10 +2009,7 @@ DBErrors CWallet::LoadWallet()
assert(m_internal_spk_managers.empty());
}
- if (nLoadWalletRet != DBErrors::LOAD_OK)
- return nLoadWalletRet;
-
- return DBErrors::LOAD_OK;
+ return nLoadWalletRet;
}
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
@@ -2260,22 +2264,36 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
}
-void CWallet::LockCoin(const COutPoint& output)
+bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch)
{
AssertLockHeld(cs_wallet);
setLockedCoins.insert(output);
+ if (batch) {
+ return batch->WriteLockedUTXO(output);
+ }
+ return true;
}
-void CWallet::UnlockCoin(const COutPoint& output)
+bool CWallet::UnlockCoin(const COutPoint& output, WalletBatch* batch)
{
AssertLockHeld(cs_wallet);
- setLockedCoins.erase(output);
+ bool was_locked = setLockedCoins.erase(output);
+ if (batch && was_locked) {
+ return batch->EraseLockedUTXO(output);
+ }
+ return true;
}
-void CWallet::UnlockAllCoins()
+bool CWallet::UnlockAllCoins()
{
AssertLockHeld(cs_wallet);
+ bool success = true;
+ WalletBatch batch(GetDatabase());
+ for (auto it = setLockedCoins.begin(); it != setLockedCoins.end(); ++it) {
+ success &= batch.EraseLockedUTXO(*it);
+ }
setLockedCoins.clear();
+ return success;
}
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
@@ -2365,6 +2383,8 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
* - If sending a transaction, assign its timestamp to the current time.
* - If receiving a transaction outside a block, assign its timestamp to the
* current time.
+ * - If receiving a transaction during a rescanning process, assign all its
+ * (not already known) transactions' timestamps to the block time.
* - If receiving a block with a future timestamp, assign all its (not already
* known) transactions' timestamps to the current time.
* - If receiving a block with a past timestamp, before the most recent known
@@ -2379,38 +2399,43 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
* https://bitcointalk.org/?topic=54527, or
* https://github.com/bitcoin/bitcoin/pull/1393.
*/
-unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
+unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const
{
unsigned int nTimeSmart = wtx.nTimeReceived;
if (!wtx.isUnconfirmed() && !wtx.isAbandoned()) {
int64_t blocktime;
- if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime))) {
- int64_t latestNow = wtx.nTimeReceived;
- int64_t latestEntry = 0;
-
- // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
- int64_t latestTolerated = latestNow + 300;
- const TxItems& txOrdered = wtxOrdered;
- for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
- CWalletTx* const pwtx = it->second;
- if (pwtx == &wtx) {
- continue;
- }
- int64_t nSmartTime;
- nSmartTime = pwtx->nTimeSmart;
- if (!nSmartTime) {
- nSmartTime = pwtx->nTimeReceived;
- }
- if (nSmartTime <= latestTolerated) {
- latestEntry = nSmartTime;
- if (nSmartTime > latestNow) {
- latestNow = nSmartTime;
+ int64_t block_max_time;
+ if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime).maxTime(block_max_time))) {
+ if (rescanning_old_block) {
+ nTimeSmart = block_max_time;
+ } else {
+ int64_t latestNow = wtx.nTimeReceived;
+ int64_t latestEntry = 0;
+
+ // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
+ int64_t latestTolerated = latestNow + 300;
+ const TxItems& txOrdered = wtxOrdered;
+ for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
+ CWalletTx* const pwtx = it->second;
+ if (pwtx == &wtx) {
+ continue;
+ }
+ int64_t nSmartTime;
+ nSmartTime = pwtx->nTimeSmart;
+ if (!nSmartTime) {
+ nSmartTime = pwtx->nTimeReceived;
+ }
+ if (nSmartTime <= latestTolerated) {
+ latestEntry = nSmartTime;
+ if (nSmartTime > latestNow) {
+ latestNow = nSmartTime;
+ }
+ break;
}
- break;
}
- }
- nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
+ nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
+ }
} else {
WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.m_confirm.hashBlock.ToString());
}
@@ -2516,6 +2541,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet);
+ bool rescan_required = false;
DBErrors nLoadWalletRet = walletInstance->LoadWallet();
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
@@ -2536,6 +2562,10 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
{
error = strprintf(_("Wallet needed to be rewritten: restart %s to complete"), PACKAGE_NAME);
return nullptr;
+ } else if (nLoadWalletRet == DBErrors::NEED_RESCAN) {
+ warnings.push_back(strprintf(_("Error reading %s! Transaction data may be missing or incorrect."
+ " Rescanning wallet."), walletFile));
+ rescan_required = true;
}
else {
error = strprintf(_("Error loading %s"), walletFile);
@@ -2716,7 +2746,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
_("The wallet will avoid paying less than the minimum relay fee."));
}
- walletInstance->m_confirm_target = args.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
+ walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
walletInstance->m_signal_rbf = args.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
@@ -2727,7 +2757,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
LOCK(walletInstance->cs_wallet);
- if (chain && !AttachChain(walletInstance, *chain, error, warnings)) {
+ if (chain && !AttachChain(walletInstance, *chain, rescan_required, error, warnings)) {
return nullptr;
}
@@ -2749,7 +2779,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
return walletInstance;
}
-bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings)
+bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
LOCK(walletInstance->cs_wallet);
// allow setting the chain if it hasn't been set already but prevent changing it
@@ -2766,8 +2796,9 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
// interface.
walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
+ // If rescan_required = true, rescan_height remains equal to 0
int rescan_height = 0;
- if (!gArgs.GetBoolArg("-rescan", false))
+ if (!rescan_required)
{
WalletBatch batch(walletInstance->GetDatabase());
CBlockLocator locator;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 2dc9eff712..767b24adbb 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -6,7 +6,7 @@
#ifndef BITCOIN_WALLET_WALLET_H
#define BITCOIN_WALLET_WALLET_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <outputtype.h>
@@ -256,8 +256,8 @@ private:
*/
typedef std::multimap<COutPoint, uint256> TxSpends;
TxSpends mapTxSpends GUARDED_BY(cs_wallet);
- void AddToSpends(const COutPoint& outpoint, const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void AddToSpends(const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AddToSpends(const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
@@ -271,8 +271,11 @@ private:
* abandoned is an indication that it is not safe to be considered abandoned.
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
+ *
+ * Should be called with rescanning_old_block set to true, if the transaction is
+ * not discovered in real time, but during a rescan of old blocks.
*/
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate, bool rescanning_old_block) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
@@ -284,7 +287,7 @@ private:
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** WalletFlags set on this wallet. */
std::atomic<uint64_t> m_wallet_flags{0};
@@ -334,7 +337,7 @@ private:
* block locator and m_last_block_processed, and registering for
* notifications about new blocks and transactions.
*/
- static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings);
+ static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings);
public:
/**
@@ -449,9 +452,9 @@ public:
bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool LockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool UnlockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ListLockedCoins(std::vector<COutPoint>& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/*
@@ -484,7 +487,7 @@ public:
bool EncryptWallet(const SecureString& strWalletPassphrase);
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
+ unsigned int ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const;
/**
* Increment the next transaction order id
@@ -503,7 +506,7 @@ public:
//! @return true if wtx is changed and needs to be saved to disk, otherwise false
using UpdateWalletTxFn = std::function<bool(CWalletTx& wtx, bool new_tx)>;
- CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true);
+ CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true, bool rescanning_old_block = false);
bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override;
void blockConnected(const CBlock& block, int height) override;
@@ -573,14 +576,13 @@ public:
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
bool SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const;
- bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
+ bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const
{
std::vector<CTxOut> v_txouts(txouts.size());
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
- return DummySignTx(txNew, v_txouts, use_max_sig);
+ return DummySignTx(txNew, v_txouts, coin_control);
}
- bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
- bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
+ bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control = nullptr) const;
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -925,4 +927,6 @@ bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
//! Remove wallet name from persistent configuration so it will not be loaded on startup.
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
+bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig);
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 03464cd2c8..8ff09a0878 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -40,6 +40,7 @@ const std::string FLAGS{"flags"};
const std::string HDCHAIN{"hdchain"};
const std::string KEYMETA{"keymeta"};
const std::string KEY{"key"};
+const std::string LOCKED_UTXO{"lockedutxo"};
const std::string MASTER_KEY{"mkey"};
const std::string MINVERSION{"minversion"};
const std::string NAME{"name"};
@@ -284,6 +285,16 @@ bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const Descri
return true;
}
+bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
+{
+ return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
+}
+
+bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
+{
+ return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
+}
+
class CWalletScanState {
public:
unsigned int nKeys{0};
@@ -300,6 +311,7 @@ public:
std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys;
std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys;
std::map<uint160, CHDChain> m_hd_chains;
+ bool tx_corrupt{false};
CWalletScanState() {
}
@@ -334,7 +346,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
// LoadToWallet call below creates a new CWalletTx that fill_wtx
// callback fills with transaction metadata.
auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
- assert(new_tx);
+ if(!new_tx) {
+ // There's some corruption here since the tx we just tried to load was already in the wallet.
+ // We don't consider this type of corruption critical, and can fix it by removing tx data and
+ // rescanning.
+ wss.tx_corrupt = true;
+ return false;
+ }
ssValue >> wtx;
if (wtx.GetHash() != hash)
return false;
@@ -701,6 +719,12 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey)));
wss.fIsEncrypted = true;
+ } else if (strType == DBKeys::LOCKED_UTXO) {
+ uint256 hash;
+ uint32_t n;
+ ssKey >> hash;
+ ssKey >> n;
+ pwallet->LockCoin(COutPoint(hash, n));
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
@@ -738,6 +762,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
{
CWalletScanState wss;
bool fNoncriticalErrors = false;
+ bool rescan_required = false;
DBErrors result = DBErrors::LOAD_OK;
LOCK(pwallet->cs_wallet);
@@ -801,12 +826,17 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
} else if (strType == DBKeys::FLAGS) {
// reading the wallet flags can only fail if unknown flags are present
result = DBErrors::TOO_NEW;
+ } else if (wss.tx_corrupt) {
+ pwallet->WalletLogPrintf("Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.\n");
+ // Set tx_corrupt back to false so that the error is only printed once (per corrupt tx)
+ wss.tx_corrupt = false;
+ result = DBErrors::CORRUPT;
} else {
// Leave other errors alone, if we try to fix them we might make things worse.
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
if (strType == DBKeys::TX)
// Rescan if there is a bad transaction record:
- gArgs.SoftSetBoolArg("-rescan", true);
+ rescan_required = true;
}
}
if (!strErr.empty())
@@ -842,8 +872,11 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second);
}
- if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
+ if (rescan_required && result == DBErrors::LOAD_OK) {
+ result = DBErrors::NEED_RESCAN;
+ } else if (fNoncriticalErrors && result == DBErrors::LOAD_OK) {
result = DBErrors::NONCRITICAL_ERROR;
+ }
// Any wallet corruption at all: skip any rewriting or
// upgrading, we don't want to make it worse.
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 25c2ec5909..9c752623b3 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_WALLET_WALLETDB_H
#define BITCOIN_WALLET_WALLETDB_H
-#include <amount.h>
#include <script/sign.h>
#include <wallet/db.h>
#include <wallet/walletutil.h>
@@ -48,7 +47,8 @@ enum class DBErrors
NONCRITICAL_ERROR,
TOO_NEW,
LOAD_FAIL,
- NEED_REWRITE
+ NEED_REWRITE,
+ NEED_RESCAN
};
namespace DBKeys {
@@ -65,6 +65,7 @@ extern const std::string FLAGS;
extern const std::string HDCHAIN;
extern const std::string KEY;
extern const std::string KEYMETA;
+extern const std::string LOCKED_UTXO;
extern const std::string MASTER_KEY;
extern const std::string MINVERSION;
extern const std::string NAME;
@@ -250,6 +251,9 @@ public:
bool WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index);
bool WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache);
+ bool WriteLockedUTXO(const COutPoint& output);
+ bool EraseLockedUTXO(const COutPoint& output);
+
/// Write destination data key,value tuple to database
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
/// Erase destination data tuple from wallet database
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 50b6c9d29f..e3cb5cee5d 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -76,6 +76,10 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
} else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
return nullptr;
+ } else if (load_wallet_ret == DBErrors::NEED_RESCAN) {
+ tfm::format(std::cerr, "Error reading %s! Some transaction data might be missing or"
+ " incorrect. Wallet requires a rescan.",
+ name);
} else {
tfm::format(std::cerr, "Error loading %s", name);
return nullptr;
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 86f47d71f3..a53de34db4 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -47,7 +47,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
notifier->SetType(entry.first);
notifier->SetAddress(address);
- notifier->SetOutboundMessageHighWaterMark(static_cast<int>(gArgs.GetArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM)));
+ notifier->SetOutboundMessageHighWaterMark(static_cast<int>(gArgs.GetIntArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM)));
notifiers.push_back(std::move(notifier));
}
}