aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci/test/00_setup_env_i686_multiprocess.sh1
-rw-r--r--configure.ac39
-rw-r--r--doc/release-notes-12677.md8
-rw-r--r--doc/release-notes-23065.md15
-rw-r--r--doc/release-notes-remove-rescan.md9
-rw-r--r--doc/release-notes.md18
-rw-r--r--src/Makefile.am5
-rw-r--r--src/addrdb.cpp10
-rw-r--r--src/addrdb.h8
-rw-r--r--src/addrman.cpp535
-rw-r--r--src/addrman.h374
-rw-r--r--src/addrman_impl.h271
-rw-r--r--src/bench/addrman.cpp20
-rw-r--r--src/bitcoin-tx.cpp1
-rw-r--r--src/bitcoind.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.h2
-rw-r--r--src/core_read.cpp2
-rw-r--r--src/core_write.cpp1
-rw-r--r--src/httpserver.cpp3
-rw-r--r--src/index/base.cpp2
-rw-r--r--src/init.cpp38
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/interfaces/wallet.h2
-rw-r--r--src/logging.cpp1
-rw-r--r--src/logging.h1
-rw-r--r--src/mapport.cpp2
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net.cpp21
-rw-r--r--src/net.h6
-rw-r--r--src/net_processing.cpp11
-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/psbt.cpp2
-rw-r--r--src/policy/feerate.h2
-rw-r--r--src/policy/fees.h2
-rw-r--r--src/primitives/transaction.cpp1
-rw-r--r--src/primitives/transaction.h2
-rw-r--r--src/qt/bitcoinamountfield.h2
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/bitcoinunits.cpp2
-rw-r--r--src/qt/bitcoinunits.h2
-rw-r--r--src/qt/coincontroldialog.h2
-rw-r--r--src/qt/guiutil.h2
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/qt/sendcoinsrecipient.h2
-rw-r--r--src/qt/transactionfilterproxy.h2
-rw-r--r--src/qt/transactionrecord.h2
-rw-r--r--src/qt/walletmodeltransaction.h2
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/rpc/blockchain.cpp2
-rw-r--r--src/rpc/blockchain.h2
-rw-r--r--src/rpc/mining.cpp4
-rw-r--r--src/rpc/misc.cpp25
-rw-r--r--src/rpc/rawtransaction.cpp1
-rw-r--r--src/rpc/rawtransaction_util.cpp1
-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/sign.cpp1
-rw-r--r--src/test/addrman_tests.cpp211
-rw-r--r--src/test/amount_tests.cpp2
-rw-r--r--src/test/fuzz/addrman.cpp77
-rw-r--r--src/test/fuzz/coins_view.cpp2
-rw-r--r--src/test/fuzz/connman.cpp2
-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/parse_numbers.cpp7
-rw-r--r--src/test/fuzz/script_flags.cpp1
-rw-r--r--src/test/fuzz/string.cpp24
-rw-r--r--src/test/fuzz/util.cpp1
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/policy_fee_tests.cpp2
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/test/util_tests.cpp155
-rw-r--r--src/test/validation_tests.cpp1
-rw-r--r--src/torcontrol.cpp4
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util/moneystr.cpp5
-rw-r--r--src/util/moneystr.h2
-rw-r--r--src/util/strencodings.cpp44
-rw-r--r--src/util/strencodings.h43
-rw-r--r--src/util/syscall_sandbox.cpp894
-rw-r--r--src/util/syscall_sandbox.h57
-rw-r--r--src/util/system.cpp20
-rw-r--r--src/validation.cpp1
-rw-r--r--src/validation.h2
-rw-r--r--src/wallet/coincontrol.h29
-rw-r--r--src/wallet/coinselection.cpp1
-rw-r--r--src/wallet/coinselection.h14
-rw-r--r--src/wallet/fees.h2
-rw-r--r--src/wallet/interfaces.cpp2
-rw-r--r--src/wallet/receive.cpp1
-rw-r--r--src/wallet/receive.h2
-rw-r--r--src/wallet/rpcwallet.cpp134
-rw-r--r--src/wallet/spend.cpp82
-rw-r--r--src/wallet/spend.h6
-rw-r--r--src/wallet/test/coinselector_tests.cpp2
-rw-r--r--src/wallet/test/spend_tests.cpp1
-rw-r--r--src/wallet/transaction.h6
-rw-r--r--src/wallet/wallet.cpp24
-rw-r--r--src/wallet/wallet.h11
-rw-r--r--src/wallet/walletdb.h1
-rw-r--r--test/config.ini.in1
-rwxr-xr-xtest/functional/feature_addrman.py4
-rwxr-xr-xtest/functional/feature_notifications.py3
-rwxr-xr-xtest/functional/feature_rbf.py25
-rwxr-xr-xtest/functional/feature_syscall_sandbox.py34
-rwxr-xr-xtest/functional/feature_versionbits_warning.py3
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py54
-rwxr-xr-xtest/functional/rpc_misc.py2
-rwxr-xr-xtest/functional/rpc_psbt.py40
-rwxr-xr-xtest/functional/rpc_signer.py3
-rwxr-xr-xtest/functional/test_framework/test_framework.py11
-rw-r--r--test/functional/test_framework/util.py9
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_send.py46
-rwxr-xr-xtest/functional/wallet_signer.py3
-rwxr-xr-xtest/lint/lint-locale-dependence.sh11
132 files changed, 2671 insertions, 1000 deletions
diff --git a/ci/test/00_setup_env_i686_multiprocess.sh b/ci/test/00_setup_env_i686_multiprocess.sh
index f7f65f6e3a..a25c98a004 100755
--- a/ci/test/00_setup_env_i686_multiprocess.sh
+++ b/ci/test/00_setup_env_i686_multiprocess.sh
@@ -14,4 +14,5 @@ export DEP_OPTS="DEBUG=1 MULTIPROCESS=1"
export GOAL="install"
export BITCOIN_CONFIG="--enable-debug CC='clang -m32' CXX='clang++ -m32' LDFLAGS='--rtlib=compiler-rt -lgcc_s'"
export TEST_RUNNER_ENV="BITCOIND=bitcoin-node"
+export TEST_RUNNER_EXTRA="--nosandbox"
export PIP_PACKAGES="lief"
diff --git a/configure.ac b/configure.ac
index 26092a7b8d..7dbefd9042 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,12 @@ case $host in
;;
esac
+AC_ARG_WITH([seccomp],
+ [AS_HELP_STRING([--with-seccomp],
+ [enable experimental syscall sandbox feature (-sandbox), default is yes if seccomp-bpf is detected under Linux x86_64])],
+ [seccomp_found=$withval],
+ [seccomp_found=auto])
+
dnl Require C++17 compiler (no GNU extensions)
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
@@ -101,7 +107,7 @@ AC_PATH_TOOL(GCOV, gcov)
AC_PATH_TOOL(LLVM_COV, llvm-cov)
AC_PATH_PROG(LCOV, lcov)
dnl Python 3.6 is specified in .python-version and should be used if available, see doc/dependencies.md
-AC_PATH_PROGS([PYTHON], [python3.6 python3.7 python3.8 python3.9 python3 python])
+AC_PATH_PROGS([PYTHON], [python3.6 python3.7 python3.8 python3.9, python3.10, python3 python])
AC_PATH_PROG(GENHTML, genhtml)
AC_PATH_PROG([GIT], [git])
AC_PATH_PROG(CCACHE,ccache)
@@ -1432,6 +1438,36 @@ if test "x$use_external_signer" != xno; then
fi
AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "x$use_external_signer" = "xyes"])
+dnl Do not compile with syscall sandbox support when compiling under the sanitizers.
+dnl The sanitizers introduce use of syscalls that are not typically used in bitcoind
+dnl (such as execve when the sanitizers execute llvm-symbolizer).
+if test x$use_sanitizers != x; then
+ AC_MSG_WARN(Specifying --with-sanitizers forces --without-seccomp since the sanitizers introduce use of syscalls not allowed by the bitcoind syscall sandbox (-sandbox=<mode>).)
+ seccomp_found=no
+fi
+if test "x$seccomp_found" != "xno"; then
+ AC_MSG_CHECKING([for seccomp-bpf (Linux x86-64)])
+ AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[
+ @%:@include <linux/seccomp.h>
+ ]], [[
+ #if !defined(__x86_64__)
+ # error Syscall sandbox is an experimental feature currently available only under Linux x86-64.
+ #endif
+ ]])],[
+ AC_MSG_RESULT(yes)
+ seccomp_found="yes"
+ AC_DEFINE(USE_SYSCALL_SANDBOX, 1, [Define this symbol to build with syscall sandbox support.])
+ ],[
+ AC_MSG_RESULT(no)
+ seccomp_found="no"
+ ])
+fi
+dnl Currently only enable -sandbox=<mode> feature if seccomp is found.
+dnl In the future, sandboxing could be also be supported with other
+dnl sandboxing mechanisms besides seccomp.
+use_syscall_sandbox=$seccomp_found
+AM_CONDITIONAL([ENABLE_SYSCALL_SANDBOX], [test "x$use_syscall_sandbox" != "xno"])
+
dnl Check for reduced exports
if test x$use_reduce_exports = xyes; then
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[CXXFLAGS="$CXXFLAGS -fvisibility=hidden"],
@@ -1921,6 +1957,7 @@ echo
echo "Options used to compile and link:"
echo " external signer = $use_external_signer"
echo " multiprocess = $build_multiprocess"
+echo " with experimental syscall sandbox support = $use_syscall_sandbox"
echo " with libs = $build_bitcoin_libs"
echo " with wallet = $enable_wallet"
if test "x$enable_wallet" != "xno"; then
diff --git a/doc/release-notes-12677.md b/doc/release-notes-12677.md
deleted file mode 100644
index d6fea9eae7..0000000000
--- a/doc/release-notes-12677.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Notable changes
-===============
-
-Updated RPCs
-------------
-
-- `listunspent` now includes `ancestorcount`, `ancestorsize`, and
-`ancestorfees` for each transaction output that is still in the mempool.
diff --git a/doc/release-notes-23065.md b/doc/release-notes-23065.md
deleted file mode 100644
index 6ec002b2df..0000000000
--- a/doc/release-notes-23065.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Notable changes
-===============
-
-Updated RPCs
-------------
-
-- `lockunspent` now optionally takes a third parameter, `persistent`, which
-causes the lock to be written persistently to the wallet database. This
-allows UTXOs to remain locked even after node restarts or crashes.
-
-GUI changes
------------
-
-- UTXOs which are locked via the GUI are now stored persistently in the
-wallet database, so are not lost on node shutdown or crash. \ No newline at end of file
diff --git a/doc/release-notes-remove-rescan.md b/doc/release-notes-remove-rescan.md
deleted file mode 100644
index fcbe7e6a82..0000000000
--- a/doc/release-notes-remove-rescan.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Notable changes
-===============
-
-Rescan startup parameter removed
---------------------------------
-
-The `-rescan` startup parameter has been removed. Wallets which require
-rescanning due to corruption will still be rescanned on startup.
-Otherwise, please use the `rescanblockchain` RPC to trigger a rescan. \ No newline at end of file
diff --git a/doc/release-notes.md b/doc/release-notes.md
index a0c1ed3b31..81e79dd3a9 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -61,6 +61,13 @@ P2P and network changes
They will become eligible for address gossip after sending an ADDR, ADDRV2,
or GETADDR message. (#21528)
+Rescan startup parameter removed
+--------------------------------
+
+The `-rescan` startup parameter has been removed. Wallets which require
+rescanning due to corruption will still be rescanned on startup.
+Otherwise, please use the `rescanblockchain` RPC to trigger a rescan. (#23123)
+
Updated RPCs
------------
@@ -70,6 +77,14 @@ Updated RPCs
`/rest/block` no longer return the `addresses` and `reqSigs` fields, which
were previously deprecated in 22.0. (#22650)
+- `listunspent` now includes `ancestorcount`, `ancestorsize`, and
+ `ancestorfees` for each transaction output that is still in the mempool.
+ (#12677)
+
+- `lockunspent` now optionally takes a third parameter, `persistent`, which
+ causes the lock to be written persistently to the wallet database. This
+ allows UTXOs to remain locked even after node restarts or crashes. (#23065)
+
New RPCs
--------
@@ -113,6 +128,9 @@ Wallet
GUI changes
-----------
+- UTXOs which are locked via the GUI are now stored persistently in the
+ wallet database, so are not lost on node shutdown or crash. (#23065)
+
Low-level changes
=================
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/addrdb.cpp b/src/addrdb.cpp
index 94c77a6d89..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.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
- 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);
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 7c6b8fe64d..c364a7710b 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -4,25 +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};
@@ -39,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();
@@ -49,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();
@@ -60,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;
@@ -86,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);
@@ -101,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}
@@ -119,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);
@@ -183,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;
@@ -192,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;
@@ -223,7 +230,7 @@ void CAddrMan::Serialize(Stream& s_) const
}
template <typename Stream>
-void CAddrMan::Unserialize(Stream& s_)
+void AddrManImpl::Unserialize(Stream& s_)
{
LOCK(cs);
@@ -262,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();
@@ -287,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);
@@ -344,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;
@@ -394,16 +401,7 @@ void CAddrMan::Unserialize(Stream& s_)
}
}
-// 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);
@@ -418,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);
@@ -432,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);
@@ -456,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);
@@ -472,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;
@@ -489,7 +487,7 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
}
}
-void CAddrMan::MakeTried(CAddrInfo& info, int nId)
+void AddrManImpl::MakeTried(AddrInfo& info, int nId)
{
AssertLockHeld(cs);
@@ -517,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;
@@ -542,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);
@@ -550,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)
@@ -598,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);
@@ -607,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) {
@@ -654,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;
@@ -673,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)
@@ -697,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 &&
@@ -722,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 {
@@ -740,118 +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;
- }
- }
-}
-
-void CAddrMan::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 CAddrMan::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 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);
@@ -865,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;
@@ -874,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;
@@ -882,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)
@@ -908,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)
@@ -928,7 +826,7 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
info.nServices = nServices;
}
-void CAddrMan::ResolveCollisions_()
+void AddrManImpl::ResolveCollisions_()
{
AssertLockHeld(cs);
@@ -941,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);
@@ -952,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)) {
@@ -989,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();
@@ -1004,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;
+ }
+
+ 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();
+}
- return mapInfo[id_old];
+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 7dd8528bef..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,290 +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, 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);
+ 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()
@@ -419,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/bench/addrman.cpp b/src/bench/addrman.cpp
index bebf86a09d..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);
@@ -105,7 +105,7 @@ static void AddrManGetAddr(benchmark::Bench& bench)
static void AddrManAddThenGood(benchmark::Bench& bench)
{
- 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) {
addrman.Good(g_addresses[source_i][addr_i]);
@@ -117,12 +117,12 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
bench.run([&] {
// To make the benchmark independent of the number of evaluations, we always prepare a new addrman.
- // This is necessary because CAddrMan::Good() method modifies the object, affecting the timing of subsequent calls
+ // 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
- // CAddrMan::Good() will still be noticeable.
- CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ // AddrMan::Good() will still be noticeable.
+ AddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
AddAddressesToAddrMan(addrman);
markSomeAsGood(addrman);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 98916460aa..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>
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/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 f00f155249..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>
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 6108961010..320811b9e9 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -69,7 +69,7 @@ CScript ParseScript(const std::string& s)
(w->front() == '-' && w->size() > 1 && std::all_of(w->begin()+1, w->end(), ::IsDigit)))
{
// Number
- int64_t n = atoi64(*w);
+ int64_t n = LocaleIndependentAtoi<int64_t>(*w);
//limit the range of numbers ParseScript accepts in decimal
//since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts
diff --git a/src/core_write.cpp b/src/core_write.cpp
index d92c970cb6..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>
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 36bbece0ea..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()
@@ -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();
}
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 65e3c275e9..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>
@@ -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);
}
@@ -1018,6 +1023,37 @@ bool AppInitParameterInteraction(const ArgsManager& args)
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;
}
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 6766e0510f..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
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 33d115f279..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>
diff --git a/src/net.cpp b/src/net.cpp
index b8ff0b13ea..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>
@@ -1615,6 +1616,7 @@ void CConnman::SocketHandler()
void CConnman::ThreadSocketHandler()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET);
while (!interruptNet)
{
DisconnectNodes();
@@ -1634,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);
@@ -1816,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())
{
@@ -2006,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
@@ -2025,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
@@ -2046,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,
@@ -2155,6 +2160,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
void CConnman::ThreadOpenAddedConnections()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION);
while (true)
{
CSemaphoreGrant grant(*semAddnode);
@@ -2218,6 +2224,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
void CConnman::ThreadMessageHandler()
{
+ SetSyscallSandboxPolicy(SyscallSandboxPolicy::MESSAGE_HANDLER);
FastRandomContext rng;
while (!flagInterruptMsgProc)
{
@@ -2437,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 008b4d679c..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;
@@ -1425,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),
@@ -2663,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,
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/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/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/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.h b/src/qt/bitcoingui.h
index 27045f5cc3..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>
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.h b/src/qt/coincontroldialog.h
index bcaf45df42..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>
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 274f0bdcbf..6287f95192 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>
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 65544acfbd..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>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 1c8ed22ada..3c0dc5aa40 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -250,7 +250,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes
for(char argch: curarg)
if (!IsDigit(argch))
throw std::runtime_error("Invalid result query");
- subelement = lastResult[atoi(curarg.c_str())];
+ subelement = lastResult[LocaleIndependentAtoi<int>(curarg)];
}
else if (lastResult.isObject())
subelement = find_value(lastResult, curarg);
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/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/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.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/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 3370afc75f..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>
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 4b0d855685..09e471afdd 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_RPC_BLOCKCHAIN_H
#define BITCOIN_RPC_BLOCKCHAIN_H
-#include <amount.h>
+#include <consensus/amount.h>
#include <streams.h>
#include <sync.h>
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 066a60b71b..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>
@@ -702,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
{
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index dff2129980..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>
@@ -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",
@@ -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/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index b32bc670b6..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>
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.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/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/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/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 45ee778b87..cfeab9dcdc 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -4,6 +4,7 @@
#include <addrdb.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <chainparams.h>
#include <merkleblock.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -27,29 +28,29 @@ FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
{
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);
+ AddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
try {
ReadFromStream(addr_man, data_stream);
} catch (const std::exception&) {
}
}
-class CAddrManDeterministic : public CAddrMan
+class AddrManDeterministic : public 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)
+ explicit AddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider)
+ : AddrMan(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)});
+ WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
}
/**
* Generate a random address. Always returns a valid address.
*/
- CNetAddr RandAddr() EXCLUSIVE_LOCKS_REQUIRED(cs)
+ CNetAddr RandAddr() EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs)
{
CNetAddr addr;
if (m_fuzzed_data_provider.remaining_bytes() > 1 && m_fuzzed_data_provider.ConsumeBool()) {
@@ -61,7 +62,7 @@ public:
{4, ADDR_TORV3_SIZE},
{5, ADDR_I2P_SIZE},
{6, ADDR_CJDNS_SIZE}};
- uint8_t net = insecure_rand.randrange(5) + 1; // [1..5]
+ uint8_t net = m_impl->insecure_rand.randrange(5) + 1; // [1..5]
if (net == 3) {
net = 6;
}
@@ -69,7 +70,7 @@ public:
CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
s << net;
- s << insecure_rand.randbytes(net_len_map.at(net));
+ s << m_impl->insecure_rand.randbytes(net_len_map.at(net));
s >> addr;
}
@@ -89,7 +90,7 @@ public:
*/
void Fill()
{
- LOCK(cs);
+ LOCK(m_impl->cs);
// Add some of the addresses directly to the "tried" table.
@@ -102,20 +103,20 @@ public:
// 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]
+ const size_t num_addresses = m_impl->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);
- Add_(addr, source, time_penalty);
+ const auto time_penalty = m_impl->insecure_rand.randrange(100000001);
+ m_impl->Add_(addr, source, time_penalty);
- if (n > 0 && mapInfo.size() % n == 0) {
- Good_(addr, false, GetTime());
+ if (n > 0 && m_impl->mapInfo.size() % n == 0) {
+ m_impl->Good_(addr, false, GetTime());
}
// 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);
+ if (m_impl->insecure_rand.randrange(10) == 0 && prev_source.IsValid()) {
+ m_impl->Add_({addr}, prev_source, time_penalty);
}
}
prev_source = source;
@@ -129,46 +130,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);
}
@@ -176,14 +177,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]`
@@ -191,7 +192,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;
}
}
@@ -200,7 +201,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;
}
}
@@ -222,7 +223,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);
@@ -231,10 +232,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,
@@ -283,7 +284,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),
@@ -301,8 +302,8 @@ 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);
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/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/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_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 dc2bf7c860..ab646c68fc 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -122,6 +122,12 @@ bool LegacyParseUInt64(const std::string& str, uint64_t* out)
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)
@@ -268,4 +274,22 @@ FUZZ_TARGET(string)
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/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/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/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/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 97e614379c..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,
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index a13700d733..51707310a2 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1474,6 +1474,35 @@ 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);
@@ -1486,27 +1515,14 @@ BOOST_AUTO_TEST_CASE(test_ToIntegral)
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-1234").value(), -1'234);
BOOST_CHECK_EQUAL(ToIntegral<int32_t>("-1").value(), -1);
- BOOST_CHECK(!ToIntegral<int32_t>(" 1"));
- BOOST_CHECK(!ToIntegral<int32_t>("1 "));
- BOOST_CHECK(!ToIntegral<int32_t>("1a"));
- BOOST_CHECK(!ToIntegral<int32_t>("1.1"));
- BOOST_CHECK(!ToIntegral<int32_t>("1.9"));
- BOOST_CHECK(!ToIntegral<int32_t>("+01.9"));
- BOOST_CHECK(!ToIntegral<int32_t>(" -1"));
- BOOST_CHECK(!ToIntegral<int32_t>("-1 "));
- BOOST_CHECK(!ToIntegral<int32_t>(" -1 "));
- BOOST_CHECK(!ToIntegral<int32_t>("+1"));
- BOOST_CHECK(!ToIntegral<int32_t>(" +1"));
- BOOST_CHECK(!ToIntegral<int32_t>(" +1 "));
- BOOST_CHECK(!ToIntegral<int32_t>("+-1"));
- BOOST_CHECK(!ToIntegral<int32_t>("-+1"));
- BOOST_CHECK(!ToIntegral<int32_t>("++1"));
- BOOST_CHECK(!ToIntegral<int32_t>("--1"));
- BOOST_CHECK(!ToIntegral<int32_t>(""));
- BOOST_CHECK(!ToIntegral<int32_t>("aap"));
- BOOST_CHECK(!ToIntegral<int32_t>("0x1"));
- BOOST_CHECK(!ToIntegral<int32_t>("-32482348723847471234"));
- BOOST_CHECK(!ToIntegral<int32_t>("32482348723847471234"));
+ 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);
@@ -1549,6 +1565,77 @@ BOOST_AUTO_TEST_CASE(test_ToIntegral)
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;
@@ -1714,32 +1801,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_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/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/txmempool.h b/src/txmempool.h
index 27ee0628a7..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>
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/strencodings.cpp b/src/util/strencodings.cpp
index 0aa80ea0ae..15bd07b374 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -281,16 +281,11 @@ 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&);
-
namespace {
template <typename T>
bool ParseIntegral(const std::string& str, T* out)
{
static_assert(std::is_integral<T>::value);
- if (!ParsePrechecks(str)) {
- return false;
- }
// Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
// handling leading +/- for backwards compatibility.
if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
@@ -307,17 +302,6 @@ bool ParseIntegral(const std::string& str, T* out)
}
}; // namespace
-[[nodiscard]] static bool ParsePrechecks(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 ParseInt32(const std::string& str, int32_t* out)
{
return ParseIntegral<int32_t>(str, out);
@@ -348,20 +332,6 @@ bool ParseUInt64(const std::string& str, uint64_t* out)
return ParseIntegral<uint64_t>(str, out);
}
-bool ParseDouble(const std::string& str, double *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();
-}
-
std::string FormatParagraph(const std::string& in, size_t width, size_t indent)
{
std::stringstream out;
@@ -403,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 1217572c45..1f7762aeef 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -11,6 +11,7 @@
#include <attributes.h>
#include <span.h>
+#include <util/string.h>
#include <charconv>
#include <cstdint>
@@ -68,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 the ParseInt64/ParseUInt64/ParseInt32/ParseUInt32 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.
@@ -97,7 +123,9 @@ constexpr inline bool IsSpace(char c) noexcept {
}
/**
- * Convert string to integral type T.
+ * 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]+`.
*
* @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.
@@ -111,7 +139,7 @@ std::optional<T> ToIntegral(const std::string& str)
if (first_nonmatching != str.data() + str.size() || error_condition != std::errc{}) {
return std::nullopt;
}
- return {result};
+ return result;
}
/**
@@ -157,13 +185,6 @@ std::optional<T> ToIntegral(const std::string& str)
[[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..6d62864a4a
--- /dev/null
+++ b/src/util/syscall_sandbox.cpp
@@ -0,0 +1,894 @@
+// 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.
+#ifndef __NR_statx
+#define __NR_statx 332
+#endif
+
+#ifndef __NR_getrandom
+#define __NR_getrandom 318
+#endif
+
+#ifndef __NR_membarrier
+#define __NR_membarrier 324
+#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_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"},
+ {__NR_pkey_alloc, "pkey_alloc"},
+ {__NR_pkey_free, "pkey_free"},
+ {__NR_pkey_mprotect, "pkey_mprotect"},
+ {__NR_poll, "poll"},
+ {__NR_ppoll, "ppoll"},
+ {__NR_prctl, "prctl"},
+ {__NR_pread64, "pread64"},
+ {__NR_preadv, "preadv"},
+ {__NR_preadv2, "preadv2"},
+ {__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"},
+ {__NR_pwritev2, "pwritev2"},
+ {__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_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_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_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 4defeed4ce..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)
@@ -594,7 +592,7 @@ std::string ArgsManager::GetArg(const std::string& strArg, const std::string& st
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/validation.cpp b/src/validation.cpp
index 4504d2ca0a..880a01eb7d 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>
diff --git a/src/validation.h b/src/validation.h
index b2282828ce..b10050f931 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -10,10 +10,10 @@
#include <config/bitcoin-config.h>
#endif
-#include <amount.h>
#include <arith_uint256.h>
#include <attributes.h>
#include <chain.h>
+#include <consensus/amount.h>
#include <fs.h>
#include <policy/feerate.h>
#include <policy/packages.h>
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 85cbec76b7..c989512d3e 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 Select(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 8d08153501..e1ca3fb379 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -4,6 +4,7 @@
#include <wallet/coinselection.h>
+#include <consensus/amount.h>
#include <policy/feerate.h>
#include <util/check.h>
#include <util/system.h>
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index a28bee622e..e7d467660f 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -5,7 +5,7 @@
#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>
@@ -37,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;
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/interfaces.cpp b/src/wallet/interfaces.cpp
index d9fc6de79b..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>
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/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index f6cf8868de..39cde4e4ec 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;
@@ -3213,6 +3214,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)},
@@ -3289,6 +3291,54 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
}
+ if (options.exists("solving_data")) {
+ 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));
+ 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");
@@ -3306,6 +3356,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.Select(coin.first, coin.second.out);
+ }
+ }
+
bilingual_str error;
if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
@@ -3321,8 +3384,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"
@@ -3357,6 +3421,26 @@ static RPCHelpMan fundrawtransaction()
{"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\"") + "\""},
+ {"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"},
+ },
+ }
+ }
+ },
},
"options"},
{"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
@@ -4202,6 +4286,26 @@ static RPCHelpMan send()
},
{"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"},
+ },
+ }
+ }
+ },
},
"options"},
},
@@ -4489,7 +4593,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.",
{
@@ -4546,6 +4652,26 @@ static RPCHelpMan walletcreatefundedpsbt()
{"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\"") + "\""},
+ {"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"},
+ },
+ }
+ }
+ },
},
"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/spend.cpp b/src/wallet/spend.cpp
index 1724375f4c..5bed09e067 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)
@@ -435,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 <= 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 {
- 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.
@@ -788,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");
+ error = _("Missing solving data for estimating transaction size");
return false;
}
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
@@ -813,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/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 40d9e90d56..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>
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/transaction.h b/src/wallet/transaction.h
index 0cd91b9ebe..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>
@@ -216,9 +216,9 @@ public:
}
const auto it_op = mapValue.find("n");
- nOrderPos = (it_op != mapValue.end()) ? atoi64(it_op->second) : -1;
+ 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>(atoi64(it_ts->second)) : 0;
+ 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 1c18cdb1bc..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>
@@ -1448,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);
@@ -1468,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++;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index cedccf7d44..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>
@@ -576,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);
@@ -928,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.h b/src/wallet/walletdb.h
index 715d9012ed..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>
diff --git a/test/config.ini.in b/test/config.ini.in
index db80bba6f1..8bcba1b39c 100644
--- a/test/config.ini.in
+++ b/test/config.ini.in
@@ -24,3 +24,4 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
@ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true
@ENABLE_EXTERNAL_SIGNER_TRUE@ENABLE_EXTERNAL_SIGNER=true
+@ENABLE_SYSCALL_SANDBOX_TRUE@ENABLE_SYSCALL_SANDBOX=true
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 5a8394db2e..93d50c1369 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -109,7 +109,7 @@ class AddrmanTest(BitcoinTestFramework):
self.stop_node(0)
write_addrman(peers_dat, len_tried=-1)
self.nodes[0].assert_start_raises_init_error(
- expected_msg=init_error("Corrupt CAddrMan serialization: nTried=-1, should be in \\[0, 16384\\]:.*"),
+ expected_msg=init_error("Corrupt AddrMan serialization: nTried=-1, should be in \\[0, 16384\\]:.*"),
match=ErrorMatch.FULL_REGEX,
)
@@ -117,7 +117,7 @@ class AddrmanTest(BitcoinTestFramework):
self.stop_node(0)
write_addrman(peers_dat, len_new=-1)
self.nodes[0].assert_start_raises_init_error(
- expected_msg=init_error("Corrupt CAddrMan serialization: nNew=-1, should be in \\[0, 65536\\]:.*"),
+ expected_msg=init_error("Corrupt AddrMan serialization: nNew=-1, should be in \\[0, 65536\\]:.*"),
match=ErrorMatch.FULL_REGEX,
)
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 4382022a7a..2a507c75c4 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -27,6 +27,9 @@ class NotificationsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
+ # The experimental syscall sandbox feature (-sandbox) is not compatible with -alertnotify,
+ # -blocknotify or -walletnotify (which all invoke execve).
+ self.disable_syscall_sandbox = True
def setup_network(self):
self.wallet = ''.join(chr(i) for i in range(FILE_CHAR_START, FILE_CHAR_END) if chr(i) not in FILE_CHARS_DISALLOWED)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index d759a5aab5..4eaaf46454 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -26,7 +26,7 @@ from test_framework.script_util import (
DUMMY_2_P2WPKH_SCRIPT,
)
from test_framework.wallet import MiniWallet
-
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
MAX_REPLACEMENT_LIMIT = 100
class ReplaceByFeeTest(BitcoinTestFramework):
@@ -44,9 +44,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
]
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
# the pre-mined test framework chain contains coinbase outputs to the
@@ -531,9 +528,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert tx2b_txid in self.nodes[0].getrawmempool()
def test_rpc(self):
- us0 = self.nodes[0].listunspent()[0]
+ us0 = self.wallet.get_utxo()
ins = [us0]
- outs = {self.nodes[0].getnewaddress(): Decimal(1.0000000)}
+ outs = {ADDRESS_BCRT1_UNSPENDABLE: Decimal(1.0000000)}
rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
json0 = self.nodes[0].decoderawtransaction(rawtx0)
@@ -541,14 +538,16 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(json0["vin"][0]["sequence"], 4294967293)
assert_equal(json1["vin"][0]["sequence"], 4294967295)
- rawtx2 = self.nodes[0].createrawtransaction([], outs)
- frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
- frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
+ if self.is_wallet_compiled():
+ self.init_wallet(0)
+ rawtx2 = self.nodes[0].createrawtransaction([], outs)
+ frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
+ frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
- json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
- json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
- assert_equal(json0["vin"][0]["sequence"], 4294967293)
- assert_equal(json1["vin"][0]["sequence"], 4294967294)
+ json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
+ json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
+ assert_equal(json0["vin"][0]["sequence"], 4294967293)
+ assert_equal(json1["vin"][0]["sequence"], 4294967294)
def test_no_inherited_signaling(self):
confirmed_utxo = self.wallet.get_utxo()
diff --git a/test/functional/feature_syscall_sandbox.py b/test/functional/feature_syscall_sandbox.py
new file mode 100755
index 0000000000..caf7f1e7fc
--- /dev/null
+++ b/test/functional/feature_syscall_sandbox.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# 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.
+"""Test bitcoind aborts if a disallowed syscall is used when compiled with the syscall sandbox."""
+
+from test_framework.test_framework import BitcoinTestFramework, SkipTest
+
+
+class SyscallSandboxTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def skip_test_if_missing_module(self):
+ if not self.is_syscall_sandbox_compiled():
+ raise SkipTest("bitcoind has not been built with syscall sandbox enabled.")
+ if self.options.nosandbox:
+ raise SkipTest("--nosandbox passed to test runner.")
+
+ def run_test(self):
+ disallowed_syscall_terminated_bitcoind = False
+ expected_log_entry = 'ERROR: The syscall "getgroups" (syscall number 115) is not allowed by the syscall sandbox'
+ with self.nodes[0].assert_debug_log([expected_log_entry]):
+ self.log.info("Invoking disallowed syscall")
+ try:
+ self.nodes[0].invokedisallowedsyscall()
+ except ConnectionError:
+ disallowed_syscall_terminated_bitcoind = True
+ assert disallowed_syscall_terminated_bitcoind
+ self.nodes = []
+
+
+if __name__ == "__main__":
+ SyscallSandboxTest().main()
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 311d871d49..d74ef5e088 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -28,6 +28,9 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ # The experimental syscall sandbox feature (-sandbox) is not compatible with -alertnotify
+ # (which invokes execve).
+ self.disable_syscall_sandbox = True
def setup_network(self):
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index cda0ae0eeb..3b01506986 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -8,6 +8,7 @@ from decimal import Decimal
from itertools import product
from test_framework.descriptors import descsum_create
+from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_approx,
@@ -19,6 +20,7 @@ from test_framework.util import (
count_bytes,
find_vout_for_address,
)
+from test_framework.wallet_util import bytes_to_wif
def get_unspent(listunspent, amount):
@@ -132,6 +134,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_subtract_fee_with_presets()
self.test_transaction_too_large()
self.test_include_unsafe()
+ self.test_external_inputs()
self.test_22670()
def test_change_position(self):
@@ -983,6 +986,56 @@ class RawTransactionsTest(BitcoinTestFramework):
wallet.sendmany("", outputs)
self.generate(self.nodes[0], 10)
assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx)
+ self.nodes[0].unloadwallet("large")
+
+ def test_external_inputs(self):
+ self.log.info("Test funding with external inputs")
+
+ eckey = ECKey()
+ eckey.generate()
+ privkey = bytes_to_wif(eckey.get_bytes())
+
+ self.nodes[2].createwallet("extfund")
+ wallet = self.nodes[2].get_wallet_rpc("extfund")
+
+ # Make a weird but signable script. sh(pkh()) descriptor accomplishes this
+ desc = descsum_create("sh(pkh({}))".format(privkey))
+ if self.options.descriptors:
+ res = self.nodes[0].importdescriptors([{"desc": desc, "timestamp": "now"}])
+ else:
+ res = self.nodes[0].importmulti([{"desc": desc, "timestamp": "now"}])
+ assert res[0]["success"]
+ addr = self.nodes[0].deriveaddresses(desc)[0]
+ addr_info = self.nodes[0].getaddressinfo(addr)
+
+ self.nodes[0].sendtoaddress(addr, 10)
+ self.nodes[0].sendtoaddress(wallet.getnewaddress(), 10)
+ self.nodes[0].generate(6)
+ ext_utxo = self.nodes[0].listunspent(addresses=[addr])[0]
+
+ # An external input without solving data should result in an error
+ raw_tx = wallet.createrawtransaction([ext_utxo], {self.nodes[0].getnewaddress(): 15})
+ assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx)
+
+ # Error conditions
+ assert_raises_rpc_error(-5, "'not a pubkey' is not hex", wallet.fundrawtransaction, raw_tx, {"solving_data": {"pubkeys":["not a pubkey"]}})
+ assert_raises_rpc_error(-5, "'01234567890a0b0c0d0e0f' is not a valid public key", wallet.fundrawtransaction, raw_tx, {"solving_data": {"pubkeys":["01234567890a0b0c0d0e0f"]}})
+ assert_raises_rpc_error(-5, "'not a script' is not hex", wallet.fundrawtransaction, raw_tx, {"solving_data": {"scripts":["not a script"]}})
+ assert_raises_rpc_error(-8, "Unable to parse descriptor 'not a descriptor'", wallet.fundrawtransaction, raw_tx, {"solving_data": {"descriptors":["not a descriptor"]}})
+
+ # But funding should work when the solving data is provided
+ funded_tx = wallet.fundrawtransaction(raw_tx, {"solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}})
+ signed_tx = wallet.signrawtransactionwithwallet(funded_tx['hex'])
+ assert not signed_tx['complete']
+ signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx['hex'])
+ assert signed_tx['complete']
+
+ funded_tx = wallet.fundrawtransaction(raw_tx, {"solving_data": {"descriptors": [desc]}})
+ signed_tx = wallet.signrawtransactionwithwallet(funded_tx['hex'])
+ assert not signed_tx['complete']
+ signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx['hex'])
+ assert signed_tx['complete']
+ self.nodes[2].unloadwallet("extfund")
def test_include_unsafe(self):
self.log.info("Test fundrawtxn with unsafe inputs")
@@ -1017,6 +1070,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])
signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]
+ self.nodes[0].unloadwallet("unsafe")
def test_22670(self):
# In issue #22670, it was observed that ApproximateBestSubset may
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index 13f33c321f..ac2a7a309b 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -57,7 +57,7 @@ class RpcMiscTest(BitcoinTestFramework):
self.log.info("test logging rpc and help")
# Test logging RPC returns the expected number of logging categories.
- assert_equal(len(node.logging()), 25)
+ assert_equal(len(node.logging()), 26)
# Test toggling a logging category on/off/on with the logging RPC.
assert_equal(node.logging()['qt'], True)
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index f330bbf1c3..6b5b2c6a0f 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -8,6 +8,8 @@
from decimal import Decimal
from itertools import product
+from test_framework.descriptors import descsum_create
+from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_approx,
@@ -16,6 +18,7 @@ from test_framework.util import (
assert_raises_rpc_error,
find_output,
)
+from test_framework.wallet_util import bytes_to_wif
import json
import os
@@ -608,5 +611,42 @@ class PSBTTest(BitcoinTestFramework):
assert_raises_rpc_error(-25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, 'cHNidP8BAJoCAAAAAkvEW8NnDtdNtDpsmze+Ht2LH35IJcKv00jKAlUs21RrAwAAAAD/////S8Rbw2cO1020OmybN74e3Ysffkglwq/TSMoCVSzbVGsBAAAAAP7///8CwLYClQAAAAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XIAAAAAAAEAnQIAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP7///9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/v///wIA+QKVAAAAABl2qRT9zXUVA8Ls5iVqynLHe5/vSe1XyYisQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAAAAAQEfQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
+ # Test that we can fund psbts with external inputs specified
+ eckey = ECKey()
+ eckey.generate()
+ privkey = bytes_to_wif(eckey.get_bytes())
+
+ # Make a weird but signable script. sh(pkh()) descriptor accomplishes this
+ desc = descsum_create("sh(pkh({}))".format(privkey))
+ if self.options.descriptors:
+ res = self.nodes[0].importdescriptors([{"desc": desc, "timestamp": "now"}])
+ else:
+ res = self.nodes[0].importmulti([{"desc": desc, "timestamp": "now"}])
+ assert res[0]["success"]
+ addr = self.nodes[0].deriveaddresses(desc)[0]
+ addr_info = self.nodes[0].getaddressinfo(addr)
+
+ self.nodes[0].sendtoaddress(addr, 10)
+ self.nodes[0].generate(6)
+ ext_utxo = self.nodes[0].listunspent(addresses=[addr])[0]
+
+ # An external input without solving data should result in an error
+ assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[1].walletcreatefundedpsbt, [ext_utxo], {self.nodes[0].getnewaddress(): 10 + ext_utxo['amount']}, 0, {'add_inputs': True})
+
+ # But funding should work when the solving data is provided
+ psbt = self.nodes[1].walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {'add_inputs': True, "solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}})
+ signed = self.nodes[1].walletprocesspsbt(psbt['psbt'])
+ assert not signed['complete']
+ signed = self.nodes[0].walletprocesspsbt(signed['psbt'])
+ assert signed['complete']
+ self.nodes[0].finalizepsbt(signed['psbt'])
+
+ psbt = self.nodes[1].walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {'add_inputs': True, "solving_data":{"descriptors": [desc]}})
+ signed = self.nodes[1].walletprocesspsbt(psbt['psbt'])
+ assert not signed['complete']
+ signed = self.nodes[0].walletprocesspsbt(signed['psbt'])
+ assert signed['complete']
+ self.nodes[0].finalizepsbt(signed['psbt'])
+
if __name__ == '__main__':
PSBTTest().main()
diff --git a/test/functional/rpc_signer.py b/test/functional/rpc_signer.py
index 9e963eba57..5c3722ef8f 100755
--- a/test/functional/rpc_signer.py
+++ b/test/functional/rpc_signer.py
@@ -27,6 +27,9 @@ class RPCSignerTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ # The experimental syscall sandbox feature (-sandbox) is not compatible with -signer (which
+ # invokes execve).
+ self.disable_syscall_sandbox = True
self.extra_args = [
[],
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 3aca93fab3..d87d0cacfd 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -101,6 +101,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.supports_cli = True
self.bind_to_localhost_only = True
self.parse_args()
+ self.disable_syscall_sandbox = self.options.nosandbox
self.default_wallet_name = "default_wallet" if self.options.descriptors else ""
self.wallet_data_filename = "wallet.dat"
# Optional list of wallet names that can be set in set_test_params to
@@ -159,6 +160,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser = argparse.ArgumentParser(usage="%(prog)s [options]")
parser.add_argument("--nocleanup", dest="nocleanup", default=False, action="store_true",
help="Leave bitcoinds and test.* datadir on exit or error")
+ parser.add_argument("--nosandbox", dest="nosandbox", default=False, action="store_true",
+ help="Don't use the syscall sandbox")
parser.add_argument("--noshutdown", dest="noshutdown", default=False, action="store_true",
help="Don't stop bitcoinds after the test execution")
parser.add_argument("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"),
@@ -468,6 +471,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args = [[]] * num_nodes
if versions is None:
versions = [None] * num_nodes
+ if self.is_syscall_sandbox_compiled() and not self.disable_syscall_sandbox:
+ for i in range(len(extra_args)):
+ if versions[i] is None or versions[i] >= 219900:
+ extra_args[i] = extra_args[i] + ["-sandbox=log-and-abort"]
if binary is None:
binary = [get_bin_from_version(v, 'bitcoind', self.options.bitcoind) for v in versions]
if binary_cli is None:
@@ -887,3 +894,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def is_bdb_compiled(self):
"""Checks whether the wallet module was compiled with BDB support."""
return self.config["components"].getboolean("USE_BDB")
+
+ def is_syscall_sandbox_compiled(self):
+ """Checks whether the syscall sandbox was compiled."""
+ return self.config["components"].getboolean("ENABLE_SYSCALL_SANDBOX")
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index f2637df35b..9f5bca6884 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -34,13 +34,14 @@ def assert_approx(v, vexp, vspan=0.00001):
raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
-def assert_fee_amount(fee, tx_size, fee_per_kB):
- """Assert the fee was in range"""
- target_fee = round(tx_size * fee_per_kB / 1000, 8)
+def assert_fee_amount(fee, tx_size, feerate_BTC_kvB):
+ """Assert the fee is in range."""
+ feerate_BTC_vB = feerate_BTC_kvB / 1000
+ target_fee = satoshi_round(tx_size * feerate_BTC_vB)
if fee < target_fee:
raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))
# allow the wallet's estimation to be at most 2 bytes off
- if fee > (tx_size + 2) * fee_per_kB / 1000:
+ if fee > (tx_size + 2) * feerate_BTC_vB:
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index bb84962b75..c5f08b27f2 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -171,6 +171,7 @@ BASE_SCRIPTS = [
'rpc_users.py',
'rpc_whitelist.py',
'feature_proxy.py',
+ 'feature_syscall_sandbox.py',
'rpc_signrawtransaction.py --legacy-wallet',
'rpc_signrawtransaction.py --descriptors',
'rpc_rawtransaction.py --legacy-wallet',
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index aecdaf821f..7b23235945 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -9,6 +9,7 @@ from itertools import product
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create
+from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -16,6 +17,7 @@ from test_framework.util import (
assert_greater_than,
assert_raises_rpc_error,
)
+from test_framework.wallet_util import bytes_to_wif
class WalletSendTest(BitcoinTestFramework):
def set_test_params(self):
@@ -35,7 +37,7 @@ class WalletSendTest(BitcoinTestFramework):
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
- expect_error=None):
+ expect_error=None, solving_data=None):
assert (amount is None) != (data is None)
from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
@@ -94,6 +96,8 @@ class WalletSendTest(BitcoinTestFramework):
options["replaceable"] = replaceable
if subtract_fee_from_outputs is not None:
options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
+ if solving_data is not None:
+ options["solving_data"] = solving_data
if len(options.keys()) == 0:
options = None
@@ -476,6 +480,46 @@ class WalletSendTest(BitcoinTestFramework):
res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
assert res["complete"]
+ self.log.info("External outputs")
+ eckey = ECKey()
+ eckey.generate()
+ privkey = bytes_to_wif(eckey.get_bytes())
+
+ self.nodes[1].createwallet("extsend")
+ ext_wallet = self.nodes[1].get_wallet_rpc("extsend")
+ self.nodes[1].createwallet("extfund")
+ ext_fund = self.nodes[1].get_wallet_rpc("extfund")
+
+ # Make a weird but signable script. sh(pkh()) descriptor accomplishes this
+ desc = descsum_create("sh(pkh({}))".format(privkey))
+ if self.options.descriptors:
+ res = ext_fund.importdescriptors([{"desc": desc, "timestamp": "now"}])
+ else:
+ res = ext_fund.importmulti([{"desc": desc, "timestamp": "now"}])
+ assert res[0]["success"]
+ addr = self.nodes[0].deriveaddresses(desc)[0]
+ addr_info = ext_fund.getaddressinfo(addr)
+
+ self.nodes[0].sendtoaddress(addr, 10)
+ self.nodes[0].sendtoaddress(ext_wallet.getnewaddress(), 10)
+ self.nodes[0].generate(6)
+ ext_utxo = ext_fund.listunspent(addresses=[addr])[0]
+
+ # An external input without solving data should result in an error
+ self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, expect_error=(-4, "Insufficient funds"))
+
+ # But funding should work when the solving data is provided
+ res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]})
+ signed = ext_wallet.walletprocesspsbt(res["psbt"])
+ signed = ext_fund.walletprocesspsbt(res["psbt"])
+ assert signed["complete"]
+ self.nodes[0].finalizepsbt(signed["psbt"])
+
+ res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"descriptors": [desc]})
+ signed = ext_wallet.walletprocesspsbt(res["psbt"])
+ signed = ext_fund.walletprocesspsbt(res["psbt"])
+ assert signed["complete"]
+ self.nodes[0].finalizepsbt(signed["psbt"])
if __name__ == '__main__':
WalletSendTest().main()
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
index 7b77755d64..c6c1cc8784 100755
--- a/test/functional/wallet_signer.py
+++ b/test/functional/wallet_signer.py
@@ -27,6 +27,9 @@ class WalletSignerTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ # The experimental syscall sandbox feature (-sandbox) is not compatible with -signer (which
+ # invokes execve).
+ self.disable_syscall_sandbox = True
self.extra_args = [
[],
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index fcc4883d0b..3015c4f9b9 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -37,23 +37,18 @@ export LC_ALL=C
# See https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings and
# https://stackoverflow.com/a/34878283 for more details.
+# TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent stoul/strtol with locale
+# independent ToIntegral<T>(...).
+# TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent snprintf with strprintf.
KNOWN_VIOLATIONS=(
"src/bitcoin-tx.cpp.*stoul"
"src/dbwrapper.cpp.*stoul"
"src/dbwrapper.cpp:.*vsnprintf"
- "src/node/blockstorage.cpp:.*atoi"
- "src/qt/rpcconsole.cpp:.*atoi"
"src/rest.cpp:.*strtol"
"src/test/dbwrapper_tests.cpp:.*snprintf"
"src/test/fuzz/locale.cpp"
- "src/test/fuzz/parse_numbers.cpp:.*atoi"
"src/test/fuzz/string.cpp"
- "src/torcontrol.cpp:.*atoi"
"src/torcontrol.cpp:.*strtol"
- "src/util/strencodings.cpp:.*atoi"
- "src/util/strencodings.cpp:.*strtoll"
- "src/util/strencodings.h:.*atoi"
- "src/util/system.cpp:.*atoi"
)
REGEXP_IGNORE_EXTERNAL_DEPENDENCIES="^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/)"