aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am5
-rw-r--r--src/Makefile.qt.include8
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include12
-rw-r--r--src/addrman.cpp203
-rw-r--r--src/addrman.h10
-rw-r--r--src/addrman_impl.h21
-rw-r--r--src/banman.h2
-rw-r--r--src/bench/bench_bitcoin.cpp4
-rw-r--r--src/bench/block_assemble.cpp4
-rw-r--r--src/bench/coin_selection.cpp13
-rw-r--r--src/bench/mempool_stress.cpp43
-rw-r--r--src/bench/rollingbloom.cpp2
-rw-r--r--src/bench/wallet_balance.cpp18
-rw-r--r--src/bitcoin-cli.cpp27
-rw-r--r--src/bitcoin-wallet.cpp13
-rw-r--r--src/chainparamsbase.cpp4
-rw-r--r--src/common/bloom.cpp (renamed from src/bloom.cpp)4
-rw-r--r--src/common/bloom.h (renamed from src/bloom.h)6
-rw-r--r--src/consensus/amount.h1
-rw-r--r--src/consensus/validation.h1
-rw-r--r--src/crypto/chacha_poly_aead.h4
-rw-r--r--src/external_signer.cpp12
-rw-r--r--src/fs.cpp1
-rw-r--r--src/init.cpp30
-rw-r--r--src/init/bitcoin-gui.cpp47
-rw-r--r--src/init/bitcoin-qt.cpp42
-rw-r--r--src/init/bitcoin-wallet.cpp12
-rw-r--r--src/key.cpp32
-rw-r--r--src/key.h3
-rw-r--r--src/logging/timer.h23
-rw-r--r--src/merkleblock.h4
-rw-r--r--src/net.cpp82
-rw-r--r--src/net.h8
-rw-r--r--src/net_processing.cpp39
-rw-r--r--src/netaddress.cpp19
-rw-r--r--src/netaddress.h39
-rw-r--r--src/netbase.cpp5
-rw-r--r--src/node/interfaces.cpp2
-rw-r--r--src/node/transaction.cpp6
-rw-r--r--src/policy/feerate.cpp6
-rw-r--r--src/policy/feerate.h1
-rw-r--r--src/policy/rbf.cpp4
-rw-r--r--src/primitives/transaction.h5
-rw-r--r--src/protocol.cpp2
-rw-r--r--src/pubkey.h4
-rw-r--r--src/qt/bitcoin.cpp19
-rw-r--r--src/qt/forms/createwalletdialog.ui3
-rw-r--r--src/qt/splashscreen.cpp4
-rw-r--r--src/qt/test/addressbooktests.cpp8
-rw-r--r--src/qt/test/test_main.cpp4
-rw-r--r--src/qt/test/wallettests.cpp19
-rw-r--r--src/rpc/blockchain.cpp15
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp7
-rw-r--r--src/support/allocators/secure.h17
-rw-r--r--src/support/allocators/zeroafterfree.h16
-rw-r--r--src/test/addrman_tests.cpp24
-rw-r--r--src/test/amount_tests.cpp10
-rw-r--r--src/test/bip32_tests.cpp2
-rw-r--r--src/test/bloom_tests.cpp2
-rw-r--r--src/test/checkqueue_tests.cpp12
-rw-r--r--src/test/crypto_tests.cpp4
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/fuzz/addrman.cpp33
-rw-r--r--src/test/fuzz/bloom_filter.cpp2
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp20
-rw-r--r--src/test/fuzz/rolling_bloom_filter.cpp2
-rw-r--r--src/test/fuzz/tx_pool.cpp4
-rw-r--r--src/test/fuzz/txrequest.cpp4
-rw-r--r--src/test/getarg_tests.cpp4
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/mempool_tests.cpp36
-rw-r--r--src/test/netbase_tests.cpp2
-rw-r--r--src/test/streams_tests.cpp11
-rw-r--r--src/test/transaction_tests.cpp4
-rw-r--r--src/test/txpackage_tests.cpp117
-rw-r--r--src/test/txrequest_tests.cpp6
-rw-r--r--src/test/txvalidation_tests.cpp98
-rw-r--r--src/test/txvalidationcache_tests.cpp3
-rw-r--r--src/test/util/net.h6
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/test/util/wallet.cpp12
-rw-r--r--src/test/validation_block_tests.cpp2
-rw-r--r--src/txmempool.cpp115
-rw-r--r--src/txmempool.h58
-rw-r--r--src/txrequest.cpp2
-rw-r--r--src/util/error.cpp4
-rw-r--r--src/util/system.cpp101
-rw-r--r--src/util/system.h16
-rw-r--r--src/validation.cpp288
-rw-r--r--src/validation.h35
-rw-r--r--src/wallet/bdb.cpp2
-rw-r--r--src/wallet/bdb.h2
-rw-r--r--src/wallet/db.cpp6
-rw-r--r--src/wallet/dump.cpp2
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/rpcdump.cpp23
-rw-r--r--src/wallet/rpcwallet.cpp69
-rw-r--r--src/wallet/rpcwallet.h1
-rw-r--r--src/wallet/salvage.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.cpp4
-rw-r--r--src/wallet/sqlite.cpp2
-rw-r--r--src/wallet/test/coinselector_tests.cpp419
-rw-r--r--src/wallet/test/fuzz/notifications.cpp173
-rw-r--r--src/wallet/test/ismine_tests.cpp40
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp44
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp2
-rw-r--r--src/wallet/test/spend_tests.cpp4
-rw-r--r--src/wallet/test/util.cpp18
-rw-r--r--src/wallet/test/util.h3
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp9
-rw-r--r--src/wallet/test/wallet_test_fixture.h1
-rw-r--r--src/wallet/test/wallet_tests.cpp72
-rw-r--r--src/wallet/transaction.h3
-rw-r--r--src/wallet/wallet.cpp25
-rw-r--r--src/wallet/wallet.h16
-rw-r--r--src/wallet/walletdb.cpp6
-rw-r--r--src/wallet/wallettool.cpp28
119 files changed, 1802 insertions, 1170 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 76755f03d2..85450149cf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -114,7 +114,6 @@ BITCOIN_CORE_H = \
bech32.h \
blockencodings.h \
blockfilter.h \
- bloom.h \
chain.h \
chainparams.h \
chainparamsbase.h \
@@ -122,6 +121,7 @@ BITCOIN_CORE_H = \
checkqueue.h \
clientversion.h \
coins.h \
+ common/bloom.h \
compat.h \
compat/assumptions.h \
compat/byteswap.h \
@@ -531,9 +531,9 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
base58.cpp \
bech32.cpp \
- bloom.cpp \
chainparams.cpp \
coins.cpp \
+ common/bloom.cpp \
compressor.cpp \
core_read.cpp \
core_write.cpp \
@@ -705,6 +705,7 @@ bitcoin_tx_LDADD += $(BOOST_LIBS)
# bitcoin-wallet binary #
bitcoin_wallet_SOURCES = bitcoin-wallet.cpp
+bitcoin_wallet_SOURCES += init/bitcoin-wallet.cpp
bitcoin_wallet_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoin_wallet_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoin_wallet_LDFLAGS = $(bitcoin_bin_ldflags)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 1e3d75a8d8..35d5b0004a 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -338,15 +338,15 @@ bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
qt_bitcoin_qt_CXXFLAGS = $(bitcoin_qt_cxxflags)
-qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
+qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoin-qt.cpp
qt_bitcoin_qt_LDADD = $(bitcoin_qt_ldadd)
qt_bitcoin_qt_LDFLAGS = $(bitcoin_qt_ldflags)
qt_bitcoin_qt_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
bitcoin_gui_CPPFLAGS = $(bitcoin_qt_cppflags)
bitcoin_gui_CXXFLAGS = $(bitcoin_qt_cxxflags)
-bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
-bitcoin_gui_LDADD = $(bitcoin_qt_ldadd)
+bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoin-gui.cpp
+bitcoin_gui_LDADD = $(bitcoin_qt_ldadd) $(LIBBITCOIN_IPC) $(LIBBITCOIN_UTIL) $(LIBMULTIPROCESS_LIBS)
bitcoin_gui_LDFLAGS = $(bitcoin_qt_ldflags)
bitcoin_gui_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
@@ -391,7 +391,7 @@ QT_BASE_TLD = $(shell tar tf $(QT_BASE_PATH) --exclude='*/*')
bitcoin_qt_apk: FORCE
mkdir -p $(APK_LIB_DIR)
- cp $(dir $(CC))../sysroot/usr/lib/$(host_alias)/libc++_shared.so $(APK_LIB_DIR)
+ cp $(dir $(lastword $(CC)))../sysroot/usr/lib/$(host_alias)/libc++_shared.so $(APK_LIB_DIR)
tar xf $(QT_BASE_PATH) -C qt/android/src/ $(QT_BASE_TLD)src/android/jar/src --strip-components=5
tar xf $(QT_BASE_PATH) -C qt/android/src/ $(QT_BASE_TLD)src/android/java/src --strip-components=5
tar xf $(QT_BASE_PATH) -C qt/android/res/ $(QT_BASE_TLD)src/android/java/res --strip-components=5
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 8a5521eeb5..797e1f9a97 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -28,7 +28,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
$(QT_INCLUDES) $(QT_TEST_INCLUDES)
qt_test_test_bitcoin_qt_SOURCES = \
- init/bitcoind.cpp \
+ init/bitcoin-qt.cpp \
qt/test/apptests.cpp \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index d5a8614b31..da4b85665f 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -140,6 +140,7 @@ BITCOIN_TESTS =\
test/transaction_tests.cpp \
test/txindex_tests.cpp \
test/txrequest_tests.cpp \
+ test/txpackage_tests.cpp \
test/txvalidation_tests.cpp \
test/txvalidationcache_tests.cpp \
test/uint256_tests.cpp \
@@ -171,6 +172,10 @@ if USE_BDB
BITCOIN_TESTS += wallet/test/db_tests.cpp
endif
+if USE_SQLITE
+FUZZ_WALLET_SRC = \
+ wallet/test/fuzz/notifications.cpp
+endif # USE_SQLITE
BITCOIN_TEST_SUITE += \
wallet/test/util.cpp \
@@ -179,7 +184,7 @@ BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.h \
wallet/test/init_test_fixture.cpp \
wallet/test/init_test_fixture.h
-endif
+endif # ENABLE_WALLET
test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS)
@@ -200,14 +205,13 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
FUZZ_SUITE_LD_COMMON += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
-FUZZ_SUITE_LDFLAGS_COMMON = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
-
if ENABLE_FUZZ_BINARY
test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fuzz_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON) $(RUNTIME_LDFLAGS)
+test_fuzz_fuzz_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) $(RUNTIME_LDFLAGS)
test_fuzz_fuzz_SOURCES = \
+ $(FUZZ_WALLET_SRC) \
test/fuzz/addition_overflow.cpp \
test/fuzz/addrman.cpp \
test/fuzz/asmap.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 832c3b3cb9..8a0433c40d 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -7,6 +7,8 @@
#include <addrman_impl.h>
#include <hash.h>
+#include <logging.h>
+#include <logging/timer.h>
#include <netaddress.h>
#include <protocol.h>
#include <random.h>
@@ -171,7 +173,7 @@ void AddrManImpl::Serialize(Stream& s_) const
// Increment `lowest_compatible` iff a newly introduced format is incompatible with
// the previous one.
- static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
+ static constexpr uint8_t lowest_compatible = Format::V4_MULTIPORT;
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
s << nKey;
@@ -387,7 +389,7 @@ void AddrManImpl::Unserialize(Stream& s_)
LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
}
- const int check_code{ForceCheckAddrman()};
+ const int check_code{CheckAddrman()};
if (check_code != 0) {
throw std::ios_base::failure(strprintf(
"Corrupt data. Consistency check failed with code %s",
@@ -395,7 +397,7 @@ void AddrManImpl::Unserialize(Stream& s_)
}
}
-AddrInfo* AddrManImpl::Find(const CNetAddr& addr, int* pnId)
+AddrInfo* AddrManImpl::Find(const CService& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -537,73 +539,13 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId)
info.fInTried = true;
}
-void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
-{
- AssertLockHeld(cs);
-
- int nId;
-
- nLastGood = nTime;
-
- AddrInfo* pinfo = Find(addr, &nId);
-
- // if not found, bail out
- if (!pinfo)
- return;
-
- AddrInfo& info = *pinfo;
-
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
- // update info
- info.nLastSuccess = nTime;
- info.nLastTry = nTime;
- info.nAttempts = 0;
- // nTime is not updated here, to avoid leaking information about
- // currently-connected peers.
-
- // if it is already in the tried set, don't do anything else
- if (info.fInTried)
- return;
-
- // if it is not in new, something bad happened
- if (!Assume(info.nRefCount > 0)) {
- return;
- }
-
- // which tried bucket to move the entry to
- int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
- int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
-
- // Will moving this address into tried evict another entry?
- if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
- if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
- m_tried_collisions.insert(nId);
- }
- // Output the entry we'd be colliding with, for debugging purposes
- auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
- LogPrint(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d\n",
- colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "",
- addr.ToString(),
- m_tried_collisions.size());
- } else {
- // move nId to the tried tables
- MakeTried(info, nId);
- LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
- addr.ToString(), addr.GetMappedAS(m_asmap), tried_bucket, tried_bucket_pos);
- }
-}
-
-bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
+bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
AssertLockHeld(cs);
if (!addr.IsRoutable())
return false;
- bool fNew = false;
int nId;
AddrInfo* pinfo = Find(addr, &nId);
@@ -644,13 +586,12 @@ bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTi
pinfo = Create(addr, source, &nId);
pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
nNew++;
- fNew = true;
}
int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
+ bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (vvNew[nUBucket][nUBucketPos] != nId) {
- bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
if (!fInsert) {
AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
@@ -670,14 +611,18 @@ bool AddrManImpl::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTi
}
}
}
- return fNew;
+ return fInsert;
}
-void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
+void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
AssertLockHeld(cs);
- AddrInfo* pinfo = Find(addr);
+ int nId;
+
+ nLastGood = nTime;
+
+ AddrInfo* pinfo = Find(addr, &nId);
// if not found, bail out
if (!pinfo)
@@ -685,10 +630,69 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTi
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
+ // update info
+ info.nLastSuccess = nTime;
+ info.nLastTry = nTime;
+ info.nAttempts = 0;
+ // nTime is not updated here, to avoid leaking information about
+ // currently-connected peers.
+
+ // if it is already in the tried set, don't do anything else
+ if (info.fInTried)
+ return;
+
+ // if it is not in new, something bad happened
+ if (!Assume(info.nRefCount > 0)) {
+ return;
+ }
+
+ // which tried bucket to move the entry to
+ int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
+ int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
+
+ // Will moving this address into tried evict another entry?
+ if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
+ if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
+ m_tried_collisions.insert(nId);
+ }
+ // Output the entry we'd be colliding with, for debugging purposes
+ auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
+ LogPrint(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d\n",
+ colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "",
+ addr.ToString(),
+ m_tried_collisions.size());
+ } else {
+ // move nId to the tried tables
+ MakeTried(info, nId);
+ LogPrint(BCLog::ADDRMAN, "Moved %s mapped to AS%i to tried[%i][%i]\n",
+ addr.ToString(), addr.GetMappedAS(m_asmap), tried_bucket, tried_bucket_pos);
+ }
+}
+
+bool AddrManImpl::Add_(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty)
+{
+ int added{0};
+ for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {
+ added += AddSingle(*it, source, nTimePenalty) ? 1 : 0;
+ }
+ if (added > 0) {
+ LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToString(), nTried, nNew);
+ }
+ return added > 0;
+}
+
+void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
+{
+ AssertLockHeld(cs);
+
+ AddrInfo* pinfo = Find(addr);
+
+ // if not found, bail out
+ if (!pinfo)
return;
+ AddrInfo& info = *pinfo;
+
// update info
info.nLastTry = nTime;
if (fCountFailure && info.nLastCountAttempt < nLastGood) {
@@ -711,40 +715,56 @@ std::pair<CAddress, int64_t> AddrManImpl::Select_(bool newOnly) const
// use a tried node
double fChanceFactor = 1.0;
while (1) {
+ // Pick a tried bucket, and an initial position in that bucket.
int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
- while (vvTried[nKBucket][nKBucketPos] == -1) {
- nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
- nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
+ // Iterate over the positions of that bucket, starting at the initial one,
+ // and looping around.
+ int i;
+ for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ if (vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
}
- int nId = vvTried[nKBucket][nKBucketPos];
+ // If the bucket is entirely empty, start over with a (likely) different one.
+ if (i == ADDRMAN_BUCKET_SIZE) continue;
+ // Find the entry to return.
+ int nId = vvTried[nKBucket][(nKBucketPos + i) % ADDRMAN_BUCKET_SIZE];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
const AddrInfo& info{it_found->second};
+ // With probability GetChance() * fChanceFactor, return the entry.
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
LogPrint(BCLog::ADDRMAN, "Selected %s from tried\n", info.ToString());
return {info, info.nLastTry};
}
+ // Otherwise start over with a (likely) different bucket, and increased chance factor.
fChanceFactor *= 1.2;
}
} else {
// use a new node
double fChanceFactor = 1.0;
while (1) {
+ // Pick a new bucket, and an initial position in that bucket.
int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
- while (vvNew[nUBucket][nUBucketPos] == -1) {
- nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
- nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
+ // Iterate over the positions of that bucket, starting at the initial one,
+ // and looping around.
+ int i;
+ for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
+ if (vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE] != -1) break;
}
- int nId = vvNew[nUBucket][nUBucketPos];
+ // If the bucket is entirely empty, start over with a (likely) different one.
+ if (i == ADDRMAN_BUCKET_SIZE) continue;
+ // Find the entry to return.
+ int nId = vvNew[nUBucket][(nUBucketPos + i) % ADDRMAN_BUCKET_SIZE];
const auto it_found{mapInfo.find(nId)};
assert(it_found != mapInfo.end());
const AddrInfo& info{it_found->second};
+ // With probability GetChance() * fChanceFactor, return the entry.
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30)) {
LogPrint(BCLog::ADDRMAN, "Selected %s from new\n", info.ToString());
return {info, info.nLastTry};
}
+ // Otherwise start over with a (likely) different bucket, and increased chance factor.
fChanceFactor *= 1.2;
}
}
@@ -800,10 +820,6 @@ void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
int64_t nUpdateInterval = 20 * 60;
if (nTime - info.nTime > nUpdateInterval)
@@ -822,10 +838,6 @@ void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
info.nServices = nServices;
}
@@ -927,18 +939,19 @@ void AddrManImpl::Check() const
if (m_consistency_check_ratio == 0) return;
if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
- const int err{ForceCheckAddrman()};
+ const int err{CheckAddrman()};
if (err) {
LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
assert(false);
}
}
-int AddrManImpl::ForceCheckAddrman() const
+int AddrManImpl::CheckAddrman() const
{
AssertLockHeld(cs);
- LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
+ LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(
+ strprintf("new %i, tried %i, total %u", nNew, nTried, vRandom.size()), BCLog::ADDRMAN);
std::unordered_set<int> setTried;
std::unordered_map<int, int> mapNew;
@@ -1018,7 +1031,6 @@ int AddrManImpl::ForceCheckAddrman() const
if (nKey.IsNull())
return -16;
- LogPrint(BCLog::ADDRMAN, "Addrman checks completed successfully\n");
return 0;
}
@@ -1031,15 +1043,10 @@ size_t AddrManImpl::size() const
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;
+ auto ret = Add_(vAddr, source, nTimePenalty);
Check();
- if (nAdd) {
- LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew);
- }
- return nAdd > 0;
+ return ret;
}
void AddrManImpl::Good(const CService& addr, int64_t nTime)
diff --git a/src/addrman.h b/src/addrman.h
index 174ab4f811..a9f697f66f 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -69,7 +69,15 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const;
- //! Add addresses to addrman's new table.
+ /**
+ * Attempt to add one or more addresses to addrman's new table.
+ *
+ * @param[in] vAddr Address records to attempt to add.
+ * @param[in] source The address of the node that sent us these addr records.
+ * @param[in] nTimePenalty A "time penalty" to apply to the address record's nTime. If a peer
+ * sends us an address record with nTime=n, then we'll add it to our
+ * addrman with nTime=(n - nTimePenalty).
+ * @return true if at least one address is successfully added. */
bool Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, int64_t nTimePenalty = 0);
//! Mark an entry as accessible, possibly moving it from "new" to "tried".
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
index 1dc7f25f9c..10a65871c1 100644
--- a/src/addrman_impl.h
+++ b/src/addrman_impl.h
@@ -6,6 +6,7 @@
#define BITCOIN_ADDRMAN_IMPL_H
#include <logging.h>
+#include <logging/timer.h>
#include <netaddress.h>
#include <protocol.h>
#include <serialize.h>
@@ -157,6 +158,7 @@ private:
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
+ V4_MULTIPORT = 4, //!< adds support for multiple ports per IP
};
//! The maximum format this software knows it can unserialize. Also, we always serialize
@@ -164,7 +166,7 @@ private:
//! 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;
+ static constexpr Format FILE_FORMAT = Format::V4_MULTIPORT;
//! 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
@@ -179,8 +181,8 @@ private:
//! 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);
+ //! find an nId based on its network address and port.
+ std::unordered_map<CService, int, CServiceHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
//! This is mutable because it is unobservable outside the class, so any
@@ -225,7 +227,7 @@ private:
const std::vector<bool> m_asmap;
//! Find an entry.
- AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ AddrInfo* Find(const CService& 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);
@@ -242,9 +244,13 @@ private:
//! Move an entry from the "new" table(s) to the "tried" table
void MakeTried(AddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ /** Attempt to add a single address to addrman's new table.
+ * @see AddrMan::Add() for parameters. */
+ bool AddSingle(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) 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);
+ bool Add_(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
void Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -260,12 +266,13 @@ private:
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.
+ //! 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);
+ int CheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs);
};
#endif // BITCOIN_ADDRMAN_IMPL_H
diff --git a/src/banman.h b/src/banman.h
index 8a03a9e3fc..f495dab49d 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -6,7 +6,7 @@
#define BITCOIN_BANMAN_H
#include <addrdb.h>
-#include <bloom.h>
+#include <common/bloom.h>
#include <fs.h>
#include <net_types.h> // For banmap_t
#include <sync.h>
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 0b43ea1fd5..83609d2787 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -24,8 +24,8 @@ static void SetupBenchArgs(ArgsManager& argsman)
argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
- argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
+ argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index b4b33d115f..0577ab80e3 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -34,10 +34,10 @@ static void AssembleBlock(benchmark::Bench& bench)
txs.at(b) = MakeTransactionRef(tx);
}
{
- LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
+ LOCK(::cs_main);
for (const auto& txr : txs) {
- const MempoolAcceptResult res = ::AcceptToMemoryPool(test_setup->m_node.chainman->ActiveChainstate(), *test_setup->m_node.mempool, txr, false /* bypass_limits */);
+ const MempoolAcceptResult res = test_setup->m_node.chainman->ProcessTransaction(txr);
assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 934b574f8b..f6a8c56743 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -32,8 +32,7 @@ static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
- wallet.SetupLegacyScriptPubKeyMan();
+ CWallet wallet(chain.get(), "", gArgs, CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -65,10 +64,6 @@ static void CoinSelection(benchmark::Bench& bench)
}
typedef std::set<CInputCoin> CoinSet;
-static NodeContext testNode;
-static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
-std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>& set)
@@ -76,10 +71,9 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx)));
+ CInputCoin coin(MakeTransactionRef(tx), nInput);
set.emplace_back();
- set.back().Insert(COutput(testWallet, *wtx, nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false);
- wtxn.emplace_back(std::move(wtx));
+ set.back().Insert(coin, 0, true, 0, 0, false);
}
// Copied from src/wallet/test/coinselector_tests.cpp
static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
@@ -97,7 +91,6 @@ static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
static void BnBExhaustion(benchmark::Bench& bench)
{
// Setup
- testWallet.SetupLegacyScriptPubKeyMan();
std::vector<OutputGroup> utxo_pool;
CoinSet selection;
CAmount value_ret = 0;
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index f28768efc8..a0a82ea359 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -6,6 +6,7 @@
#include <policy/policy.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
+#include <validation.h>
#include <vector>
@@ -26,14 +27,8 @@ struct Available {
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
};
-static void ComplexMemPool(benchmark::Bench& bench)
+static std::vector<CTransactionRef> CreateOrderedCoins(FastRandomContext& det_rand, int childTxs, int min_ancestors)
{
- int childTxs = 800;
- if (bench.complexityN() > 1) {
- childTxs = static_cast<int>(bench.complexityN());
- }
-
- FastRandomContext det_rand{true};
std::vector<Available> available_coins;
std::vector<CTransactionRef> ordered_coins;
// Create some base transactions
@@ -58,8 +53,10 @@ static void ComplexMemPool(benchmark::Bench& bench)
size_t idx = det_rand.randrange(available_coins.size());
Available coin = available_coins[idx];
uint256 hash = coin.ref->GetHash();
- // biased towards taking just one ancestor, but maybe more
- size_t n_to_take = det_rand.randrange(2) == 0 ? 1 : 1+det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
+ // biased towards taking min_ancestors parents, but maybe more
+ size_t n_to_take = det_rand.randrange(2) == 0 ?
+ min_ancestors :
+ min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
for (size_t i = 0; i < n_to_take; ++i) {
tx.vin.emplace_back();
tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);
@@ -79,6 +76,17 @@ static void ComplexMemPool(benchmark::Bench& bench)
ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
}
+ return ordered_coins;
+}
+
+static void ComplexMemPool(benchmark::Bench& bench)
+{
+ FastRandomContext det_rand{true};
+ int childTxs = 800;
+ if (bench.complexityN() > 1) {
+ childTxs = static_cast<int>(bench.complexityN());
+ }
+ std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
@@ -91,4 +99,21 @@ static void ComplexMemPool(benchmark::Bench& bench)
});
}
+static void MempoolCheck(benchmark::Bench& bench)
+{
+ FastRandomContext det_rand{true};
+ const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
+ const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5);
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
+ CTxMemPool pool;
+ LOCK2(cs_main, pool.cs);
+ const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
+ for (auto& tx : ordered_coins) AddTx(tx, pool);
+
+ bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
+ pool.check(coins_tip, /* spendheight */ 2);
+ });
+}
+
BENCHMARK(ComplexMemPool);
+BENCHMARK(MempoolCheck);
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 28167767db..30bc1d5fdf 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -4,7 +4,7 @@
#include <bench/bench.h>
-#include <bloom.h>
+#include <common/bloom.h>
static void RollingBloom(benchmark::Bench& bench)
{
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index a205d8b6e7..8e3ca59496 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -14,21 +14,22 @@
#include <optional>
-static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_watchonly, const bool add_mine)
+static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_mine)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
- CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()};
+ CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
{
- wallet.SetupLegacyScriptPubKeyMan();
+ LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet.SetupDescriptorScriptPubKeyMans();
if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
}
auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});
const std::optional<std::string> address_mine{add_mine ? std::optional<std::string>{getnewaddress(wallet)} : std::nullopt};
- if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY);
for (int i = 0; i < 100; ++i) {
generatetoaddress(test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY));
@@ -42,14 +43,13 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
if (set_dirty) wallet.MarkDirty();
bal = GetBalance(wallet);
if (add_mine) assert(bal.m_mine_trusted > 0);
- if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
});
}
-static void WalletBalanceDirty(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); }
-static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); }
-static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); }
-static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); }
+static void WalletBalanceDirty(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ true, /* add_mine */ true); }
+static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_mine */ true); }
+static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_mine */ true); }
+static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_mine */ false); }
BENCHMARK(WalletBalanceDirty);
BENCHMARK(WalletBalanceClean);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 43e986a765..338d32ef6b 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -74,7 +74,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions(argsman);
- argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS);
+ argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -83,7 +83,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
+ argsman.AddArg("-rpcwaittimeout=<n>", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -551,15 +551,26 @@ public:
}
// Report peer connection totals by type.
- result += " ipv4 ipv6 onion";
- const bool any_i2p_peers = m_counts.at(2).at(3); // false if total i2p peers count is 0, otherwise true
- if (any_i2p_peers) result += " i2p";
+ result += " ";
+ std::vector<int8_t> reachable_networks;
+ for (const UniValue& network : networkinfo["networks"].getValues()) {
+ if (network["reachable"].get_bool()) {
+ const std::string& network_name{network["name"].get_str()};
+ const int8_t network_id{NetworkStringToId(network_name)};
+ if (network_id == UNKNOWN_NETWORK) continue;
+ result += strprintf("%8s", network_name); // column header
+ reachable_networks.push_back(network_id);
+ }
+ };
result += " total block";
if (m_manual_peers_count) result += " manual";
+
const std::array rows{"in", "out", "total"};
- for (uint8_t i = 0; i < 3; ++i) {
- result += strprintf("\n%-5s %5i %5i %5i", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2)); // ipv4/ipv6/onion peers counts
- if (any_i2p_peers) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p peers count
+ for (size_t i = 0; i < rows.size(); ++i) {
+ result += strprintf("\n%-5s", rows[i]); // row header
+ for (int8_t n : reachable_networks) {
+ result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
+ }
result += strprintf(" %5i", m_counts.at(i).at(m_networks.size())); // total peers count
if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
result += strprintf(" %5i", m_block_relay_peers_count);
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 765954c92e..afa5b99549 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -8,6 +8,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
+#include <interfaces/init.h>
#include <logging.h>
#include <util/system.h>
#include <util/translation.h>
@@ -27,9 +28,10 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
- argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_STRING, OptionsCategory::OPTIONS);
+ argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
argsman.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-legacy", "Create legacy wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
@@ -83,6 +85,13 @@ int main(int argc, char* argv[])
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
+
+ int exit_status;
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeWalletInit(argc, argv, exit_status);
+ if (!init) {
+ return exit_status;
+ }
+
SetupEnvironment();
RandomInit();
try {
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index dc484f5c03..f24863f419 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -24,8 +24,8 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
diff --git a/src/bloom.cpp b/src/common/bloom.cpp
index 15e06389de..26b70b4d14 100644
--- a/src/bloom.cpp
+++ b/src/common/bloom.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 <bloom.h>
+#include <common/bloom.h>
#include <hash.h>
#include <primitives/transaction.h>
@@ -82,7 +82,7 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- return contains(MakeUCharSpan(stream));
+ return contains(stream);
}
bool CBloomFilter::IsWithinSizeConstraints() const
diff --git a/src/bloom.h b/src/common/bloom.h
index 422646d8b9..25c16fbfe2 100644
--- a/src/bloom.h
+++ b/src/common/bloom.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_BLOOM_H
-#define BITCOIN_BLOOM_H
+#ifndef BITCOIN_COMMON_BLOOM_H
+#define BITCOIN_COMMON_BLOOM_H
#include <serialize.h>
#include <span.h>
@@ -124,4 +124,4 @@ private:
int nHashFuncs;
};
-#endif // BITCOIN_BLOOM_H
+#endif // BITCOIN_COMMON_BLOOM_H
diff --git a/src/consensus/amount.h b/src/consensus/amount.h
index 8b41a2277d..96566ea13f 100644
--- a/src/consensus/amount.h
+++ b/src/consensus/amount.h
@@ -11,6 +11,7 @@
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount;
+/** The amount of satoshis in one BTC. */
static constexpr CAmount COIN = 100000000;
/** No amount larger than this (in satoshi) is valid.
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index c4d305434a..05416d0aca 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -53,6 +53,7 @@ enum class TxValidationResult {
*/
TX_CONFLICT,
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/RBF/etc limits
+ TX_NO_MEMPOOL, //!< this node does not have a mempool so can't validate the transaction
};
/** A "reason" why a block was invalid, suitable for determining whether the
diff --git a/src/crypto/chacha_poly_aead.h b/src/crypto/chacha_poly_aead.h
index 0afe8fcc14..6a7998335d 100644
--- a/src/crypto/chacha_poly_aead.h
+++ b/src/crypto/chacha_poly_aead.h
@@ -117,8 +117,8 @@ static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/
class ChaCha20Poly1305AEAD
{
private:
- ChaCha20 m_chacha_main; // payload and poly1305 key-derivation cipher instance
- ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length)
+ ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length) and poly1305 key-derivation cipher instance
+ ChaCha20 m_chacha_main; // payload
unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]; // aad keystream cache
uint64_t m_cached_aad_seqnr; // aad keystream cache hint
diff --git a/src/external_signer.cpp b/src/external_signer.cpp
index d6388b759a..75070899c6 100644
--- a/src/external_signer.cpp
+++ b/src/external_signer.cpp
@@ -9,6 +9,7 @@
#include <util/system.h>
#include <external_signer.h>
+#include <algorithm>
#include <stdexcept>
#include <string>
#include <vector>
@@ -75,15 +76,14 @@ bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::str
ssTx << psbtx;
// Check if signer fingerprint matches any input master key fingerprint
- bool match = false;
- for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
- const PSBTInput& input = psbtx.inputs[i];
+ auto matches_signer_fingerprint = [&](const PSBTInput& input) {
for (const auto& entry : input.hd_keypaths) {
- if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) match = true;
+ if (m_fingerprint == strprintf("%08x", ReadBE32(entry.second.fingerprint))) return true;
}
- }
+ return false;
+ };
- if (!match) {
+ if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) {
error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str());
return false;
}
diff --git a/src/fs.cpp b/src/fs.cpp
index 8cae7f32c6..7a99444eef 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -16,6 +16,7 @@
#define NOMINMAX
#endif
#include <codecvt>
+#include <limits>
#include <windows.h>
#endif
diff --git a/src/init.cpp b/src/init.cpp
index 164b7bb55d..b0335183d6 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -422,13 +422,14 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bind=<addr>[:<port>][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-cjdnsreachable", "If set then this host is configured for CJDNS (connecting to fc00::/8 addresses would lead us to the CJDNS network) (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
- argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listenonion", strprintf("Automatically create Tor onion service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u). This limit does not apply to connections manually added via -addnode or the addnode RPC, which have a separate limit of %u.", DEFAULT_MAX_PEER_CONNECTIONS, MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -438,7 +439,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with non-onion networks and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -447,7 +448,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -462,7 +463,7 @@ void SetupServerArgs(ArgsManager& argsman)
hidden_args.emplace_back("-upnp");
#endif
#ifdef USE_NATPMP
- argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+ argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
hidden_args.emplace_back("-natpmp");
#endif // USE_NATPMP
@@ -514,7 +515,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -551,13 +552,13 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcwhitelist=<whitelist>", "Set a whitelist to filter incoming RPC calls for a specific user. The field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc 2>,...,<rpc n>. If multiple whitelists are set for a given user, they are set-intersected. See -rpcwhitelistdefault documentation for information on default whitelist behavior.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
- argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
+ argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
#if HAVE_DECL_FORK
- argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
- argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-daemonwait", strprintf("Wait for initialization to be finished before exiting. This implies -daemon (default: %d)", DEFAULT_DAEMONWAIT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
hidden_args.emplace_back("-daemonwait");
@@ -1294,6 +1295,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
+ if (!args.IsArgSet("-cjdnsreachable")) {
+ SetReachable(NET_CJDNS, false);
+ }
+ // Now IsReachable(NET_CJDNS) is true if:
+ // 1. -cjdnsreachable is given and
+ // 2.1. -onlynet is not given or
+ // 2.2. -onlynet=cjdns is given
+
// Check for host lookup allowed before parsing any network related parameters
fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
@@ -1315,6 +1324,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_ONION, addrProxy);
+ SetProxy(NET_CJDNS, addrProxy);
SetNameProxy(addrProxy);
SetReachable(NET_ONION, true); // by default, -proxy sets onion as reachable, unless -noonion later
}
diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp
new file mode 100644
index 0000000000..c549ed3cc0
--- /dev/null
+++ b/src/init/bitcoin-gui.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+#include <node/context.h>
+#include <util/system.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+const char* EXE_NAME = "bitcoin-gui";
+
+class BitcoinGuiInit : public interfaces::Init
+{
+public:
+ BitcoinGuiInit(const char* arg0) : m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
+ {
+ m_node.args = &gArgs;
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ interfaces::Ipc* ipc() override { return m_ipc.get(); }
+ NodeContext m_node;
+ std::unique_ptr<interfaces::Ipc> m_ipc;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[])
+{
+ return std::make_unique<init::BitcoinGuiInit>(argc > 0 ? argv[0] : "");
+}
+} // namespace interfaces
diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp
new file mode 100644
index 0000000000..d71177e885
--- /dev/null
+++ b/src/init/bitcoin-qt.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+#include <node/context.h>
+#include <util/system.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+class BitcoinQtInit : public interfaces::Init
+{
+public:
+ BitcoinQtInit()
+ {
+ m_node.args = &gArgs;
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ NodeContext m_node;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[])
+{
+ return std::make_unique<init::BitcoinQtInit>();
+}
+} // namespace interfaces
diff --git a/src/init/bitcoin-wallet.cpp b/src/init/bitcoin-wallet.cpp
new file mode 100644
index 0000000000..e9dcde72fe
--- /dev/null
+++ b/src/init/bitcoin-wallet.cpp
@@ -0,0 +1,12 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interfaces/init.h>
+
+namespace interfaces {
+std::unique_ptr<Init> MakeWalletInit(int argc, char* argv[], int& exit_status)
+{
+ return std::make_unique<Init>();
+}
+} // namespace interfaces
diff --git a/src/key.cpp b/src/key.cpp
index 39155e4311..7688254515 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -229,6 +229,12 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool gr
assert(ret);
secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig);
vchSig.resize(nSigLen);
+ // Additional verification step to prevent using a potentially corrupted signature
+ secp256k1_pubkey pk;
+ ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pk, begin());
+ assert(ret);
+ ret = secp256k1_ecdsa_verify(GetVerifyContext(), &sig, hash.begin(), &pk);
+ assert(ret);
return true;
}
@@ -251,13 +257,21 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return false;
vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE);
int rec = -1;
- secp256k1_ecdsa_recoverable_signature sig;
- int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, nullptr);
+ secp256k1_ecdsa_recoverable_signature rsig;
+ int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &rsig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, nullptr);
assert(ret);
- ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &sig);
+ ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &rsig);
assert(ret);
assert(rec != -1);
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
+ // Additional verification step to prevent using a potentially corrupted signature
+ secp256k1_pubkey epk, rpk;
+ ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &epk, begin());
+ assert(ret);
+ ret = secp256k1_ecdsa_recover(GetVerifyContext(), &rpk, &rsig, hash.begin());
+ assert(ret);
+ ret = secp256k1_ec_pubkey_cmp(GetVerifyContext(), &epk, &rpk);
+ assert(ret == 0);
return true;
}
@@ -275,6 +289,13 @@ bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint2
if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false;
}
bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, aux ? (unsigned char*)aux->data() : nullptr);
+ if (ret) {
+ // Additional verification step to prevent using a potentially corrupted signature
+ secp256k1_xonly_pubkey pubkey_verify;
+ ret = secp256k1_keypair_xonly_pub(GetVerifyContext(), &pubkey_verify, nullptr, &keypair);
+ ret &= secp256k1_schnorrsig_verify(GetVerifyContext(), sig.data(), hash.begin(), 32, &pubkey_verify);
+ }
+ if (!ret) memory_cleanse(sig.data(), sig.size());
memory_cleanse(&keypair, sizeof(keypair));
return ret;
}
@@ -319,10 +340,11 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}
-void CExtKey::SetSeed(const unsigned char *seed, unsigned int nSeedLen) {
+void CExtKey::SetSeed(Span<const uint8_t> seed)
+{
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
- CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
+ CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(seed.data(), seed.size()).Finalize(vout.data());
key.Set(vout.data(), vout.data() + 32, true);
memcpy(chaincode.begin(), vout.data() + 32, 32);
nDepth = 0;
diff --git a/src/key.h b/src/key.h
index 9b94baa026..af8d2e72d8 100644
--- a/src/key.h
+++ b/src/key.h
@@ -85,6 +85,7 @@ public:
//! Simple read-only vector-like interface.
unsigned int size() const { return (fValid ? keydata.size() : 0); }
+ const unsigned char* data() const { return keydata.data(); }
const unsigned char* begin() const { return keydata.data(); }
const unsigned char* end() const { return keydata.data() + size(); }
@@ -177,7 +178,7 @@ struct CExtKey {
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtKey& out, unsigned int nChild) const;
CExtPubKey Neuter() const;
- void SetSeed(const unsigned char* seed, unsigned int nSeedLen);
+ void SetSeed(Span<const uint8_t> seed);
};
/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */
diff --git a/src/logging/timer.h b/src/logging/timer.h
index 79627b1fe3..b77a0e17c7 100644
--- a/src/logging/timer.h
+++ b/src/logging/timer.h
@@ -27,10 +27,12 @@ public:
Timer(
std::string prefix,
std::string end_msg,
- BCLog::LogFlags log_category = BCLog::LogFlags::ALL) :
+ BCLog::LogFlags log_category = BCLog::LogFlags::ALL,
+ bool msg_on_completion = true) :
m_prefix(std::move(prefix)),
m_title(std::move(end_msg)),
- m_log_category(log_category)
+ m_log_category(log_category),
+ m_message_on_completion(msg_on_completion)
{
this->Log(strprintf("%s started", m_title));
m_start_t = GetTime<std::chrono::microseconds>();
@@ -38,7 +40,11 @@ public:
~Timer()
{
- this->Log(strprintf("%s completed", m_title));
+ if (m_message_on_completion) {
+ this->Log(strprintf("%s completed", m_title));
+ } else {
+ this->Log("completed");
+ }
}
void Log(const std::string& msg)
@@ -74,14 +80,17 @@ private:
std::chrono::microseconds m_start_t{};
//! Log prefix; usually the name of the function this was created in.
- const std::string m_prefix{};
+ const std::string m_prefix;
//! A descriptive message of what is being timed.
- const std::string m_title{};
+ const std::string m_title;
//! Forwarded on to LogPrint if specified - has the effect of only
//! outputting the timing log when a particular debug= category is specified.
- const BCLog::LogFlags m_log_category{};
+ const BCLog::LogFlags m_log_category;
+
+ //! Whether to output the message again on completion.
+ const bool m_message_on_completion;
};
} // namespace BCLog
@@ -91,6 +100,8 @@ private:
BCLog::Timer<std::chrono::microseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category)
#define LOG_TIME_MILLIS_WITH_CATEGORY(end_msg, log_category) \
BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category)
+#define LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(end_msg, log_category) \
+ BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, log_category, /* msg_on_completion=*/false)
#define LOG_TIME_SECONDS(end_msg) \
BCLog::Timer<std::chrono::seconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg)
diff --git a/src/merkleblock.h b/src/merkleblock.h
index 0e4ed72130..70749b6378 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -6,10 +6,10 @@
#ifndef BITCOIN_MERKLEBLOCK_H
#define BITCOIN_MERKLEBLOCK_H
+#include <common/bloom.h>
+#include <primitives/block.h>
#include <serialize.h>
#include <uint256.h>
-#include <primitives/block.h>
-#include <bloom.h>
#include <vector>
diff --git a/src/net.cpp b/src/net.cpp
index 7271ff22b2..82e55d4189 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -230,9 +230,27 @@ std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode)
return std::nullopt;
}
+/**
+ * If an IPv6 address belongs to the address range used by the CJDNS network and
+ * the CJDNS network is reachable (-cjdnsreachable config is set), then change
+ * the type from NET_IPV6 to NET_CJDNS.
+ * @param[in] service Address to potentially convert.
+ * @return a copy of `service` either unmodified or changed to CJDNS.
+ */
+CService MaybeFlipIPv6toCJDNS(const CService& service)
+{
+ CService ret{service};
+ if (ret.m_net == NET_IPV6 && ret.m_addr[0] == 0xfc && IsReachable(NET_CJDNS)) {
+ ret.m_net = NET_CJDNS;
+ }
+ return ret;
+}
+
// learn a new local address
-bool AddLocal(const CService& addr, int nScore)
+bool AddLocal(const CService& addr_, int nScore)
{
+ CService addr{MaybeFlipIPv6toCJDNS(addr_)};
+
if (!addr.IsRoutable())
return false;
@@ -409,7 +427,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (pszDest) {
std::vector<CService> resolved;
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
- addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE);
+ const CService rnd{resolved[GetRand(resolved.size())]};
+ addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), NODE_NONE};
if (!addrConnect.IsValid()) {
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToString(), pszDest);
return nullptr;
@@ -626,25 +645,26 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
- uint32_t out_err_raw_size{0};
- std::optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
- if (!result) {
+ bool reject_message{false};
+ CNetMessage msg = m_deserializer->GetMessage(time, reject_message);
+ if (reject_message) {
// Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
- mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err_raw_size;
+ mapRecvBytesPerMsgCmd.at(NET_MESSAGE_COMMAND_OTHER) += msg.m_raw_message_size;
continue;
}
- //store received bytes per message command
- //to prevent a memory DOS, only allow valid commands
- mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(result->m_command);
- if (i == mapRecvBytesPerMsgCmd.end())
+ // Store received bytes per message command
+ // to prevent a memory DOS, only allow valid commands
+ auto i = mapRecvBytesPerMsgCmd.find(msg.m_command);
+ if (i == mapRecvBytesPerMsgCmd.end()) {
i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
+ }
assert(i != mapRecvBytesPerMsgCmd.end());
- i->second += result->m_raw_message_size;
+ i->second += msg.m_raw_message_size;
// push the message to the process queue,
- vRecvMsg.push_back(std::move(*result));
+ vRecvMsg.push_back(std::move(msg));
complete = true;
}
@@ -718,16 +738,18 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
+CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, bool& reject_message)
{
+ // Initialize out parameter
+ reject_message = false;
// decompose a single CNetMessage from the TransportDeserializer
- std::optional<CNetMessage> msg(std::move(vRecv));
+ CNetMessage msg(std::move(vRecv));
// store command string, time, and sizes
- msg->m_command = hdr.GetCommand();
- msg->m_time = time;
- msg->m_message_size = hdr.nMessageSize;
- msg->m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
+ msg.m_command = hdr.GetCommand();
+ msg.m_time = time;
+ msg.m_message_size = hdr.nMessageSize;
+ msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
uint256 hash = GetMessageHash();
@@ -737,17 +759,15 @@ std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono
// Check checksum and header command string
if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n",
- SanitizeString(msg->m_command), msg->m_message_size,
+ SanitizeString(msg.m_command), msg.m_message_size,
HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
HexStr(hdr.pchChecksum),
m_node_id);
- out_err_raw_size = msg->m_raw_message_size;
- msg = std::nullopt;
+ reject_message = true;
} else if (!hdr.IsCommandValid()) {
LogPrint(BCLog::NET, "Header error: Invalid message type (%s, %u bytes), peer=%d\n",
- SanitizeString(hdr.GetCommand()), msg->m_message_size, m_node_id);
- out_err_raw_size = msg->m_raw_message_size;
- msg.reset();
+ SanitizeString(hdr.GetCommand()), msg.m_message_size, m_node_id);
+ reject_message = true;
}
// Always reset the network deserializer (prepare for the next message)
@@ -1091,9 +1111,11 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
LogPrintf("Warning: Unknown socket family\n");
+ } else {
+ addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE};
}
- const CAddress addr_bind = GetBindAddress(hSocket);
+ const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(hSocket)), NODE_NONE};
NetPermissionFlags permissionFlags = NetPermissionFlags::None;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
@@ -1295,9 +1317,8 @@ void CConnman::NotifyNumConnectionsChanged()
}
}
-bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now_in) const
+bool CConnman::ShouldRunInactivityChecks(const CNode& node, int64_t now) const
{
- const int64_t now = now_in ? now_in.value() : GetTimeSeconds();
return node.nTimeConnected + m_peer_connect_timeout < now;
}
@@ -2460,7 +2481,10 @@ NodeId CConnman::GetNewNodeId()
}
-bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
+bool CConnman::Bind(const CService& addr_, unsigned int flags, NetPermissionFlags permissions)
+{
+ const CService addr{MaybeFlipIPv6toCJDNS(addr_)};
+
if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) {
return false;
}
@@ -2981,7 +3005,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
- m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), id, SER_NETWORK, INIT_PROTO_VERSION));
m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
}
diff --git a/src/net.h b/src/net.h
index e2071414b4..878f10cd42 100644
--- a/src/net.h
+++ b/src/net.h
@@ -7,8 +7,8 @@
#define BITCOIN_NET_H
#include <addrman.h>
-#include <bloom.h>
#include <chainparams.h>
+#include <common/bloom.h>
#include <compat.h>
#include <consensus/amount.h>
#include <crypto/siphash.h>
@@ -308,7 +308,7 @@ public:
/** read and deserialize data, advances msg_bytes data pointer */
virtual int Read(Span<const uint8_t>& msg_bytes) = 0;
// decomposes a message from the context
- virtual std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
+ virtual CNetMessage GetMessage(std::chrono::microseconds time, bool& reject_message) = 0;
virtual ~TransportDeserializer() {}
};
@@ -372,7 +372,7 @@ public:
}
return ret;
}
- std::optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
+ CNetMessage GetMessage(std::chrono::microseconds time, bool& reject_message) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -942,7 +942,7 @@ public:
std::chrono::microseconds PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval);
/** Return true if we should disconnect the peer for failing an inactivity check. */
- bool ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now=std::nullopt) const;
+ bool ShouldRunInactivityChecks(const CNode& node, int64_t secs_now) const;
private:
struct ListenSocket {
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 66b99aa2bb..2185ccc700 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -461,9 +461,9 @@ private:
bool AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
- * Filter for transactions that were recently rejected by
- * AcceptToMemoryPool. These are not rerequested until the chain tip
- * changes, at which point the entire filter is reset.
+ * Filter for transactions that were recently rejected by the mempool.
+ * These are not rerequested until the chain tip changes, at which point
+ * the entire filter is reset.
*
* Without this filter we'd be re-requesting txs from each of our peers,
* increasing bandwidth consumption considerably. For instance, with 100
@@ -1409,6 +1409,7 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat
case TxValidationResult::TX_WITNESS_STRIPPED:
case TxValidationResult::TX_CONFLICT:
case TxValidationResult::TX_MEMPOOL_POLICY:
+ case TxValidationResult::TX_NO_MEMPOOL:
break;
}
if (message != "") {
@@ -2242,7 +2243,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
if (porphanTx == nullptr) continue;
- const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, porphanTx, false /* bypass_limits */);
+ const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
@@ -2299,7 +2300,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
break;
}
}
- m_mempool.check(m_chainman.ActiveChainstate());
}
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer,
@@ -2960,16 +2960,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
best_block = &inv.hash;
}
} else if (inv.IsGenTxMsg()) {
+ if (reject_tx_invs) {
+ LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
+ pfrom.fDisconnect = true;
+ return;
+ }
const GenTxid gtxid = ToGenTxid(inv);
const bool fAlreadyHave = AlreadyHaveTx(gtxid);
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
pfrom.AddKnownTx(inv.hash);
- if (reject_tx_invs) {
- LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
- pfrom.fDisconnect = true;
- return;
- } else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
+ if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
AddTxAnnouncement(pfrom, gtxid, current_time);
}
} else {
@@ -3242,12 +3243,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid))) {
+ if (AlreadyHaveTx(GenTxid::Wtxid(wtxid))) {
if (pfrom.HasPermission(NetPermissionFlags::ForceRelay)) {
// Always relay transactions received from peers with forcerelay
// permission, even if they were already in the mempool, allowing
// the node to function as a gateway for nodes hidden behind it.
- if (!m_mempool.exists(tx.GetHash())) {
+ if (!m_mempool.exists(GenTxid::Txid(tx.GetHash()))) {
LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
} else {
LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
@@ -3257,11 +3258,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- const MempoolAcceptResult result = AcceptToMemoryPool(m_chainman.ActiveChainstate(), m_mempool, ptx, false /* bypass_limits */);
+ const MempoolAcceptResult result = m_chainman.ProcessTransaction(ptx);
const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
- m_mempool.check(m_chainman.ActiveChainstate());
// As this version of the transaction was acceptable, we can forget about any
// requests for it.
m_txrequest.ForgetTxHash(tx.GetHash());
@@ -3312,7 +3312,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// wtxidrelay peers.
// Eventually we should replace this with an improved
// protocol for getting all unconfirmed parents.
- const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
+ const auto gtxid{GenTxid::Txid(parent_txid)};
pfrom.AddKnownTx(parent_txid);
if (!AlreadyHaveTx(gtxid)) AddTxAnnouncement(pfrom, gtxid, current_time);
}
@@ -3380,8 +3380,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
// If a tx has been detected by m_recent_rejects, we will have reached
- // this point and the tx will have been ignored. Because we haven't run
- // the tx through AcceptToMemoryPool, we won't have computed a DoS
+ // this point and the tx will have been ignored. Because we haven't
+ // submitted the tx to our mempool, we won't have computed a DoS
// score for it or determined exactly why we consider it invalid.
//
// This means we won't penalize any peer subsequently relaying a DoSy
@@ -4312,8 +4312,11 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::microseconds now)
{
- if (m_connman.ShouldRunInactivityChecks(node_to) && peer.m_ping_nonce_sent &&
+ if (m_connman.ShouldRunInactivityChecks(node_to, std::chrono::duration_cast<std::chrono::seconds>(now).count()) &&
+ peer.m_ping_nonce_sent &&
now > peer.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL}) {
+ // The ping timeout is using mocktime. To disable the check during
+ // testing, increase -peertimeout.
LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(now - peer.m_ping_start.load()), peer.m_id);
node_to.fDisconnect = true;
return;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index f9fff5a6d5..7f1dd698b0 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -663,7 +663,7 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
}
/**
- * Try to get our IPv6 address.
+ * Try to get our IPv6 (or CJDNS) address.
*
* @param[out] pipv6Addr The in6_addr struct to which to copy.
*
@@ -674,7 +674,7 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
*/
bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
{
- if (!IsIPv6()) {
+ if (!IsIPv6() && !IsCJDNS()) {
return false;
}
assert(sizeof(*pipv6Addr) == m_addr.size());
@@ -794,8 +794,14 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
vchRet.push_back((ipv4 >> 24) & 0xFF);
vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
- } else if (IsTor() || IsI2P() || IsCJDNS()) {
+ } else if (IsTor() || IsI2P()) {
nBits = 4;
+ } else if (IsCJDNS()) {
+ // Treat in the same way as Tor and I2P because the address in all of
+ // them is "random" bytes (derived from a public key). However in CJDNS
+ // the first byte is a constant 0xfc, so the random bytes come after it.
+ // Thus skip the constant 8 bits at the start.
+ nBits = 12;
} else if (IsHeNet()) {
// for he.net, use /36 groups
nBits = 36;
@@ -892,6 +898,11 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const
case NET_I2P: return REACH_PRIVATE;
default: return REACH_DEFAULT;
}
+ case NET_CJDNS:
+ switch (ourNet) {
+ case NET_CJDNS: return REACH_PRIVATE;
+ default: return REACH_DEFAULT;
+ }
case NET_TEREDO:
switch(ourNet) {
default: return REACH_DEFAULT;
@@ -993,7 +1004,7 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
paddrin->sin_port = htons(port);
return true;
}
- if (IsIPv6()) {
+ if (IsIPv6() || IsCJDNS()) {
if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6))
return false;
*addrlen = sizeof(struct sockaddr_in6);
diff --git a/src/netaddress.h b/src/netaddress.h
index 66c8c48f08..b0b1c5ca9e 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -224,7 +224,7 @@ public:
*/
bool IsRelayable() const
{
- return IsIPv4() || IsIPv6() || IsTor() || IsI2P();
+ return IsIPv4() || IsIPv6() || IsTor() || IsI2P() || IsCJDNS();
}
/**
@@ -253,7 +253,6 @@ public:
}
}
- friend class CNetAddrHash;
friend class CSubNet;
private:
@@ -467,22 +466,6 @@ private:
}
};
-class CNetAddrHash
-{
-public:
- size_t operator()(const CNetAddr& a) const noexcept
- {
- CSipHasher hasher(m_salt_k0, m_salt_k1);
- hasher.Write(a.m_net);
- hasher.Write(a.m_addr.data(), a.m_addr.size());
- return static_cast<size_t>(hasher.Finalize());
- }
-
-private:
- const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
- const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
-};
-
class CSubNet
{
protected:
@@ -565,6 +548,26 @@ public:
READWRITEAS(CNetAddr, obj);
READWRITE(Using<BigEndianFormatter<2>>(obj.port));
}
+
+ friend class CServiceHash;
+ friend CService MaybeFlipIPv6toCJDNS(const CService& service);
+};
+
+class CServiceHash
+{
+public:
+ size_t operator()(const CService& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.port);
+ hasher.Write(a.m_addr.data(), a.m_addr.size());
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
};
#endif // BITCOIN_NETADDRESS_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 64d17189a6..6191f25cd9 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -96,6 +96,9 @@ enum Network ParseNetwork(const std::string& net_in) {
if (net == "i2p") {
return NET_I2P;
}
+ if (net == "cjdns") {
+ return NET_CJDNS;
+ }
return NET_UNROUTABLE;
}
@@ -120,7 +123,7 @@ std::vector<std::string> GetNetworkNames(bool append_unroutable)
std::vector<std::string> names;
for (int n = 0; n < NET_MAX; ++n) {
const enum Network network{static_cast<Network>(n)};
- if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue;
names.emplace_back(GetNetworkName(network));
}
if (append_unroutable) {
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 73f4036057..192caf7994 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -555,7 +555,7 @@ public:
{
if (!m_node.mempool) return false;
LOCK(m_node.mempool->cs);
- return m_node.mempool->exists(txid);
+ return m_node.mempool->exists(GenTxid::Txid(txid));
}
bool hasDescendantsInMempool(const uint256& txid) override
{
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 2a7bcc057f..33b8e9351c 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -70,8 +70,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (max_tx_fee > 0) {
// First, call ATMP with test_accept and check the fee. If ATMP
// fails here, return error immediately.
- const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
- true /* test_accept */);
+ const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
} else if (result.m_base_fees.value() > max_tx_fee) {
@@ -79,8 +78,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
}
}
// Try to submit the transaction to the mempool.
- const MempoolAcceptResult result = AcceptToMemoryPool(node.chainman->ActiveChainstate(), *node.mempool, tx, false /* bypass_limits */,
- false /* test_accept */);
+ const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
}
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp
index 25b9282b4e..ce149067b7 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -7,6 +7,8 @@
#include <tinyformat.h>
+#include <cmath>
+
CFeeRate::CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes)
{
const int64_t nSize{num_bytes};
@@ -22,7 +24,9 @@ CAmount CFeeRate::GetFee(uint32_t num_bytes) const
{
const int64_t nSize{num_bytes};
- CAmount nFee = nSatoshisPerK * nSize / 1000;
+ // Be explicit that we're converting from a double to int64_t (CAmount) here.
+ // We've previously had issues with the silent double->int64_t conversion.
+ CAmount nFee{static_cast<CAmount>(std::ceil(nSatoshisPerK * nSize / 1000.0))};
if (nFee == 0 && nSize != 0) {
if (nSatoshisPerK > 0) nFee = CAmount(1);
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index b16f3f8251..8ba896bb01 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -48,6 +48,7 @@ public:
CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes);
/**
* Return the fee in satoshis for the given size in bytes.
+ * If the calculated fee would have fractional satoshis, then the returned fee will always be rounded up to the nearest satoshi.
*/
CAmount GetFee(uint32_t num_bytes) const;
/**
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 7ac2e22006..7e6b0cf245 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -22,7 +22,7 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
// If this transaction is not in our mempool, then we can't be sure
// we will know about all its inputs.
- if (!pool.exists(tx.GetHash())) {
+ if (!pool.exists(GenTxid::Txid(tx.GetHash()))) {
return RBFTransactionState::UNKNOWN;
}
@@ -98,7 +98,7 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
// if the new input refers to a tx that's in the mempool.
- if (pool.exists(tx.vin[j].prevout.hash)) {
+ if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) {
return strprintf("replacement %s adds unconfirmed input, idx %d",
tx.GetHash().ToString(), j);
}
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 46db39f8db..947c1c60bb 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -391,8 +391,11 @@ class GenTxid
{
bool m_is_wtxid;
uint256 m_hash;
-public:
GenTxid(bool is_wtxid, const uint256& hash) : m_is_wtxid(is_wtxid), m_hash(hash) {}
+
+public:
+ static GenTxid Txid(const uint256& hash) { return GenTxid{false, hash}; }
+ static GenTxid Wtxid(const uint256& hash) { return GenTxid{true, hash}; }
bool IsWtxid() const { return m_is_wtxid; }
const uint256& GetHash() const { return m_hash; }
friend bool operator==(const GenTxid& a, const GenTxid& b) { return a.m_is_wtxid == b.m_is_wtxid && a.m_hash == b.m_hash; }
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 2e70b41e4c..7506c81815 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -223,5 +223,5 @@ std::vector<std::string> serviceFlagsToStr(uint64_t flags)
GenTxid ToGenTxid(const CInv& inv)
{
assert(inv.IsGenTxMsg());
- return {inv.IsMsgWtx(), inv.hash};
+ return inv.IsMsgWtx() ? GenTxid::Wtxid(inv.hash) : GenTxid::Txid(inv.hash);
}
diff --git a/src/pubkey.h b/src/pubkey.h
index 861a2cf500..f174ad8d85 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -230,8 +230,8 @@ public:
XOnlyPubKey& operator=(const XOnlyPubKey&) = default;
/** Determine if this pubkey is fully valid. This is true for approximately 50% of all
- * possible 32-byte arrays. If false, VerifySchnorr and CreatePayToContract will always
- * fail. */
+ * possible 32-byte arrays. If false, VerifySchnorr, CheckTapTweak and CreateTapTweak
+ * will always fail. */
bool IsFullyValid() const;
/** Test whether this is the 0 key (the result of default construction). This implies
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 7de56a648a..3f412d8f19 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -13,7 +13,6 @@
#include <interfaces/handler.h>
#include <interfaces/init.h>
#include <interfaces/node.h>
-#include <node/context.h>
#include <node/ui_interface.h>
#include <noui.h>
#include <qt/bitcoingui.h>
@@ -154,10 +153,11 @@ static bool InitSettings()
std::vector<std::string> errors;
if (!gArgs.ReadSettingsFile(&errors)) {
- bilingual_str error = _("Settings file could not be read");
- InitError(Untranslated(strprintf("%s:\n%s\n", error.original, MakeUnorderedList(errors))));
+ std::string error = QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be read");
+ std::string error_translated = QCoreApplication::translate("bitcoin-core", error.c_str()).toStdString();
+ InitError(Untranslated(strprintf("%s:\n%s\n", error, MakeUnorderedList(errors))));
- QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Reset | QMessageBox::Abort);
+ QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error_translated)), QMessageBox::Reset | QMessageBox::Abort);
/*: Explanatory text shown on startup when the settings file cannot be read.
Prompts user to make a choice between resetting or aborting. */
messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?"));
@@ -176,10 +176,11 @@ static bool InitSettings()
errors.clear();
if (!gArgs.WriteSettingsFile(&errors)) {
- bilingual_str error = _("Settings file could not be written");
- InitError(Untranslated(strprintf("%s:\n%s\n", error.original, MakeUnorderedList(errors))));
+ std::string error = QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be written");
+ std::string error_translated = QCoreApplication::translate("bitcoin-core", error.c_str()).toStdString();
+ InitError(Untranslated(strprintf("%s:\n%s\n", error, MakeUnorderedList(errors))));
- QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Ok);
+ QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error_translated)), QMessageBox::Ok);
/*: Explanatory text shown on startup when the settings file could not be written.
Prompts user to check that we have the ability to write to the file.
Explains that the user has the option of running without a settings file.*/
@@ -462,9 +463,7 @@ int GuiMain(int argc, char* argv[])
std::tie(argc, argv) = winArgs.get();
#endif
- NodeContext node_context;
- int unused_exit_status;
- std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
SetupEnvironment();
util::ThreadSetInternalName("main");
diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui
index b11fb026b0..56adbe17a5 100644
--- a/src/qt/forms/createwalletdialog.ui
+++ b/src/qt/forms/createwalletdialog.ui
@@ -107,6 +107,9 @@
<property name="text">
<string>Descriptor Wallet</string>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 2292c01d6a..61b52fd08a 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -184,8 +184,8 @@ static void InitMessage(SplashScreen *splash, const std::string &message)
static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible)
{
InitMessage(splash, title + std::string("\n") +
- (resume_possible ? _("(press q to shutdown and continue later)").translated
- : _("press q to shutdown").translated) +
+ (resume_possible ? SplashScreen::tr("(press q to shutdown and continue later)").toStdString()
+ : SplashScreen::tr("press q to shutdown").toStdString()) +
strprintf("\n%d", nProgress) + "%");
}
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 0de781661a..39cc1c1e1d 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -63,9 +63,13 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_client = wallet_client.get();
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
- wallet->SetupLegacyScriptPubKeyMan();
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", gArgs, CreateMockWalletDatabase());
wallet->LoadWallet();
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ {
+ LOCK(wallet->cs_wallet);
+ wallet->SetupDescriptorScriptPubKeyMans();
+ }
auto build_address = [&wallet]() {
CKey key;
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 55d00bb37e..b26cddf4ae 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -52,9 +52,7 @@ int main(int argc, char* argv[])
BasicTestingSetup dummy{CBaseChainParams::REGTEST};
}
- NodeContext node_context;
- int unused_exit_status;
- std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
gArgs.ForceSetArg("-listen", "0");
gArgs.ForceSetArg("-listenonion", "0");
gArgs.ForceSetArg("-discover", "0");
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 62b135d3f1..93d6aa805f 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -141,13 +141,22 @@ void TestGUI(interfaces::Node& node)
auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args));
test.m_node.wallet_client = wallet_client.get();
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", gArgs, CreateMockWalletDatabase());
wallet->LoadWallet();
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
{
- auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
- wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
- spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
+ LOCK(wallet->cs_wallet);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ // Add the coinbase key
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(test.coinbaseKey) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
+ CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type);
+ wallet->SetAddressBook(dest, "", "receive");
wallet->SetLastBlockProcessed(105, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
}
{
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index dadd82e03f..55048f6811 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -516,7 +516,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
{
- if (pool.exists(txin.prevout.hash))
+ if (pool.exists(GenTxid::Txid(txin.prevout.hash)))
setDepends.insert(txin.prevout.hash.ToString());
}
@@ -2202,7 +2202,11 @@ static RPCHelpMan savemempool()
return RPCHelpMan{"savemempool",
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
{},
- RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"},
+ }},
RPCExamples{
HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
@@ -2211,6 +2215,8 @@ static RPCHelpMan savemempool()
{
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
+ const NodeContext& node = EnsureAnyNodeContext(request.context);
+
if (!mempool.IsLoaded()) {
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
}
@@ -2219,7 +2225,10 @@ static RPCHelpMan savemempool()
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
}
- return NullUniValue;
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("filename", fs::path((node.args->GetDataDirNet() / "mempool.dat")).u8string());
+
+ return ret;
},
};
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index a9bee33c5d..e33f1ce4a3 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -566,7 +566,7 @@ static UniValue GetNetworksInfo()
UniValue networks(UniValue::VARR);
for (int n = 0; n < NET_MAX; ++n) {
enum Network network = static_cast<enum Network>(n);
- if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue;
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 483717aa7a..89f2309cb7 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -946,12 +946,13 @@ static RPCHelpMan testmempoolaccept()
NodeContext& node = EnsureAnyNodeContext(request.context);
CTxMemPool& mempool = EnsureMemPool(node);
- CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
+ ChainstateManager& chainman = EnsureChainman(node);
+ CChainState& chainstate = chainman.ActiveChainstate();
const PackageMempoolAcceptResult package_result = [&] {
LOCK(::cs_main);
if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /* test_accept */ true);
return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
- AcceptToMemoryPool(chainstate, mempool, txns[0], /* bypass_limits */ false, /* test_accept*/ true));
+ chainman.ProcessTransaction(txns[0], /*test_accept=*/ true));
}();
UniValue rpc_result(UniValue::VARR);
@@ -977,7 +978,7 @@ static RPCHelpMan testmempoolaccept()
if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
const CAmount fee = tx_result.m_base_fees.value();
// Check that fee does not exceed maximum fee
- const int64_t virtual_size = GetVirtualTransactionSize(*tx);
+ const int64_t virtual_size = tx_result.m_vsize.value();
const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
if (max_raw_tx_fee && fee > max_raw_tx_fee) {
result_inner.pushKV("allowed", false);
diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h
index 0e31ad3ce3..c4923dc56f 100644
--- a/src/support/allocators/secure.h
+++ b/src/support/allocators/secure.h
@@ -9,6 +9,7 @@
#include <support/lockedpool.h>
#include <support/cleanse.h>
+#include <memory>
#include <string>
//
@@ -17,15 +18,13 @@
//
template <typename T>
struct secure_allocator : public std::allocator<T> {
- // MSVC8 default copy constructor is broken
- typedef std::allocator<T> base;
- typedef typename base::size_type size_type;
- typedef typename base::difference_type difference_type;
- typedef typename base::pointer pointer;
- typedef typename base::const_pointer const_pointer;
- typedef typename base::reference reference;
- typedef typename base::const_reference const_reference;
- typedef typename base::value_type value_type;
+ using base = std::allocator<T>;
+ using traits = std::allocator_traits<base>;
+ using size_type = typename traits::size_type;
+ using difference_type = typename traits::difference_type;
+ using pointer = typename traits::pointer;
+ using const_pointer = typename traits::const_pointer;
+ using value_type = typename traits::value_type;
secure_allocator() noexcept {}
secure_allocator(const secure_allocator& a) noexcept : base(a) {}
template <typename U>
diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h
index 418f0ee656..77de4b1e69 100644
--- a/src/support/allocators/zeroafterfree.h
+++ b/src/support/allocators/zeroafterfree.h
@@ -13,15 +13,13 @@
template <typename T>
struct zero_after_free_allocator : public std::allocator<T> {
- // MSVC8 default copy constructor is broken
- typedef std::allocator<T> base;
- typedef typename base::size_type size_type;
- typedef typename base::difference_type difference_type;
- typedef typename base::pointer pointer;
- typedef typename base::const_pointer const_pointer;
- typedef typename base::reference reference;
- typedef typename base::const_reference const_reference;
- typedef typename base::value_type value_type;
+ using base = std::allocator<T>;
+ using traits = std::allocator_traits<base>;
+ using size_type = typename traits::size_type;
+ using difference_type = typename traits::difference_type;
+ using pointer = typename traits::pointer;
+ using const_pointer = typename traits::const_pointer;
+ using value_type = typename traits::value_type;
zero_after_free_allocator() noexcept {}
zero_after_free_allocator(const zero_after_free_allocator& a) noexcept : base(a) {}
template <typename U>
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index bd6f470219..eabc11c467 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -89,7 +89,7 @@ public:
deterministic = makeDeterministic;
}
- AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
+ AddrInfo* Find(const CService& addr, int* pnId = nullptr)
{
LOCK(m_impl->cs);
return m_impl->Find(addr, pnId);
@@ -222,15 +222,15 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_CHECK_EQUAL(addrman.size(), 1U);
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);
+ BOOST_CHECK(addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman.size(), 2U);
auto addr_ret2 = addrman.Select().first;
- BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334");
- // Test: Add same IP but diff port to tried table, it doesn't get added.
- // Perhaps this is not ideal behavior but it is the current behavior.
+ // Test: Add same IP but diff port to tried table; this converts the entry with
+ // the specified port to tried, but not the other.
addrman.Good(CAddress(addr1_port, NODE_NONE));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK_EQUAL(addrman.size(), 2U);
bool newOnly = true;
auto addr_ret3 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
@@ -347,7 +347,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions)
//Test: tried table collision!
CService addr1 = ResolveService("250.1.1." + ToString(++num_addrs));
uint32_t collisions{1};
- BOOST_CHECK(addrman.Add({CAddress(addr1, NODE_NONE)}, source));
+ BOOST_CHECK(!addrman.Add({CAddress(addr1, NODE_NONE)}, source));
BOOST_CHECK_EQUAL(addrman.size(), num_addrs - collisions);
CService addr2 = ResolveService("250.1.1." + ToString(++num_addrs));
@@ -369,18 +369,18 @@ BOOST_AUTO_TEST_CASE(addrman_find)
CNetAddr source2 = ResolveIP("250.1.2.2");
BOOST_CHECK(addrman.Add({addr1}, source1));
- BOOST_CHECK(!addrman.Add({addr2}, source2));
+ BOOST_CHECK(addrman.Add({addr2}, source2));
BOOST_CHECK(addrman.Add({addr3}, source1));
- // Test: ensure Find returns an IP matching what we searched on.
+ // Test: ensure Find returns an IP/port matching what we searched on.
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.
+ // Test; Find discriminates by port number.
AddrInfo* info2 = addrman.Find(addr2);
BOOST_REQUIRE(info2);
- BOOST_CHECK_EQUAL(info2->ToString(), info1->ToString());
+ BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:9999");
// Test: Find returns another IP matching what we searched on.
AddrInfo* info3 = addrman.Find(addr3);
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 114fe3907c..aa23d71671 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -48,13 +48,13 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(-9e3));
feeRate = CFeeRate(123);
- // Truncates the result, if not integer
+ // Rounds up the result, if not integer
BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(1)); // Special case: returns 1 instead of 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(1));
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(14));
- BOOST_CHECK_EQUAL(feeRate.GetFee(122), CAmount(15));
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(122));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(2));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(15));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(122), CAmount(16));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(123));
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(123));
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(1107));
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index a89868e1ef..0fa6b7784f 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -124,7 +124,7 @@ void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key;
CExtPubKey pubkey;
- key.SetSeed(seed.data(), seed.size());
+ key.SetSeed(seed);
pubkey = key.Neuter();
for (const TestDerivation &derive : test.vDerive) {
unsigned char data[74];
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 23ef2062ef..fe5ed0a3c8 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_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 <bloom.h>
+#include <common/bloom.h>
#include <clientversion.h>
#include <key.h>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 64c6d7f634..4d34407ca8 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -18,7 +18,17 @@
#include <utility>
#include <vector>
-BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)
+/**
+ * Identical to TestingSetup but excludes lock contention logging, as some of
+ * these tests are designed to be heavily contested to trigger race conditions
+ * or other issues.
+ */
+struct NoLockLoggingTestingSetup : public TestingSetup {
+ NoLockLoggingTestingSetup()
+ : TestingSetup{CBaseChainParams::MAIN, /*extra_args=*/{"-debugexclude=lock"}} {}
+};
+
+BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, NoLockLoggingTestingSetup)
static const unsigned int QUEUE_BATCH_SIZE = 128;
static const int SCRIPT_CHECK_THREADS = 3;
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 5b3b39fdb8..1483bd3cb3 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -694,8 +694,8 @@ BOOST_AUTO_TEST_CASE(chacha20_poly1305_aead_testvector)
TestChaCha20Poly1305AEAD(true, 0,
/* m */ "0000000000000000000000000000000000000000000000000000000000000000",
- /* k1 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000",
- /* k2 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* k1 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000",
+ /* k2 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000",
/* AAD keystream */ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586",
/* encrypted message & MAC */ "76b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32d2fc11829c1b6c1df1f551cd6131ff08",
/* encrypted message & MAC at sequence 999 */ "b0a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3aaa7aa16ec62c5e24f040c08bb20c3598");
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 0bfe6eecd9..668ff150ee 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -52,6 +52,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
const CChainParams& chainparams = Params();
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
+ // Disable inactivity checks for this test to avoid interference
+ static_cast<ConnmanTestMsg*>(connman.get())->SetPeerConnectTimeout(99999);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, false);
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 8df3707fc9..c6df6a0e61 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -137,24 +137,29 @@ public:
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
// Keys may be different.
- using AddrInfoHasher = std::function<size_t(const AddrInfo&)>;
- using AddrInfoEq = std::function<bool(const AddrInfo&, const AddrInfo&)>;
-
- CNetAddrHash netaddr_hasher;
-
- 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;
+ auto addrinfo_hasher = [](const AddrInfo& a) {
+ CSipHasher hasher(0, 0);
+ auto addr_key = a.GetKey();
+ auto source_key = a.source.GetAddrBytes();
+ hasher.Write(a.nLastSuccess);
+ hasher.Write(a.nAttempts);
+ hasher.Write(a.nRefCount);
+ hasher.Write(a.fInTried);
+ hasher.Write(a.GetNetwork());
+ hasher.Write(a.source.GetNetwork());
+ hasher.Write(addr_key.size());
+ hasher.Write(source_key.size());
+ hasher.Write(addr_key.data(), addr_key.size());
+ hasher.Write(source_key.data(), source_key.size());
+ return (size_t)hasher.Finalize();
};
- 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;
+ auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
+ return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.nLastSuccess, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
+ std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.nLastSuccess, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
};
- using Addresses = std::unordered_set<AddrInfo, AddrInfoHasher, AddrInfoEq>;
+ using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
const size_t num_addresses{m_impl->mapInfo.size()};
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index c5bb8744a4..746591a176 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.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 <bloom.h>
+#include <common/bloom.h>
#include <primitives/transaction.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
index edee5aeef7..29b7223c90 100644
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -68,18 +68,16 @@ FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serializa
}
if (deserializer.Complete()) {
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
- uint32_t out_err_raw_size{0};
- std::optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)};
- if (result) {
- assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE);
- assert(result->m_raw_message_size <= mutable_msg_bytes.size());
- assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size);
- assert(result->m_time == m_time);
+ bool reject_message{false};
+ CNetMessage msg = deserializer.GetMessage(m_time, reject_message);
+ assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
+ assert(msg.m_raw_message_size <= mutable_msg_bytes.size());
+ assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
+ assert(msg.m_time == m_time);
- std::vector<unsigned char> header;
- auto msg = CNetMsgMaker{result->m_recv.GetVersion()}.Make(result->m_command, MakeUCharSpan(result->m_recv));
- serializer.prepareForTransport(msg, header);
- }
+ std::vector<unsigned char> header;
+ auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_command, MakeUCharSpan(msg.m_recv));
+ serializer.prepareForTransport(msg2, header);
}
}
}
diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp
index b9ed497e68..9c18ad49cb 100644
--- a/src/test/fuzz/rolling_bloom_filter.cpp
+++ b/src/test/fuzz/rolling_bloom_filter.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 <bloom.h>
+#include <common/bloom.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index 6201cc813c..17b5ef88b9 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -81,7 +81,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate)
{
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
@@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh
std::vector<uint256> all_txids;
tx_pool.queryHashes(all_txids);
assert(all_txids.size() < info_all.size());
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
}
SyncWithValidationInterfaceQueue();
}
diff --git a/src/test/fuzz/txrequest.cpp b/src/test/fuzz/txrequest.cpp
index 72438ff2d7..a73bbcfc25 100644
--- a/src/test/fuzz/txrequest.cpp
+++ b/src/test/fuzz/txrequest.cpp
@@ -204,7 +204,7 @@ public:
}
// Call TxRequestTracker's implementation.
- m_tracker.ReceivedInv(peer, GenTxid{is_wtxid, TXHASHES[txhash]}, preferred, reqtime);
+ m_tracker.ReceivedInv(peer, is_wtxid ? GenTxid::Wtxid(TXHASHES[txhash]) : GenTxid::Txid(TXHASHES[txhash]), preferred, reqtime);
}
void RequestedTx(int peer, int txhash, std::chrono::microseconds exptime)
@@ -252,7 +252,7 @@ public:
for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
Announcement& ann2 = m_announcements[txhash][peer2];
if (ann2.m_state == State::REQUESTED && ann2.m_time <= m_now) {
- expected_expired.emplace_back(peer2, GenTxid{ann2.m_is_wtxid, TXHASHES[txhash]});
+ expected_expired.emplace_back(peer2, ann2.m_is_wtxid ? GenTxid::Wtxid(TXHASHES[txhash]) : GenTxid::Txid(TXHASHES[txhash]));
ann2.m_state = State::COMPLETED;
break;
}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 17e904fcff..b0c8068ab9 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -194,8 +194,8 @@ BOOST_AUTO_TEST_CASE(boolargno)
BOOST_AUTO_TEST_CASE(logargs)
{
- const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_BOOL);
- const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_BOOL);
+ const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_ANY);
+ const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_ANY);
const auto okaylog = std::make_pair("-okaylog", ArgsManager::ALLOW_ANY);
const auto dontlog = std::make_pair("-dontlog", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE);
SetupArgs({okaylog_bool, okaylog_negbool, okaylog, dontlog});
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index 8629d13840..0361618c82 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse)
privkey = DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
- BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
+ BOOST_CHECK_MESSAGE(Span<const uint8_t>{privkey} == Span<const uint8_t>{exp_payload}, "key mismatch:" + strTest);
// Private key must be invalid public key
destination = DecodeDestination(exp_base58string);
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index bf36f8a6c9..b3497b8ef8 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -444,12 +444,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2));
pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
- BOOST_CHECK(pool.exists(tx1.GetHash()));
- BOOST_CHECK(pool.exists(tx2.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx2.GetHash())));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
- BOOST_CHECK(pool.exists(tx1.GetHash()));
- BOOST_CHECK(!pool.exists(tx2.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
pool.addUnchecked(entry.FromTx(tx2));
CMutableTransaction tx3 = CMutableTransaction();
@@ -462,14 +462,14 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3));
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
- BOOST_CHECK(!pool.exists(tx1.GetHash()));
- BOOST_CHECK(pool.exists(tx2.GetHash()));
- BOOST_CHECK(pool.exists(tx3.GetHash()));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx2.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx3.GetHash())));
pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
- BOOST_CHECK(!pool.exists(tx1.GetHash()));
- BOOST_CHECK(!pool.exists(tx2.GetHash()));
- BOOST_CHECK(!pool.exists(tx3.GetHash()));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx1.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx2.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx3.GetHash())));
CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
@@ -529,19 +529,19 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
// we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
- BOOST_CHECK(pool.exists(tx4.GetHash()));
- BOOST_CHECK(pool.exists(tx6.GetHash()));
- BOOST_CHECK(!pool.exists(tx7.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
- if (!pool.exists(tx5.GetHash()))
+ if (!pool.exists(GenTxid::Txid(tx5.GetHash())))
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
- BOOST_CHECK(pool.exists(tx4.GetHash()));
- BOOST_CHECK(!pool.exists(tx5.GetHash()));
- BOOST_CHECK(pool.exists(tx6.GetHash()));
- BOOST_CHECK(!pool.exists(tx7.GetHash()));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx4.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx5.GetHash())));
+ BOOST_CHECK(pool.exists(GenTxid::Txid(tx6.GetHash())));
+ BOOST_CHECK(!pool.exists(GenTxid::Txid(tx7.GetHash())));
pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 687d2f6747..b6d7496cc7 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -339,11 +339,13 @@ BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
BOOST_CHECK_EQUAL(ParseNetwork("ipv6"), NET_IPV6);
BOOST_CHECK_EQUAL(ParseNetwork("onion"), NET_ONION);
BOOST_CHECK_EQUAL(ParseNetwork("tor"), NET_ONION);
+ BOOST_CHECK_EQUAL(ParseNetwork("cjdns"), NET_CJDNS);
BOOST_CHECK_EQUAL(ParseNetwork("IPv4"), NET_IPV4);
BOOST_CHECK_EQUAL(ParseNetwork("IPv6"), NET_IPV6);
BOOST_CHECK_EQUAL(ParseNetwork("ONION"), NET_ONION);
BOOST_CHECK_EQUAL(ParseNetwork("TOR"), NET_ONION);
+ BOOST_CHECK_EQUAL(ParseNetwork("CJDNS"), NET_CJDNS);
BOOST_CHECK_EQUAL(ParseNetwork(":)"), NET_UNROUTABLE);
BOOST_CHECK_EQUAL(ParseNetwork("tÖr"), NET_UNROUTABLE);
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index acd0151e1a..54f04d2e67 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -215,7 +215,9 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
BOOST_AUTO_TEST_CASE(streams_buffered_file)
{
- FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
+ fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
+ FILE* file = fsbridge::fopen(streams_test_filename, "w+b");
+
// The value at each offset is the offset.
for (uint8_t j = 0; j < 40; ++j) {
fwrite(&j, 1, 1, file);
@@ -343,7 +345,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file)
// We can explicitly close the file, or the destructor will do it.
bf.fclose();
- fs::remove("streams_test_tmp");
+ fs::remove(streams_test_filename);
}
BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
@@ -351,8 +353,9 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
// Make this test deterministic.
SeedInsecureRand(SeedRand::ZEROS);
+ fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
for (int rep = 0; rep < 50; ++rep) {
- FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
+ FILE* file = fsbridge::fopen(streams_test_filename, "w+b");
size_t fileSize = InsecureRandRange(256);
for (uint8_t i = 0; i < fileSize; ++i) {
fwrite(&i, 1, 1, file);
@@ -453,7 +456,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
maxPos = currentPos;
}
}
- fs::remove("streams_test_tmp");
+ fs::remove(streams_test_filename);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index c813fbea32..252a85c282 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -810,10 +810,10 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
// nDustThreshold = 182 * 3702 / 1000
dustRelayFee = CFeeRate(3702);
// dust:
- t.vout[0].nValue = 673 - 1;
+ t.vout[0].nValue = 674 - 1;
CheckIsNotStandard(t, "dust");
// not dust:
- t.vout[0].nValue = 673;
+ t.vout[0].nValue = 674;
CheckIsStandard(t);
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
new file mode 100644
index 0000000000..537a6ccea1
--- /dev/null
+++ b/src/test/txpackage_tests.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <consensus/validation.h>
+#include <key_io.h>
+#include <policy/packages.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <test/util/setup_common.h>
+#include <validation.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(txpackage_tests)
+
+// Create placeholder transactions that have no meaning.
+inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
+{
+ CMutableTransaction mtx = CMutableTransaction();
+ mtx.vin.resize(num_inputs);
+ mtx.vout.resize(num_outputs);
+ auto random_script = CScript() << ToByteVector(InsecureRand256()) << ToByteVector(InsecureRand256());
+ for (size_t i{0}; i < num_inputs; ++i) {
+ mtx.vin[i].prevout.hash = InsecureRand256();
+ mtx.vin[i].prevout.n = 0;
+ mtx.vin[i].scriptSig = random_script;
+ }
+ for (size_t o{0}; o < num_outputs; ++o) {
+ mtx.vout[o].nValue = 1 * CENT;
+ mtx.vout[o].scriptPubKey = random_script;
+ }
+ return MakeTransactionRef(mtx);
+}
+
+BOOST_FIXTURE_TEST_CASE(package_sanitization_tests, TestChain100Setup)
+{
+ // Packages can't have more than 25 transactions.
+ Package package_too_many;
+ package_too_many.reserve(MAX_PACKAGE_COUNT + 1);
+ for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) {
+ package_too_many.emplace_back(create_placeholder_tx(1, 1));
+ }
+ PackageValidationState state_too_many;
+ BOOST_CHECK(!CheckPackage(package_too_many, state_too_many));
+ BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY);
+ BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");
+
+ // Packages can't have a total size of more than 101KvB.
+ CTransactionRef large_ptx = create_placeholder_tx(150, 150);
+ Package package_too_large;
+ auto size_large = GetVirtualTransactionSize(*large_ptx);
+ size_t total_size{0};
+ while (total_size <= MAX_PACKAGE_SIZE * 1000) {
+ package_too_large.push_back(large_ptx);
+ total_size += size_large;
+ }
+ BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
+ PackageValidationState state_too_large;
+ BOOST_CHECK(!CheckPackage(package_too_large, state_too_large));
+ BOOST_CHECK_EQUAL(state_too_large.GetResult(), PackageValidationResult::PCKG_POLICY);
+ BOOST_CHECK_EQUAL(state_too_large.GetRejectReason(), "package-too-large");
+}
+
+BOOST_FIXTURE_TEST_CASE(package_validation_tests, TestChain100Setup)
+{
+ LOCK(cs_main);
+ unsigned int initialPoolSize = m_node.mempool->size();
+
+ // Parent and Child Package
+ CKey parent_key;
+ parent_key.MakeNewKey(true);
+ CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));
+ auto mtx_parent = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[0], /* vout */ 0,
+ /* input_height */ 0, /* input_signing_key */ coinbaseKey,
+ /* output_destination */ parent_locking_script,
+ /* output_amount */ CAmount(49 * COIN), /* submit */ false);
+ CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
+
+ CKey child_key;
+ child_key.MakeNewKey(true);
+ CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));
+ auto mtx_child = CreateValidMempoolTransaction(/* input_transaction */ tx_parent, /* vout */ 0,
+ /* input_height */ 101, /* input_signing_key */ parent_key,
+ /* output_destination */ child_locking_script,
+ /* output_amount */ CAmount(48 * COIN), /* submit */ false);
+ CTransactionRef tx_child = MakeTransactionRef(mtx_child);
+ const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {tx_parent, tx_child}, /* test_accept */ true);
+ BOOST_CHECK_MESSAGE(result_parent_child.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << result_parent_child.m_state.GetRejectReason());
+ auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());
+ auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash());
+ BOOST_CHECK(it_parent != result_parent_child.m_tx_results.end());
+ BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << it_parent->second.m_state.GetRejectReason());
+ BOOST_CHECK(it_child != result_parent_child.m_tx_results.end());
+ BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(),
+ "Package validation unexpectedly failed: " << it_child->second.m_state.GetRejectReason());
+
+
+ // A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
+ CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
+ BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000);
+ auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /* test_accept */ true);
+ BOOST_CHECK(result_single_large.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
+ BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed");
+ auto it_giant_tx = result_single_large.m_tx_results.find(giant_ptx->GetWitnessHash());
+ BOOST_CHECK(it_giant_tx != result_single_large.m_tx_results.end());
+ BOOST_CHECK_EQUAL(it_giant_tx->second.m_state.GetRejectReason(), "tx-size");
+
+ // Check that mempool size hasn't changed.
+ BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
+}
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
index 1d137b03b1..99d41882c9 100644
--- a/src/test/txrequest_tests.cpp
+++ b/src/test/txrequest_tests.cpp
@@ -221,7 +221,7 @@ public:
/** Generate a random GenTxid; the txhash follows NewTxHash; the is_wtxid flag is random. */
GenTxid NewGTxid(const std::vector<std::vector<NodeId>>& orders = {})
{
- return {InsecureRandBool(), NewTxHash(orders)};
+ return InsecureRandBool() ? GenTxid::Wtxid(NewTxHash(orders)) : GenTxid::Txid(NewTxHash(orders));
}
/** Generate a new random NodeId to use as peer. The same NodeId is never returned twice
@@ -494,8 +494,8 @@ void BuildWtxidTest(Scenario& scenario, int config)
auto peerT = scenario.NewPeer();
auto peerW = scenario.NewPeer();
auto txhash = scenario.NewTxHash();
- GenTxid txid{false, txhash};
- GenTxid wtxid{true, txhash};
+ auto txid{GenTxid::Txid(txhash)};
+ auto wtxid{GenTxid::Wtxid(txhash)};
auto reqtimeT = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
auto reqtimeW = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index ade9e210f2..c71ab01af4 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -37,8 +37,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
- const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
- true /* bypass_limits */);
+ const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
@@ -50,99 +49,4 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
}
-
-// Create placeholder transactions that have no meaning.
-inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
-{
- CMutableTransaction mtx = CMutableTransaction();
- mtx.vin.resize(num_inputs);
- mtx.vout.resize(num_outputs);
- auto random_script = CScript() << ToByteVector(InsecureRand256()) << ToByteVector(InsecureRand256());
- for (size_t i{0}; i < num_inputs; ++i) {
- mtx.vin[i].prevout.hash = InsecureRand256();
- mtx.vin[i].prevout.n = 0;
- mtx.vin[i].scriptSig = random_script;
- }
- for (size_t o{0}; o < num_outputs; ++o) {
- mtx.vout[o].nValue = 1 * CENT;
- mtx.vout[o].scriptPubKey = random_script;
- }
- return MakeTransactionRef(mtx);
-}
-
-BOOST_FIXTURE_TEST_CASE(package_tests, TestChain100Setup)
-{
- LOCK(cs_main);
- unsigned int initialPoolSize = m_node.mempool->size();
-
- // Parent and Child Package
- CKey parent_key;
- parent_key.MakeNewKey(true);
- CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));
- auto mtx_parent = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[0], /* vout */ 0,
- /* input_height */ 0, /* input_signing_key */ coinbaseKey,
- /* output_destination */ parent_locking_script,
- /* output_amount */ CAmount(49 * COIN), /* submit */ false);
- CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
-
- CKey child_key;
- child_key.MakeNewKey(true);
- CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));
- auto mtx_child = CreateValidMempoolTransaction(/* input_transaction */ tx_parent, /* vout */ 0,
- /* input_height */ 101, /* input_signing_key */ parent_key,
- /* output_destination */ child_locking_script,
- /* output_amount */ CAmount(48 * COIN), /* submit */ false);
- CTransactionRef tx_child = MakeTransactionRef(mtx_child);
- const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {tx_parent, tx_child}, /* test_accept */ true);
- BOOST_CHECK_MESSAGE(result_parent_child.m_state.IsValid(),
- "Package validation unexpectedly failed: " << result_parent_child.m_state.GetRejectReason());
- auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());
- auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash());
- BOOST_CHECK(it_parent != result_parent_child.m_tx_results.end());
- BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(),
- "Package validation unexpectedly failed: " << it_parent->second.m_state.GetRejectReason());
- BOOST_CHECK(it_child != result_parent_child.m_tx_results.end());
- BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(),
- "Package validation unexpectedly failed: " << it_child->second.m_state.GetRejectReason());
-
- // Packages can't have more than 25 transactions.
- Package package_too_many;
- package_too_many.reserve(MAX_PACKAGE_COUNT + 1);
- for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) {
- package_too_many.emplace_back(create_placeholder_tx(1, 1));
- }
- auto result_too_many = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_many, /* test_accept */ true);
- BOOST_CHECK(result_too_many.m_state.IsInvalid());
- BOOST_CHECK_EQUAL(result_too_many.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
- BOOST_CHECK_EQUAL(result_too_many.m_state.GetRejectReason(), "package-too-many-transactions");
-
- // Packages can't have a total size of more than 101KvB.
- CTransactionRef large_ptx = create_placeholder_tx(150, 150);
- Package package_too_large;
- auto size_large = GetVirtualTransactionSize(*large_ptx);
- size_t total_size{0};
- while (total_size <= MAX_PACKAGE_SIZE * 1000) {
- package_too_large.push_back(large_ptx);
- total_size += size_large;
- }
- BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
- auto result_too_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_large, /* test_accept */ true);
- BOOST_CHECK(result_too_large.m_state.IsInvalid());
- BOOST_CHECK_EQUAL(result_too_large.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
- BOOST_CHECK_EQUAL(result_too_large.m_state.GetRejectReason(), "package-too-large");
-
- // A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
- CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
- BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000);
- auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /* test_accept */ true);
- BOOST_CHECK(result_single_large.m_state.IsInvalid());
- BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
- BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed");
- auto it_giant_tx = result_single_large.m_tx_results.find(giant_ptx->GetWitnessHash());
- BOOST_CHECK(it_giant_tx != result_single_large.m_tx_results.end());
- BOOST_CHECK_EQUAL(it_giant_tx->second.m_state.GetRejectReason(), "tx-size");
-
- // Check that mempool size hasn't changed.
- BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
-}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index afb3ad0cfd..8be64531c4 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -36,8 +36,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx),
- true /* bypass_limits */);
+ const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(tx));
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 939ec322ed..d89fc34b75 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -17,6 +17,12 @@
struct ConnmanTestMsg : public CConnman {
using CConnman::CConnman;
+
+ void SetPeerConnectTimeout(int64_t timeout)
+ {
+ m_peer_connect_timeout = timeout;
+ }
+
void AddTestNode(CNode& node)
{
LOCK(cs_vNodes);
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index a3c7564d76..5a0c8e152a 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -315,7 +315,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
// If submit=true, add transaction to the mempool.
if (submit) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
+ const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(mempool_txn));
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
diff --git a/src/test/util/wallet.cpp b/src/test/util/wallet.cpp
index 061659818f..76c1bf93a5 100644
--- a/src/test/util/wallet.cpp
+++ b/src/test/util/wallet.cpp
@@ -25,16 +25,4 @@ std::string getnewaddress(CWallet& w)
return EncodeDestination(dest);
}
-void importaddress(CWallet& wallet, const std::string& address)
-{
- auto spk_man = wallet.GetLegacyScriptPubKeyMan();
- LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- const auto dest = DecodeDestination(address);
- assert(IsValidDestination(dest));
- const auto script = GetScriptForDestination(dest);
- wallet.MarkDirty();
- assert(!spk_man->HaveWatchOnly(script));
- if (!spk_man->AddWatchOnly(script, 0 /* nCreateTime */)) assert(false);
- wallet.SetAddressBook(dest, /* label */ "", "receive");
-}
#endif // ENABLE_WALLET
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 8f4ff6815b..8a48d539f8 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
LOCK(cs_main);
for (const auto& tx : txs) {
- const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
+ const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(tx);
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 5a93f30c8a..502a27dc6b 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -5,6 +5,7 @@
#include <txmempool.h>
+#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
@@ -21,6 +22,58 @@
#include <cmath>
#include <optional>
+// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
+struct update_descendant_state
+{
+ update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
+ modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
+ {}
+
+ void operator() (CTxMemPoolEntry &e)
+ { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
+
+ private:
+ int64_t modifySize;
+ CAmount modifyFee;
+ int64_t modifyCount;
+};
+
+struct update_ancestor_state
+{
+ update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
+ modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
+ {}
+
+ void operator() (CTxMemPoolEntry &e)
+ { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
+
+ private:
+ int64_t modifySize;
+ CAmount modifyFee;
+ int64_t modifyCount;
+ int64_t modifySigOpsCost;
+};
+
+struct update_fee_delta
+{
+ explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
+
+ void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
+
+private:
+ int64_t feeDelta;
+};
+
+struct update_lock_points
+{
+ explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
+
+ void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
+
+private:
+ const LockPoints& lp;
+};
+
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
int64_t time, unsigned int entry_height,
bool spends_coinbase, int64_t sigops_cost, LockPoints lp)
@@ -276,7 +329,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
{
- CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
+ const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst();
// add or remove this tx as a child of each parent
for (const CTxMemPoolEntry& parent : parents) {
UpdateChild(mapTx.iterator_to(parent), it, add);
@@ -671,16 +724,7 @@ void CTxMemPool::clear()
_clear();
}
-static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
-{
- TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
- CAmount txfee = 0;
- bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee);
- assert(fCheckResult);
- UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
-}
-
-void CTxMemPool::check(CChainState& active_chainstate) const
+void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
{
if (m_check_ratio == 0) return;
@@ -693,20 +737,16 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t checkTotal = 0;
CAmount check_total_fee{0};
uint64_t innerUsage = 0;
+ uint64_t prev_ancestor_count{0};
- CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
- const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- std::list<const CTxMemPoolEntry*> waitingOnDependants;
- for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
- unsigned int i = 0;
+ for (const auto& it : GetSortedDepthAndScore()) {
checkTotal += it->GetTxSize();
check_total_fee += it->GetFee();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
- bool fDependsWait = false;
CTxMemPoolEntry::Parents setParentCheck;
for (const CTxIn &txin : tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
@@ -714,17 +754,17 @@ void CTxMemPool::check(CChainState& active_chainstate) const
if (it2 != mapTx.end()) {
const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
- fDependsWait = true;
setParentCheck.insert(*it2);
- } else {
- assert(active_coins_tip.HaveCoin(txin.prevout));
}
+ // We are iterating through the mempool entries sorted in order by ancestor count.
+ // All parents must have been checked before their children and their coins added to
+ // the mempoolDuplicate coins cache.
+ assert(mempoolDuplicate.HaveCoin(txin.prevout));
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
assert(it3 != mapNextTx.end());
assert(it3->first == &txin.prevout);
assert(it3->second == &tx);
- i++;
}
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
return a.GetTx().GetHash() == b.GetTx().GetHash();
@@ -751,6 +791,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const
assert(it->GetSizeWithAncestors() == nSizeCheck);
assert(it->GetSigOpCostWithAncestors() == nSigOpCheck);
assert(it->GetModFeesWithAncestors() == nFeesCheck);
+ // Sanity check: we are walking in ascending ancestor count order.
+ assert(prev_ancestor_count <= it->GetCountWithAncestors());
+ prev_ancestor_count = it->GetCountWithAncestors();
// Check children against mapNextTx
CTxMemPoolEntry::Children setChildrenCheck;
@@ -769,24 +812,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const
// just a sanity check, not definitive that this calc is correct...
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
- if (fDependsWait)
- waitingOnDependants.push_back(&(*it));
- else {
- CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
- }
- }
- unsigned int stepsSinceLastRemove = 0;
- while (!waitingOnDependants.empty()) {
- const CTxMemPoolEntry* entry = waitingOnDependants.front();
- waitingOnDependants.pop_front();
- if (!mempoolDuplicate.HaveInputs(entry->GetTx())) {
- waitingOnDependants.push_back(entry);
- stepsSinceLastRemove++;
- assert(stepsSinceLastRemove < waitingOnDependants.size());
- } else {
- CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
- stepsSinceLastRemove = 0;
- }
+ TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
+ CAmount txfee = 0;
+ assert(!tx.IsCoinBase());
+ assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
+ for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
+ AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
}
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
uint256 hash = it->second->GetHash();
@@ -895,8 +926,6 @@ TxMempoolInfo CTxMemPool::info(const GenTxid& gtxid) const
return GetInfo(i);
}
-TxMempoolInfo CTxMemPool::info(const uint256& txid) const { return info(GenTxid{false, txid}); }
-
void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta)
{
{
@@ -969,7 +998,7 @@ CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) c
bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
{
for (unsigned int i = 0; i < tx.vin.size(); i++)
- if (exists(tx.vin[i].prevout.hash))
+ if (exists(GenTxid::Txid(tx.vin[i].prevout.hash)))
return false;
return true;
}
@@ -1140,7 +1169,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
if (pvNoSpendsRemaining) {
for (const CTransaction& tx : txn) {
for (const CTxIn& txin : tx.vin) {
- if (exists(txin.prevout.hash)) continue;
+ if (exists(GenTxid::Txid(txin.prevout.hash))) continue;
pvNoSpendsRemaining->push_back(txin.prevout);
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 460e9d0ceb..85417ac3fc 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -159,58 +159,6 @@ public:
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
};
-// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
-struct update_descendant_state
-{
- update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) :
- modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount)
- {}
-
- void operator() (CTxMemPoolEntry &e)
- { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
-
- private:
- int64_t modifySize;
- CAmount modifyFee;
- int64_t modifyCount;
-};
-
-struct update_ancestor_state
-{
- update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int64_t _modifySigOpsCost) :
- modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOpsCost(_modifySigOpsCost)
- {}
-
- void operator() (CTxMemPoolEntry &e)
- { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOpsCost); }
-
- private:
- int64_t modifySize;
- CAmount modifyFee;
- int64_t modifyCount;
- int64_t modifySigOpsCost;
-};
-
-struct update_fee_delta
-{
- explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
-
- void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
-
-private:
- int64_t feeDelta;
-};
-
-struct update_lock_points
-{
- explicit update_lock_points(const LockPoints& _lp) : lp(_lp) { }
-
- void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
-
-private:
- const LockPoints& lp;
-};
-
// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
struct mempoolentry_txid
{
@@ -622,7 +570,7 @@ public:
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
* check does nothing.
*/
- void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
// addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
@@ -782,7 +730,6 @@ public:
}
return (mapTx.count(gtxid.GetHash()) != 0);
}
- bool exists(const uint256& txid) const { return exists(GenTxid{false, txid}); }
CTransactionRef get(const uint256& hash) const;
txiter get_iter_from_wtxid(const uint256& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
@@ -790,7 +737,6 @@ public:
AssertLockHeld(cs);
return mapTx.project<0>(mapTx.get<index_by_wtxid>().find(wtxid));
}
- TxMempoolInfo info(const uint256& hash) const;
TxMempoolInfo info(const GenTxid& gtxid) const;
std::vector<TxMempoolInfo> infoAll() const;
@@ -802,7 +748,7 @@ public:
LOCK(cs);
// Sanity check the transaction is in the mempool & insert into
// unbroadcast set.
- if (exists(txid)) m_unbroadcast_txids.insert(txid);
+ if (exists(GenTxid::Txid(txid))) m_unbroadcast_txids.insert(txid);
};
/** Removes a transaction from the unbroadcast set */
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
index f8d7a1ece8..7d478a5b26 100644
--- a/src/txrequest.cpp
+++ b/src/txrequest.cpp
@@ -300,7 +300,7 @@ std::map<uint256, TxHashInfo> ComputeTxHashInfo(const Index& index, const Priori
GenTxid ToGenTxid(const Announcement& ann)
{
- return {ann.m_is_wtxid, ann.m_txhash};
+ return ann.m_is_wtxid ? GenTxid::Wtxid(ann.m_txhash) : GenTxid::Txid(ann.m_txhash);
}
} // namespace
diff --git a/src/util/error.cpp b/src/util/error.cpp
index 48c81693f3..d019ba018d 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -20,9 +20,9 @@ bilingual_str TransactionErrorString(const TransactionError err)
case TransactionError::P2P_DISABLED:
return Untranslated("Peer-to-peer functionality missing or disabled");
case TransactionError::MEMPOOL_REJECTED:
- return Untranslated("Transaction rejected by AcceptToMemoryPool");
+ return Untranslated("Transaction rejected by mempool");
case TransactionError::MEMPOOL_ERROR:
- return Untranslated("AcceptToMemoryPool failed");
+ return Untranslated("Mempool internal error");
case TransactionError::INVALID_PSBT:
return Untranslated("PSBT is not well-formed");
case TransactionError::PSBT_MISMATCH:
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 12d7dc49b2..99d111b066 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -71,6 +71,7 @@
#endif
#include <boost/algorithm/string/replace.hpp>
+#include <optional>
#include <thread>
#include <typeinfo>
#include <univalue.h>
@@ -182,60 +183,65 @@ static std::string SettingName(const std::string& arg)
return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
}
+struct KeyInfo {
+ std::string name;
+ std::string section;
+ bool negated{false};
+};
+
/**
- * Interpret -nofoo as if the user supplied -foo=0.
- *
- * This method also tracks when the -no form was supplied, and if so,
- * checks whether there was a double-negative (-nofoo=0 -> -foo=1).
- *
- * If there was not a double negative, it removes the "no" from the key
- * and returns false.
+ * Parse "name", "section.name", "noname", "section.noname" settings keys.
*
- * If there was a double negative, it removes "no" from the key, and
- * returns true.
- *
- * If there was no "no", it returns the string value untouched.
- *
- * Where an option was negated can be later checked using the
+ * @note Where an option was negated can be later checked using the
* IsArgNegated() method. One use case for this is to have a way to disable
* options that are not normally boolean (e.g. using -nodebuglogfile to request
* that debug log output is not sent to any file at all).
*/
-
-static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value)
+KeyInfo InterpretKey(std::string key)
{
+ KeyInfo result;
// Split section name from key name for keys like "testnet.foo" or "regtest.bar"
size_t option_index = key.find('.');
if (option_index != std::string::npos) {
- section = key.substr(0, option_index);
+ result.section = key.substr(0, option_index);
key.erase(0, option_index + 1);
}
if (key.substr(0, 2) == "no") {
key.erase(0, 2);
- // Double negatives like -nofoo=0 are supported (but discouraged)
- if (!InterpretBool(value)) {
- LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value);
- return true;
- }
- return false;
+ result.negated = true;
}
- return value;
+ result.name = key;
+ return result;
}
/**
- * Check settings value validity according to flags.
+ * Interpret settings value based on registered flags.
+ *
+ * @param[in] key key information to know if key was negated
+ * @param[in] value string value of setting to be parsed
+ * @param[in] flags ArgsManager registered argument flags
+ * @param[out] error Error description if settings value is not valid
*
- * TODO: Add more meaningful error checks here in the future
- * See "here's how the flags are meant to behave" in
- * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
+ * @return parsed settings value if it is valid, otherwise nullopt accompanied
+ * by a descriptive error string
*/
-static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error)
-{
- if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) {
- error = strprintf("Negating of -%s is meaningless and therefore forbidden", key);
+static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string& value,
+ unsigned int flags, std::string& error)
+{
+ // Return negated settings as false values.
+ if (key.negated) {
+ if (flags & ArgsManager::DISALLOW_NEGATION) {
+ error = strprintf("Negating of -%s is meaningless and therefore forbidden", key.name);
+ return std::nullopt;
+ }
+ // Double negatives like -nofoo=0 are supported (but discouraged)
+ if (!InterpretBool(value)) {
+ LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, value);
+ return true;
+ }
return false;
}
- return true;
+ return value;
}
namespace {
@@ -351,21 +357,21 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
// Transform -foo to foo
key.erase(0, 1);
- std::string section;
- util::SettingsValue value = InterpretOption(section, key, val);
- std::optional<unsigned int> flags = GetArgFlags('-' + key);
+ KeyInfo keyinfo = InterpretKey(key);
+ std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
// Unknown command line options and command line options with dot
- // characters (which are returned from InterpretOption with nonempty
+ // characters (which are returned from InterpretKey with nonempty
// section strings) are not valid.
- if (!flags || !section.empty()) {
+ if (!flags || !keyinfo.section.empty()) {
error = strprintf("Invalid parameter %s", argv[i]);
return false;
}
- if (!CheckValid(key, value, *flags, error)) return false;
+ std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val, *flags, error);
+ if (!value) return false;
- m_settings.command_line_options[key].push_back(value);
+ m_settings.command_line_options[keyinfo.name].push_back(*value);
}
// we do not allow -includeconf from command line, only -noincludeconf
@@ -548,10 +554,8 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
return false;
}
for (const auto& setting : m_settings.rw_settings) {
- std::string section;
- std::string key = setting.first;
- (void)InterpretOption(section, key, /* value */ {}); // Split setting key into section and argname
- if (!GetArgFlags('-' + key)) {
+ KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
+ if (!GetArgFlags('-' + key.name)) {
LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
}
}
@@ -870,15 +874,14 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
- std::string section;
- std::string key = option.first;
- util::SettingsValue value = InterpretOption(section, key, option.second);
- std::optional<unsigned int> flags = GetArgFlags('-' + key);
+ KeyInfo key = InterpretKey(option.first);
+ std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
if (flags) {
- if (!CheckValid(key, value, *flags, error)) {
+ std::optional<util::SettingsValue> value = InterpretValue(key, option.second, *flags, error);
+ if (!value) {
return false;
}
- m_settings.ro_config[section][key].push_back(value);
+ m_settings.ro_config[key.section][key.name].push_back(*value);
} else {
if (ignore_invalid_keys) {
LogPrintf("Ignoring unknown configuration value %s\n", option.first);
diff --git a/src/util/system.h b/src/util/system.h
index 2e217f6f90..37d976221b 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -158,12 +158,18 @@ struct SectionInfo
class ArgsManager
{
public:
+ /**
+ * Flags controlling how config and command line arguments are validated and
+ * interpreted.
+ */
enum Flags : uint32_t {
- // Boolean options can accept negation syntax -noOPTION or -noOPTION=1
- ALLOW_BOOL = 0x01,
- ALLOW_INT = 0x02,
- ALLOW_STRING = 0x04,
- ALLOW_ANY = ALLOW_BOOL | ALLOW_INT | ALLOW_STRING,
+ ALLOW_ANY = 0x01, //!< disable validation
+ // ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
+ // ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
+ // ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
+ // ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
+ DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
+
DEBUG_ONLY = 0x100,
/* Some options would cause cross-contamination if values for
* mainnet were used while running on regtest/testnet (or vice-versa).
diff --git a/src/validation.cpp b/src/validation.cpp
index ff71020ebb..f163130a18 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -355,7 +355,7 @@ void CChainState::MaybeUpdateMempoolForReorg(
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
m_mempool->removeRecursive(**it, MemPoolRemovalReason::REORG);
- } else if (m_mempool->exists((*it)->GetHash())) {
+ } else if (m_mempool->exists(GenTxid::Txid((*it)->GetHash()))) {
vHashUpdate.push_back((*it)->GetHash());
}
++it;
@@ -450,7 +450,36 @@ public:
/** Whether we allow transactions to replace mempool transactions by BIP125 rules. If false,
* any transaction spending the same inputs as a transaction in the mempool is considered
* a conflict. */
- const bool m_allow_bip125_replacement{true};
+ const bool m_allow_bip125_replacement;
+
+ /** Parameters for single transaction mempool validation. */
+ static ATMPArgs SingleAccept(const CChainParams& chainparams, int64_t accept_time,
+ bool bypass_limits, std::vector<COutPoint>& coins_to_uncache,
+ bool test_accept) {
+ return ATMPArgs{/* m_chainparams */ chainparams,
+ /* m_accept_time */ accept_time,
+ /* m_bypass_limits */ bypass_limits,
+ /* m_coins_to_uncache */ coins_to_uncache,
+ /* m_test_accept */ test_accept,
+ /* m_allow_bip125_replacement */ true,
+ };
+ }
+
+ /** Parameters for test package mempool validation through testmempoolaccept. */
+ static ATMPArgs PackageTestAccept(const CChainParams& chainparams, int64_t accept_time,
+ std::vector<COutPoint>& coins_to_uncache) {
+ return ATMPArgs{/* m_chainparams */ chainparams,
+ /* m_accept_time */ accept_time,
+ /* m_bypass_limits */ false,
+ /* m_coins_to_uncache */ coins_to_uncache,
+ /* m_test_accept */ true,
+ /* m_allow_bip125_replacement */ false,
+ };
+ }
+
+ // No default ctor to avoid exposing details to clients and allowing the possibility of
+ // mixing up the order of the arguments. Use static functions above instead.
+ ATMPArgs() = delete;
};
// Single transaction acceptance
@@ -468,13 +497,29 @@ private:
// of checking a given transaction.
struct Workspace {
explicit Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {}
+ /** Txids of mempool transactions that this transaction directly conflicts with. */
std::set<uint256> m_conflicts;
+ /** Iterators to mempool entries that this transaction directly conflicts with. */
+ CTxMemPool::setEntries m_iters_conflicting;
+ /** Iterators to all mempool entries that would be replaced by this transaction, including
+ * those it directly conflicts with and their descendants. */
CTxMemPool::setEntries m_all_conflicting;
+ /** All mempool ancestors of this transaction. */
CTxMemPool::setEntries m_ancestors;
+ /** Mempool entry constructed for this transaction. Constructed in PreChecks() but not
+ * inserted into the mempool until Finalize(). */
std::unique_ptr<CTxMemPoolEntry> m_entry;
+ /** Pointers to the transactions that have been removed from the mempool and replaced by
+ * this transaction, used to return to the MemPoolAccept caller. Only populated if
+ * validation is successful and the original transactions are removed. */
std::list<CTransactionRef> m_replaced_transactions;
+ /** Virtual size of the transaction as used by the mempool, calculated using serialized size
+ * of the transaction and sigops. */
+ int64_t m_vsize;
+ /** Fees paid by this transaction: total input amounts subtracted by total output amounts. */
CAmount m_base_fees;
+ /** Base fees + any fee delta set by the user with prioritisetransaction. */
CAmount m_modified_fees;
/** Total modified fees of all transactions being replaced. */
CAmount m_conflicting_fees{0};
@@ -482,8 +527,12 @@ private:
size_t m_conflicting_size{0};
const CTransactionRef& m_ptx;
+ /** Txid. */
const uint256& m_hash;
TxValidationState m_state;
+ /** A temporary cache containing serialized transaction data for signature verification.
+ * Reused across PolicyScriptChecks and ConsensusScriptChecks. */
+ PrecomputedTransactionData m_precomputed_txdata;
};
// Run the policy checks on a given transaction, excluding any script checks.
@@ -492,15 +541,23 @@ private:
// only tests that are fast should be done here (to avoid CPU DoS).
bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ // Run checks for mempool replace-by-fee.
+ bool ReplacementChecks(Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+
+ // Enforce package mempool ancestor/descendant limits (distinct from individual
+ // ancestor/descendant limits done in PreChecks).
+ bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
+ PackageValidationState& package_state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+
// Run the script checks using our policy flags. As this can be slow, we should
// only invoke this on transactions that have otherwise passed policy checks.
- bool PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool PolicyScriptChecks(const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Re-run the script checks, using consensus flags, and try to cache the
// result in the scriptcache. This should be done after
// PolicyScriptChecks(). This requires that all inputs either be in our
// utxo set or in the mempool.
- bool ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Try to add the transaction to the mempool, removing any conflicts first.
// Returns true if the transaction is in the mempool after any size
@@ -536,6 +593,9 @@ private:
// in-mempool conflicts; see below).
size_t m_limit_descendants;
size_t m_limit_descendant_size;
+
+ /** Whether the transaction(s) would replace any mempool transactions. If so, RBF rules apply. */
+ bool m_rbf{false};
};
bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
@@ -551,13 +611,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Alias what we need out of ws
TxValidationState& state = ws.m_state;
- std::set<uint256>& setConflicts = ws.m_conflicts;
- CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
- CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
- CAmount& nModifiedFees = ws.m_modified_fees;
- CAmount& nConflictingFees = ws.m_conflicting_fees;
- size_t& nConflictingSize = ws.m_conflicting_size;
if (!CheckTransaction(tx, state)) {
return false; // state filled in by CheckTransaction
@@ -585,10 +639,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
- if (m_pool.exists(GenTxid(true, tx.GetWitnessHash()))) {
+ if (m_pool.exists(GenTxid::Wtxid(tx.GetWitnessHash()))) {
// Exact transaction already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-in-mempool");
- } else if (m_pool.exists(GenTxid(false, tx.GetHash()))) {
+ } else if (m_pool.exists(GenTxid::Txid(tx.GetHash()))) {
// Transaction with the same non-witness data but different witness (same txid, different
// wtxid) already exists in the mempool.
return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-same-nonwitness-data-in-mempool");
@@ -603,7 +657,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Transaction conflicts with a mempool tx, but we're not allowing replacements.
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
}
- if (!setConflicts.count(ptxConflicting->GetHash()))
+ if (!ws.m_conflicts.count(ptxConflicting->GetHash()))
{
// Transactions that don't explicitly signal replaceability are
// *not* replaceable with the current logic, even if one of their
@@ -616,7 +670,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
}
- setConflicts.insert(ptxConflicting->GetHash());
+ ws.m_conflicts.insert(ptxConflicting->GetHash());
}
}
}
@@ -680,9 +734,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
- // nModifiedFees includes any fee deltas from PrioritiseTransaction
- nModifiedFees = ws.m_base_fees;
- m_pool.ApplyDelta(hash, nModifiedFees);
+ // ws.m_modified_fees includes any fee deltas from PrioritiseTransaction
+ ws.m_modified_fees = ws.m_base_fees;
+ m_pool.ApplyDelta(hash, ws.m_modified_fees);
// Keep track of transactions that spend a coinbase, which we re-scan
// during reorgs to ensure COINBASE_MATURITY is still met.
@@ -697,7 +751,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
fSpendsCoinbase, nSigOpsCost, lp));
- unsigned int nSize = entry->GetTxSize();
+ ws.m_vsize = entry->GetTxSize();
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
@@ -705,11 +759,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// No transactions are allowed below minRelayTxFee except from disconnected
// blocks
- if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false;
+ if (!bypass_limits && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
- const CTxMemPool::setEntries setIterConflicting = m_pool.GetIterSet(setConflicts);
+ ws.m_iters_conflicting = m_pool.GetIterSet(ws.m_conflicts);
// Calculate in-mempool ancestors, up to a limit.
- if (setConflicts.size() == 1) {
+ if (ws.m_conflicts.size() == 1) {
// In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we
// would meet the chain limits after the conflicts have been removed. However, there isn't a practical
// way to do this short of calculating the ancestor and descendant sets with an overlay cache of
@@ -737,16 +791,16 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// the ancestor limits should be the same for both our new transaction and any conflicts).
// We don't bother incrementing m_limit_descendants by the full removal count as that limit never comes
// into force here (as we're only adding a single transaction).
- assert(setIterConflicting.size() == 1);
- CTxMemPool::txiter conflict = *setIterConflicting.begin();
+ assert(ws.m_iters_conflicting.size() == 1);
+ CTxMemPool::txiter conflict = *ws.m_iters_conflicting.begin();
m_limit_descendants += 1;
m_limit_descendant_size += conflict->GetSizeWithDescendants();
}
std::string errString;
- if (!m_pool.CalculateMemPoolAncestors(*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
- setAncestors.clear();
+ if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
+ ws.m_ancestors.clear();
// If CalculateMemPoolAncestors fails second time, we want the original error string.
std::string dummy_err_string;
// Contracting/payment channels CPFP carve-out:
@@ -760,60 +814,85 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// to be secure by simply only having two immediately-spendable
// outputs - one for each counterparty. For more info on the uses for
// this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
- if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
- !m_pool.CalculateMemPoolAncestors(*entry, setAncestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
+ if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
+ !m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", errString);
}
}
// A transaction that spends outputs that would be replaced by it is invalid. Now
// that we have the set of all ancestors we can detect this
- // pathological case by making sure setConflicts and setAncestors don't
+ // pathological case by making sure ws.m_conflicts and ws.m_ancestors don't
// intersect.
- if (const auto err_string{EntriesAndTxidsDisjoint(setAncestors, setConflicts, hash)}) {
+ if (const auto err_string{EntriesAndTxidsDisjoint(ws.m_ancestors, ws.m_conflicts, hash)}) {
// We classify this as a consensus error because a transaction depending on something it
// conflicts with would be inconsistent.
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
}
+ m_rbf = !ws.m_conflicts.empty();
+ return true;
+}
- if (!setConflicts.empty()) {
- CFeeRate newFeeRate(nModifiedFees, nSize);
- // It's possible that the replacement pays more fees than its direct conflicts but not more
- // than all conflicts (i.e. the direct conflicts have high-fee descendants). However, if the
- // replacement doesn't pay more fees than its direct conflicts, then we can be sure it's not
- // more economically rational to mine. Before we go digging through the mempool for all
- // transactions that would need to be removed (direct conflicts and all descendants), check
- // that the replacement transaction pays more than its direct conflicts.
- if (const auto err_string{PaysMoreThanConflicts(setIterConflicting, newFeeRate, hash)}) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
- }
+bool MemPoolAccept::ReplacementChecks(Workspace& ws)
+{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_pool.cs);
- // Calculate all conflicting entries and enforce BIP125 Rule #5.
- if (const auto err_string{GetEntriesForConflicts(tx, m_pool, setIterConflicting, allConflicting)}) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
- "too many potential replacements", *err_string);
- }
- // Enforce BIP125 Rule #2.
- if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, setIterConflicting)}) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
- "replacement-adds-unconfirmed", *err_string);
- }
+ const CTransaction& tx = *ws.m_ptx;
+ const uint256& hash = ws.m_hash;
+ TxValidationState& state = ws.m_state;
- // Check if it's economically rational to mine this transaction rather than the ones it
- // replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
- for (CTxMemPool::txiter it : allConflicting) {
- nConflictingFees += it->GetModifiedFee();
- nConflictingSize += it->GetTxSize();
- }
- if (const auto err_string{PaysForRBF(nConflictingFees, nModifiedFees, nSize, ::incrementalRelayFee, hash)}) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
- }
+ CFeeRate newFeeRate(ws.m_modified_fees, ws.m_vsize);
+ // It's possible that the replacement pays more fees than its direct conflicts but not more
+ // than all conflicts (i.e. the direct conflicts have high-fee descendants). However, if the
+ // replacement doesn't pay more fees than its direct conflicts, then we can be sure it's not
+ // more economically rational to mine. Before we go digging through the mempool for all
+ // transactions that would need to be removed (direct conflicts and all descendants), check
+ // that the replacement transaction pays more than its direct conflicts.
+ if (const auto err_string{PaysMoreThanConflicts(ws.m_iters_conflicting, newFeeRate, hash)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
+ }
+
+ // Calculate all conflicting entries and enforce BIP125 Rule #5.
+ if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, ws.m_all_conflicting)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
+ "too many potential replacements", *err_string);
+ }
+ // Enforce BIP125 Rule #2.
+ if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, ws.m_iters_conflicting)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
+ "replacement-adds-unconfirmed", *err_string);
+ }
+ // Check if it's economically rational to mine this transaction rather than the ones it
+ // replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
+ for (CTxMemPool::txiter it : ws.m_all_conflicting) {
+ ws.m_conflicting_fees += it->GetModifiedFee();
+ ws.m_conflicting_size += it->GetTxSize();
+ }
+ if (const auto err_string{PaysForRBF(ws.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,
+ ::incrementalRelayFee, hash)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
return true;
}
-bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
+bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
+ PackageValidationState& package_state)
+{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_pool.cs);
+
+ std::string err_string;
+ if (!m_pool.CheckPackageLimits(txns, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants,
+ m_limit_descendant_size, err_string)) {
+ // This is a package-wide error, separate from an individual transaction error.
+ return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
+ }
+ return true;
+}
+
+bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
{
const CTransaction& tx = *ws.m_ptx;
TxValidationState& state = ws.m_state;
@@ -822,13 +901,13 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, Prec
// Check input scripts and signatures.
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, txdata)) {
+ if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata)) {
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation.
TxValidationState state_dummy; // Want reported failures to be from first CheckInputScripts
- if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
- !CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
+ if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata) &&
+ !CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
state.GetRejectReason(), state.GetDebugMessage());
@@ -839,7 +918,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, Prec
return true;
}
-bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
+bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
{
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
@@ -862,7 +941,8 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, P
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
- if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
+ if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
+ ws.m_precomputed_txdata, m_active_chainstate.CoinsTip())) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), state.ToString());
}
@@ -877,24 +957,19 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
TxValidationState& state = ws.m_state;
const bool bypass_limits = args.m_bypass_limits;
- CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
- CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
- const CAmount& nModifiedFees = ws.m_modified_fees;
- const CAmount& nConflictingFees = ws.m_conflicting_fees;
- const size_t& nConflictingSize = ws.m_conflicting_size;
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
// Remove conflicting transactions from the mempool
- for (CTxMemPool::txiter it : allConflicting)
+ for (CTxMemPool::txiter it : ws.m_all_conflicting)
{
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s additional fees, %d delta bytes\n",
it->GetTx().GetHash().ToString(),
hash.ToString(),
- FormatMoney(nModifiedFees - nConflictingFees),
- (int)entry->GetTxSize() - (int)nConflictingSize);
+ FormatMoney(ws.m_modified_fees - ws.m_conflicting_fees),
+ (int)entry->GetTxSize() - (int)ws.m_conflicting_size);
ws.m_replaced_transactions.push_back(it->GetSharedTx());
}
- m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
+ m_pool.RemoveStaged(ws.m_all_conflicting, false, MemPoolRemovalReason::REPLACED);
// This transaction should only count for fee estimation if:
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
@@ -903,12 +978,12 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
bool validForFeeEstimation = !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
// Store transaction in memory
- m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation);
+ m_pool.addUnchecked(*entry, ws.m_ancestors, validForFeeEstimation);
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
- if (!m_pool.exists(hash))
+ if (!m_pool.exists(GenTxid::Txid(hash)))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
}
return true;
@@ -923,26 +998,24 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
if (!PreChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
- // Only compute the precomputed transaction data if we need to verify
- // scripts (ie, other policy checks pass). We perform the inexpensive
- // checks first and avoid hashing and signature verification unless those
- // checks pass, to mitigate CPU exhaustion denial-of-service attacks.
- PrecomputedTransactionData txdata;
+ if (m_rbf && !ReplacementChecks(ws)) return MempoolAcceptResult::Failure(ws.m_state);
- if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult::Failure(ws.m_state);
+ // Perform the inexpensive checks first and avoid hashing and signature verification unless
+ // those checks pass, to mitigate CPU exhaustion denial-of-service attacks.
+ if (!PolicyScriptChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
- if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult::Failure(ws.m_state);
+ if (!ConsensusScriptChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
// Tx was accepted, but not added
if (args.m_test_accept) {
- return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+ return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize, ws.m_base_fees);
}
if (!Finalize(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
- return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+ return MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize, ws.m_base_fees);
}
PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args)
@@ -981,18 +1054,12 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
// because it's unnecessary. Also, CPFP carve out can increase the limit for individual
// transactions, but this exemption is not extended to packages in CheckPackageLimits().
std::string err_string;
- if (txns.size() > 1 &&
- !m_pool.CheckPackageLimits(txns, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants,
- m_limit_descendant_size, err_string)) {
- // All transactions must have individually passed mempool ancestor and descendant limits
- // inside of PreChecks(), so this is separate from an individual transaction error.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
+ if (txns.size() > 1 && !PackageMempoolChecks(txns, package_state)) {
return PackageMempoolAcceptResult(package_state, std::move(results));
}
for (Workspace& ws : workspaces) {
- PrecomputedTransactionData txdata;
- if (!PolicyScriptChecks(args, ws, txdata)) {
+ if (!PolicyScriptChecks(args, ws)) {
// Exit early to avoid doing pointless work. Update the failed tx result; the rest are unfinished.
package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
@@ -1002,7 +1069,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
// When test_accept=true, transactions that pass PolicyScriptChecks are valid because there are
// no further mempool checks (passing PolicyScriptChecks implies passing ConsensusScriptChecks).
results.emplace(ws.m_ptx->GetWitnessHash(),
- MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_base_fees));
+ MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions),
+ ws.m_vsize, ws.m_base_fees));
}
}
@@ -1019,9 +1087,7 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache,
- test_accept, /* m_allow_bip125_replacement */ true };
-
+ auto args = MemPoolAccept::ATMPArgs::SingleAccept(chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept);
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// Remove coins that were not present in the coins cache before calling
@@ -1054,8 +1120,7 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
std::vector<COutPoint> coins_to_uncache;
const CChainParams& chainparams = Params();
- MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
- test_accept, /* m_allow_bip125_replacement */ false };
+ auto args = MemPoolAccept::ATMPArgs::PackageTestAccept(chainparams, GetTime(), coins_to_uncache);
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
// Uncache coins pertaining to transactions that were not submitted to the mempool.
@@ -1236,12 +1301,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
AddCoins(inputs, tx, nHeight);
}
-void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
-{
- CTxUndo txundo;
- UpdateCoins(tx, inputs, txundo, nHeight);
-}
-
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
@@ -1664,8 +1723,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// can be duplicated to remove the ability to spend the first instance -- even after
// being sent to another address.
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
- // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
- // already refuses previously-known transaction ids entirely.
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
@@ -2487,7 +2544,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
// any disconnected transactions back to the mempool.
MaybeUpdateMempoolForReorg(disconnectpool, true);
}
- if (m_mempool) m_mempool->check(*this);
+ if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);
CheckForkWarningConditions();
@@ -3429,6 +3486,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
return true;
}
+MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef& tx, bool test_accept)
+{
+ CChainState& active_chainstate = ActiveChainstate();
+ if (!active_chainstate.m_mempool) {
+ TxValidationState state;
+ state.Invalid(TxValidationResult::TX_NO_MEMPOOL, "no-mempool");
+ return MempoolAcceptResult::Failure(state);
+ }
+ auto result = AcceptToMemoryPool(active_chainstate, *active_chainstate.m_mempool, tx, /*bypass_limits=*/ false, test_accept);
+ active_chainstate.m_mempool->check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
+ return result;
+}
+
bool TestBlockValidity(BlockValidationState& state,
const CChainParams& chainparams,
CChainState& chainstate,
@@ -4500,7 +4570,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
// wallet(s) having loaded it while we were processing
// mempool transactions; consider these as valid, instead of
// failed, but mark them as 'already there'
- if (pool.exists(tx->GetHash())) {
+ if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
++already_there;
} else {
++failed;
diff --git a/src/validation.h b/src/validation.h
index 1c23b3ad5c..21cd389757 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -158,14 +158,16 @@ struct MempoolAcceptResult {
// The following fields are only present when m_result_type = ResultType::VALID
/** Mempool transactions replaced by the tx per BIP 125 rules. */
const std::optional<std::list<CTransactionRef>> m_replaced_transactions;
+ /** Virtual size as used by the mempool, calculated using serialized size and sigops. */
+ const std::optional<int64_t> m_vsize;
/** Raw base fees in satoshis. */
const std::optional<CAmount> m_base_fees;
static MempoolAcceptResult Failure(TxValidationState state) {
return MempoolAcceptResult(state);
}
- static MempoolAcceptResult Success(std::list<CTransactionRef>&& replaced_txns, CAmount fees) {
- return MempoolAcceptResult(std::move(replaced_txns), fees);
+ static MempoolAcceptResult Success(std::list<CTransactionRef>&& replaced_txns, int64_t vsize, CAmount fees) {
+ return MempoolAcceptResult(std::move(replaced_txns), vsize, fees);
}
// Private constructors. Use static methods MempoolAcceptResult::Success, etc. to construct.
@@ -177,9 +179,9 @@ private:
}
/** Constructor for success case */
- explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, CAmount fees)
+ explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, int64_t vsize, CAmount fees)
: m_result_type(ResultType::VALID),
- m_replaced_transactions(std::move(replaced_txns)), m_base_fees(fees) {}
+ m_replaced_transactions(std::move(replaced_txns)), m_vsize{vsize}, m_base_fees(fees) {}
};
/**
@@ -206,9 +208,16 @@ struct PackageMempoolAcceptResult
};
/**
- * (Try to) add a transaction to the memory pool.
- * @param[in] bypass_limits When true, don't enforce mempool fee limits.
- * @param[in] test_accept When true, run validation checks but don't submit to mempool.
+ * Try to add a transaction to the mempool. This is an internal function and is
+ * exposed only for testing. Client code should use ChainstateManager::ProcessTransaction()
+ *
+ * @param[in] active_chainstate Reference to the active chainstate.
+ * @param[in] pool Reference to the node's mempool.
+ * @param[in] tx The transaction to submit for mempool acceptance.
+ * @param[in] bypass_limits When true, don't enforce mempool fee and capacity limits.
+ * @param[in] test_accept When true, run validation checks but don't submit to mempool.
+ *
+ * @returns a MempoolAcceptResult indicating whether the transaction was accepted/rejected with reason.
*/
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -229,9 +238,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
const Package& txns, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Apply the effects of this transaction on the UTXO set represented by view */
-void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
-
/** Transaction validation functions */
/**
@@ -997,6 +1003,15 @@ public:
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
+ /**
+ * Try to add a transaction to the memory pool.
+ *
+ * @param[in] tx The transaction to submit for mempool acceptance.
+ * @param[in] test_accept When true, run validation checks but don't submit to mempool.
+ */
+ [[nodiscard]] MempoolAcceptResult ProcessTransaction(const CTransactionRef& tx, bool test_accept=false)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 2290e119fd..74fc10ab25 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -132,7 +132,7 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
fs::path pathIn = fs::PathFromString(strPath);
TryCreateDirectories(pathIn);
if (!LockDirectory(pathIn, ".walletlock")) {
- LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
+ LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
return false;
}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index b666a8e73a..7d0f80518a 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -113,7 +113,7 @@ public:
*/
bool Rewrite(const char* pszSkip=nullptr) override;
- /** Indicate the a new database user has began using the database. */
+ /** Indicate that a new database user has begun using the database. */
void AddRef() override;
/** Indicate that database user has stopped using the database and that it could be flushed or closed. */
void RemoveRef() override;
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index c74c69ed09..305f6c31fc 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -12,7 +12,6 @@
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
{
- const size_t offset = wallet_dir.native().size() + (wallet_dir == wallet_dir.root_name() ? 0 : 1);
std::vector<fs::path> paths;
boost::system::error_code ec;
@@ -28,10 +27,7 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
}
try {
- // Get wallet path relative to walletdir by removing walletdir from the wallet path.
- // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
- const auto path_str = it->path().native().substr(offset);
- const fs::path path{path_str.begin(), path_str.end()};
+ const fs::path path{it->path().lexically_relative(wallet_dir)};
if (it->status().type() == fs::directory_file &&
(IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) {
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index 08d94b76d9..e50b4ca5f7 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -191,7 +191,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
// dummy chain interface
bool ret = true;
- std::shared_ptr<CWallet> wallet(new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet(new CWallet(nullptr /* chain */, name, gArgs, std::move(database)), WalletToolReleaseWallet);
{
LOCK(wallet->cs_wallet);
DBErrors load_wallet_ret = wallet->LoadWallet();
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 59a59f9794..7a5526a4cb 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -84,7 +84,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
#endif
#ifdef USE_SQLITE
- argsman.AddArg("-unsafesqlitesync", "Set SQLite synchronous=OFF to disable waiting for the database to sync to disk. This is unsafe and can cause data loss and corruption. This option is only used by tests to improve their performance (default: false)", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+ argsman.AddArg("-unsafesqlitesync", "Set SQLite synchronous=OFF to disable waiting for the database to sync to disk. This is unsafe and can cause data loss and corruption. This option is only used by tests to improve their performance (default: false)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
#else
argsman.AddHiddenArgs({"-unsafesqlitesync"});
#endif
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 9b09bc23d6..403c978680 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -57,7 +57,7 @@ static std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWallet& wallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+static bool GetWalletAddressesForKey(const LegacyScriptPubKeyMan* spk_man, const CWallet& wallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
bool fLabelFound = false;
CKey key;
@@ -681,10 +681,10 @@ RPCHelpMan dumpprivkey()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
- LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
+ const LegacyScriptPubKeyMan& spk_man = EnsureConstLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
@@ -731,11 +731,11 @@ RPCHelpMan dumpwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
- CWallet& wallet = *pwallet;
- LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(wallet);
+ const CWallet& wallet = *pwallet;
+ const LegacyScriptPubKeyMan& spk_man = EnsureConstLegacyScriptPubKeyMan(wallet);
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -797,7 +797,7 @@ RPCHelpMan dumpwallet()
CKey seed;
if (spk_man.GetKey(seed_id, seed)) {
CExtKey masterKey;
- masterKey.SetSeed(seed.begin(), seed.size());
+ masterKey.SetSeed(seed);
file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
}
@@ -809,6 +809,9 @@ RPCHelpMan dumpwallet()
std::string strLabel;
CKey key;
if (spk_man.GetKey(keyid, key)) {
+ CKeyMetadata metadata;
+ const auto it{spk_man.mapKeyMetadata.find(keyid)};
+ if (it != spk_man.mapKeyMetadata.end()) metadata = it->second;
file << strprintf("%s %s ", EncodeSecret(key), strTime);
if (GetWalletAddressesForKey(&spk_man, wallet, keyid, strAddr, strLabel)) {
file << strprintf("label=%s", strLabel);
@@ -816,12 +819,12 @@ RPCHelpMan dumpwallet()
file << "hdseed=1";
} else if (mapKeyPool.count(keyid)) {
file << "reserve=1";
- } else if (spk_man.mapKeyMetadata[keyid].hdKeypath == "s") {
+ } else if (metadata.hdKeypath == "s") {
file << "inactivehdseed=1";
} else {
file << "change=1";
}
- file << strprintf(" # addr=%s%s\n", strAddr, (spk_man.mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(spk_man.mapKeyMetadata[keyid].key_origin.path) : ""));
+ file << strprintf(" # addr=%s%s\n", strAddr, (metadata.has_key_origin ? " hdkeypath="+WriteHDKeypath(metadata.key_origin.path) : ""));
}
}
file << "\n";
@@ -1788,7 +1791,7 @@ RPCHelpMan listdescriptors()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 6959466d1d..babb61b03a 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -103,7 +103,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
- std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
+ const std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
return pwallet;
}
@@ -150,6 +150,15 @@ LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_cr
return *spk_man;
}
+const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
+{
+ const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
+ if (!spk_man) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
+ }
+ return *spk_man;
+}
+
static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
{
interfaces::Chain& chain = wallet.chain();
@@ -570,7 +579,7 @@ static RPCHelpMan listaddressgroupings()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -627,7 +636,7 @@ static RPCHelpMan signmessage()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -729,7 +738,7 @@ static RPCHelpMan getreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -767,7 +776,7 @@ static RPCHelpMan getreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -807,7 +816,7 @@ static RPCHelpMan getbalance()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -846,7 +855,7 @@ static RPCHelpMan getunconfirmedbalance()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1234,7 +1243,7 @@ static RPCHelpMan listreceivedbyaddress()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1276,7 +1285,7 @@ static RPCHelpMan listreceivedbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1389,8 +1398,9 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{
return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
"transaction conflicted that many blocks ago."},
- {RPCResult::Type::BOOL, "generated", /* optional */ true, "Only present if transaction only input is a coinbase one."},
- {RPCResult::Type::BOOL, "trusted", /* optional */ true, "Only present if we consider transaction to be trusted and so safe to spend from."},
+ {RPCResult::Type::BOOL, "generated", /* optional */ true, "Only present if the transaction's only input is a coinbase one."},
+ {RPCResult::Type::BOOL, "trusted", /* optional */ true, "Whether we consider the transaction to be trusted and safe to spend from.\n"
+ "Only present when the transaction has 0 confirmations (or negative confirmations, if conflicted)."},
{RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash containing the transaction."},
{RPCResult::Type::NUM, "blockheight", /* optional */ true, "The block height containing the transaction."},
{RPCResult::Type::NUM, "blockindex", /* optional */ true, "The index of the transaction in the block that includes it."},
@@ -1408,7 +1418,7 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "comment", /* optional */ true, "If a comment is associated with the transaction, only present if not empty."},
{RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
- "may be unknown for unconfirmed transactions not in the mempool"}};
+ "may be unknown for unconfirmed transactions not in the mempool."}};
}
static RPCHelpMan listtransactions()
@@ -1460,7 +1470,7 @@ static RPCHelpMan listtransactions()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1576,7 +1586,7 @@ static RPCHelpMan listsinceblock()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
const CWallet& wallet = *pwallet;
@@ -1717,7 +1727,7 @@ static RPCHelpMan gettransaction()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -1828,7 +1838,7 @@ static RPCHelpMan backupwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -2330,7 +2340,7 @@ static RPCHelpMan listlockunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -2423,9 +2433,9 @@ static RPCHelpMan getbalances()
HelpExampleRpc("getbalances", "")},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
if (!rpc_wallet) return NullUniValue;
- CWallet& wallet = *rpc_wallet;
+ const CWallet& wallet = *rpc_wallet;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -2499,7 +2509,7 @@ static RPCHelpMan getwalletinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
// Make sure the results are valid at least up to the most recent block
@@ -2770,7 +2780,7 @@ static RPCHelpMan createwallet()
{"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
- {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
+ {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation"},
{"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
{"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
},
@@ -2812,12 +2822,11 @@ static RPCHelpMan createwallet()
if (!request.params[4].isNull() && request.params[4].get_bool()) {
flags |= WALLET_FLAG_AVOID_REUSE;
}
- if (!request.params[5].isNull() && request.params[5].get_bool()) {
+ if (request.params[5].isNull() || request.params[5].get_bool()) {
#ifndef USE_SQLITE
throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)");
#endif
flags |= WALLET_FLAG_DESCRIPTORS;
- warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
}
if (!request.params[7].isNull() && request.params[7].get_bool()) {
#ifdef ENABLE_EXTERNAL_SIGNER
@@ -2840,7 +2849,7 @@ static RPCHelpMan createwallet()
options.create_passphrase = passphrase;
bilingual_str error;
std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
- std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
+ const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
if (!wallet) {
RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
throw JSONRPCError(code, error.original);
@@ -3030,7 +3039,7 @@ static RPCHelpMan listunspent()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
int nMinDepth = 1;
@@ -3593,7 +3602,7 @@ RPCHelpMan signrawtransactionwithwallet()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
@@ -4058,7 +4067,7 @@ RPCHelpMan getaddressinfo()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4165,7 +4174,7 @@ static RPCHelpMan getaddressesbylabel()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4226,7 +4235,7 @@ static RPCHelpMan listlabels()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
LOCK(pwallet->cs_wallet);
@@ -4555,7 +4564,7 @@ static RPCHelpMan walletprocesspsbt()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
+ const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
const CWallet& wallet{*pwallet};
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 8b88ffe8ed..3a85fc0c64 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -34,6 +34,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
void EnsureWalletIsUnlocked(const CWallet&);
WalletContext& EnsureWalletContext(const std::any& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
+const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet);
RPCHelpMan getaddressinfo();
RPCHelpMan signrawtransactionwithwallet();
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index 4151099c1f..241d77c9de 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -133,7 +133,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
}
DbTxn* ptxn = env->TxnBegin();
- CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
+ CWallet dummyWallet(nullptr, "", gArgs, CreateDummyWalletDatabase());
for (KeyValPair& row : salvagedData)
{
/* Filter for only private key type KV pairs to be added to the salvaged wallet */
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 619ebc8b4f..9173c790d4 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -400,7 +400,7 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
CKey key;
GetKey(meta.hd_seed_id, key);
CExtKey masterKey;
- masterKey.SetSeed(key.begin(), key.size());
+ masterKey.SetSeed(key);
// Add to map
CKeyID master_id = masterKey.key.GetPubKey().GetID();
std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint);
@@ -1085,7 +1085,7 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
if (!GetKey(hd_chain.seed_id, seed))
throw std::runtime_error(std::string(__func__) + ": seed not found");
- masterKey.SetSeed(seed.begin(), seed.size());
+ masterKey.SetSeed(seed);
// derive m/0'
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 650e083e8e..c493b96248 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -228,7 +228,7 @@ void SQLiteDatabase::Open()
// Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode.
int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
- throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?\n");
+ throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of " PACKAGE_NAME "?\n");
}
ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index f80c4637b8..a8a7e12a32 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -28,19 +28,9 @@ BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup)
typedef std::set<CInputCoin> CoinSet;
-static std::vector<COutput> vCoins;
-static NodeContext testNode;
-static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
-static CAmount balance = 0;
-
-CoinEligibilityFilter filter_standard(1, 6, 0);
-CoinEligibilityFilter filter_confirmed(1, 1, 0);
-CoinEligibilityFilter filter_standard_extra(6, 6, 0);
-CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
- /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
- /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
+static const CoinEligibilityFilter filter_standard(1, 6, 0);
+static const CoinEligibilityFilter filter_confirmed(1, 1, 0);
+static const CoinEligibilityFilter filter_standard_extra(6, 6, 0);
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
{
@@ -62,9 +52,8 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set, CAmount fe
set.insert(coin);
}
-static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
- balance += nValue;
static int nextLockTime = 0;
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
@@ -82,24 +71,19 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- CWalletTx* wtx = wallet.AddToWallet(MakeTransactionRef(std::move(tx)), /* confirm= */ {});
+ uint256 txid = tx.GetHash();
+
+ LOCK(wallet.cs_wallet);
+ auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx))));
+ assert(ret.second);
+ CWalletTx& wtx = (*ret.first).second;
if (fIsFromMe)
{
- wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
- wtx->m_is_cache_empty = false;
+ wtx.m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
+ wtx.m_is_cache_empty = false;
}
- COutput output(wallet, *wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
- vCoins.push_back(output);
-}
-static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
-{
- add_coin(testWallet, nValue, nAge, fIsFromMe, nInput, spendable);
-}
-
-static void empty_wallet(void)
-{
- vCoins.clear();
- balance = 0;
+ COutput output(wallet, wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
+ coins.push_back(output);
}
static bool equal_sets(CoinSet a, CoinSet b)
@@ -142,20 +126,20 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
return static_groups;
}
-inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinEligibilityFilter& filter)
+inline std::vector<OutputGroup>& KnapsackGroupOutputs(const std::vector<COutput>& coins, CWallet& wallet, const CoinEligibilityFilter& filter)
{
+ CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
+ /* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
+ /* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
static std::vector<OutputGroup> static_groups;
- static_groups = GroupOutputs(testWallet, vCoins, coin_selection_params, filter, /* positive_only */false);
+ static_groups = GroupOutputs(wallet, coins, coin_selection_params, filter, /* positive_only */false);
return static_groups;
}
// Branch and bound coin selection tests
BOOST_AUTO_TEST_CASE(bnb_search_test)
{
-
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
-
// Setup
std::vector<CInputCoin> utxo_pool;
CoinSet selection;
@@ -288,196 +272,213 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
/* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
- CoinSet setCoinsRet;
- CAmount nValueRet;
- empty_wallet();
- add_coin(1);
- vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
- BOOST_CHECK(!SelectCoinsBnB(GroupCoins(vCoins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
-
- // Test fees subtracted from output:
- empty_wallet();
- add_coin(1 * CENT);
- vCoins.at(0).nInputBytes = 40;
- coin_selection_params_bnb.m_subtract_fee_outputs = true;
- BOOST_CHECK(SelectCoinsBnB(GroupCoins(vCoins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
- BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
-
- // Make sure that can use BnB when there are preset inputs
- empty_wallet();
{
- std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
wallet->LoadWallet();
- wallet->SetupLegacyScriptPubKeyMan();
LOCK(wallet->cs_wallet);
- add_coin(*wallet, 5 * CENT, 6 * 24, false, 0, true);
- add_coin(*wallet, 3 * CENT, 6 * 24, false, 0, true);
- add_coin(*wallet, 2 * CENT, 6 * 24, false, 0, true);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ std::vector<COutput> coins;
+ CoinSet setCoinsRet;
+ CAmount nValueRet;
+
+ add_coin(coins, *wallet, 1);
+ coins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
+
+ // Test fees subtracted from output:
+ coins.clear();
+ add_coin(coins, *wallet, 1 * CENT);
+ coins.at(0).nInputBytes = 40;
+ coin_selection_params_bnb.m_subtract_fee_outputs = true;
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(coins), 1 * CENT, coin_selection_params_bnb.m_cost_of_change, setCoinsRet, nValueRet));
+ BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
+ }
+
+ {
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ std::vector<COutput> coins;
+ CoinSet setCoinsRet;
+ CAmount nValueRet;
+
+ add_coin(coins, *wallet, 5 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 3 * CENT, 6 * 24, false, 0, true);
+ add_coin(coins, *wallet, 2 * CENT, 6 * 24, false, 0, true);
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
- coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
+ coin_control.Select(COutPoint(coins.at(0).tx->GetHash(), coins.at(0).i));
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
- BOOST_CHECK(SelectCoins(*wallet, vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
+ BOOST_CHECK(SelectCoins(*wallet, coins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
}
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
{
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
CoinSet setCoinsRet, setCoinsRet2;
CAmount nValueRet;
-
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
+ std::vector<COutput> coins;
// test multiple times to allow for differences in the shuffle order
for (int i = 0; i < RUN_TESTS; i++)
{
- empty_wallet();
+ coins.clear();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
- add_coin(1*CENT, 4); // add a new 1 cent coin
+ add_coin(coins, *wallet, 1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(1 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
// but we can find a new 1 cent
- BOOST_CHECK(KnapsackSolver(1 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
- add_coin(2*CENT); // add a mature 2 cent coin
+ add_coin(coins, *wallet, 2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!KnapsackSolver(3 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(3 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
// we can make 3 cents of new coins
- BOOST_CHECK(KnapsackSolver(3 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(3 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
- add_coin(5*CENT); // add a mature 5 cent coin,
- add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
- add_coin(20*CENT); // and a mature 20 cent coin
+ add_coin(coins, *wallet, 5*CENT); // add a mature 5 cent coin,
+ add_coin(coins, *wallet, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
+ add_coin(coins, *wallet, 20*CENT); // and a mature 20 cent coin
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(filter_standard_extra), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(38 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard_extra), setCoinsRet, nValueRet));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK(KnapsackSolver(37 * CENT, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(37 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK(KnapsackSolver(38 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(38 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK(KnapsackSolver(34 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(34 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK(KnapsackSolver(7 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(7 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK(KnapsackSolver(8 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(8 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK(KnapsackSolver(9 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(9 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
- empty_wallet();
+ coins.clear();
- add_coin( 6*CENT);
- add_coin( 7*CENT);
- add_coin( 8*CENT);
- add_coin(20*CENT);
- add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
+ add_coin(coins, *wallet, 6*CENT);
+ add_coin(coins, *wallet, 7*CENT);
+ add_coin(coins, *wallet, 8*CENT);
+ add_coin(coins, *wallet, 20*CENT);
+ add_coin(coins, *wallet, 30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK(KnapsackSolver(71 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
- BOOST_CHECK(!KnapsackSolver(72 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(71 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(!KnapsackSolver(72 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
+ add_coin(coins, *wallet, 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
- add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
+ add_coin(coins, *wallet, 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(16 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK(KnapsackSolver(11 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(11 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// check that the smallest bigger coin is used
- add_coin( 1*COIN);
- add_coin( 2*COIN);
- add_coin( 3*COIN);
- add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK(KnapsackSolver(95 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ add_coin(coins, *wallet, 1*COIN);
+ add_coin(coins, *wallet, 2*COIN);
+ add_coin(coins, *wallet, 3*COIN);
+ add_coin(coins, *wallet, 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
+ BOOST_CHECK(KnapsackSolver(95 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK(KnapsackSolver(195 * CENT, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(195 * CENT, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// empty the wallet and start again, now with fractions of a cent, to test small change avoidance
- empty_wallet();
- add_coin(MIN_CHANGE * 1 / 10);
- add_coin(MIN_CHANGE * 2 / 10);
- add_coin(MIN_CHANGE * 3 / 10);
- add_coin(MIN_CHANGE * 4 / 10);
- add_coin(MIN_CHANGE * 5 / 10);
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 1 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 2 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 3 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 4 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 5 / 10);
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
- add_coin(1111*MIN_CHANGE);
+ add_coin(coins, *wallet, 1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
- add_coin(MIN_CHANGE * 6 / 10);
- add_coin(MIN_CHANGE * 7 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
// they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
- empty_wallet();
+ coins.clear();
for (int j = 0; j < 20; j++)
- add_coin(50000 * COIN);
+ add_coin(coins, *wallet, 50000 * COIN);
- BOOST_CHECK(KnapsackSolver(500000 * COIN, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(500000 * COIN, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -485,79 +486,79 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// we need to try finding an exact subset anyway
// sometimes it will fail, and so we use the next biggest coin:
- empty_wallet();
- add_coin(MIN_CHANGE * 5 / 10);
- add_coin(MIN_CHANGE * 6 / 10);
- add_coin(MIN_CHANGE * 7 / 10);
- add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 5 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 7 / 10);
+ add_coin(coins, *wallet, 1111 * MIN_CHANGE);
+ BOOST_CHECK(KnapsackSolver(1 * MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
- empty_wallet();
- add_coin(MIN_CHANGE * 4 / 10);
- add_coin(MIN_CHANGE * 6 / 10);
- add_coin(MIN_CHANGE * 8 / 10);
- add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 4 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 6 / 10);
+ add_coin(coins, *wallet, MIN_CHANGE * 8 / 10);
+ add_coin(coins, *wallet, 1111 * MIN_CHANGE);
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
// test avoiding small change
- empty_wallet();
- add_coin(MIN_CHANGE * 5 / 100);
- add_coin(MIN_CHANGE * 1);
- add_coin(MIN_CHANGE * 100);
+ coins.clear();
+ add_coin(coins, *wallet, MIN_CHANGE * 5 / 100);
+ add_coin(coins, *wallet, MIN_CHANGE * 1);
+ add_coin(coins, *wallet, MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 10001 / 100, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 10001 / 100, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 9990 / 100, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(MIN_CHANGE * 9990 / 100, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
- }
-
- // test with many inputs
- for (CAmount amt=1500; amt < COIN; amt*=10) {
- empty_wallet();
- // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
- for (uint16_t j = 0; j < 676; j++)
- add_coin(amt);
-
- // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
- for (int i = 0; i < RUN_TESTS; i++) {
- BOOST_CHECK(KnapsackSolver(2000, KnapsackGroupOutputs(filter_confirmed), setCoinsRet, nValueRet));
-
- if (amt - 2000 < MIN_CHANGE) {
- // needs more than one input:
- uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
- CAmount returnValue = amt * returnSize;
- BOOST_CHECK_EQUAL(nValueRet, returnValue);
- BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
- } else {
- // one input is sufficient:
- BOOST_CHECK_EQUAL(nValueRet, amt);
- BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- }
- }
- }
-
- // test randomness
- {
- empty_wallet();
- for (int i2 = 0; i2 < 100; i2++)
- add_coin(COIN);
-
- // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
- for (int i = 0; i < RUN_TESTS; i++) {
+ }
+
+ // test with many inputs
+ for (CAmount amt=1500; amt < COIN; amt*=10) {
+ coins.clear();
+ // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
+ for (uint16_t j = 0; j < 676; j++)
+ add_coin(coins, *wallet, amt);
+
+ // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
+ for (int i = 0; i < RUN_TESTS; i++) {
+ BOOST_CHECK(KnapsackSolver(2000, KnapsackGroupOutputs(coins, *wallet, filter_confirmed), setCoinsRet, nValueRet));
+
+ if (amt - 2000 < MIN_CHANGE) {
+ // needs more than one input:
+ uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
+ CAmount returnValue = amt * returnSize;
+ BOOST_CHECK_EQUAL(nValueRet, returnValue);
+ BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
+ } else {
+ // one input is sufficient:
+ BOOST_CHECK_EQUAL(nValueRet, amt);
+ BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
+ }
+ }
+ }
+
+ // test randomness
+ {
+ coins.clear();
+ for (int i2 = 0; i2 < 100; i2++)
+ add_coin(coins, *wallet, COIN);
+
+ // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
+ for (int i = 0; i < RUN_TESTS; i++) {
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
- BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet, nValueRet));
- BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet));
+ BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(coins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(50 * COIN, GroupCoins(coins), setCoinsRet2, nValueRet));
BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
int fails = 0;
@@ -567,66 +568,67 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// When choosing 1 from 100 identical coins, 1% of the time, this test will choose the same coin twice
// which will cause it to fail.
// To avoid that issue, run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet, nValueRet));
- BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(vCoins), setCoinsRet2, nValueRet));
+ BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(coins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(COIN, GroupCoins(coins), setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
BOOST_CHECK_NE(fails, RANDOM_REPEATS);
- }
-
- // add 75 cents in small change. not enough to make 90 cents,
- // then try making 90 cents. there are multiple competing "smallest bigger" coins,
- // one of which should be picked at random
- add_coin(5 * CENT);
- add_coin(10 * CENT);
- add_coin(15 * CENT);
- add_coin(20 * CENT);
- add_coin(25 * CENT);
-
- for (int i = 0; i < RUN_TESTS; i++) {
+ }
+
+ // add 75 cents in small change. not enough to make 90 cents,
+ // then try making 90 cents. there are multiple competing "smallest bigger" coins,
+ // one of which should be picked at random
+ add_coin(coins, *wallet, 5 * CENT);
+ add_coin(coins, *wallet, 10 * CENT);
+ add_coin(coins, *wallet, 15 * CENT);
+ add_coin(coins, *wallet, 20 * CENT);
+ add_coin(coins, *wallet, 25 * CENT);
+
+ for (int i = 0; i < RUN_TESTS; i++) {
int fails = 0;
for (int j = 0; j < RANDOM_REPEATS; j++)
{
- BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet, nValueRet));
- BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(vCoins), setCoinsRet2, nValueRet));
+ BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(coins), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(90*CENT, GroupCoins(coins), setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
BOOST_CHECK_NE(fails, RANDOM_REPEATS);
- }
- }
-
- empty_wallet();
+ }
+ }
}
BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
{
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
CoinSet setCoinsRet;
CAmount nValueRet;
-
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
-
- empty_wallet();
+ std::vector<COutput> coins;
// Test vValue sort order
for (int i = 0; i < 1000; i++)
- add_coin(1000 * COIN);
- add_coin(3 * COIN);
+ add_coin(coins, *wallet, 1000 * COIN);
+ add_coin(coins, *wallet, 3 * COIN);
- BOOST_CHECK(KnapsackSolver(1003 * COIN, KnapsackGroupOutputs(filter_standard), setCoinsRet, nValueRet));
+ BOOST_CHECK(KnapsackSolver(1003 * COIN, KnapsackGroupOutputs(coins, *wallet, filter_standard), setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
-
- empty_wallet();
}
// Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
BOOST_AUTO_TEST_CASE(SelectCoins_test)
{
- LOCK(testWallet.cs_wallet);
- testWallet.SetupLegacyScriptPubKeyMan();
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", m_args, CreateMockWalletDatabase());
+ wallet->LoadWallet();
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
// Random generator stuff
std::default_random_engine generator;
@@ -636,12 +638,15 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
// Run this test 100 times
for (int i = 0; i < 100; ++i)
{
- empty_wallet();
+ std::vector<COutput> coins;
+ CAmount balance{0};
// Make a wallet with 1000 exponentially distributed random inputs
for (int j = 0; j < 1000; ++j)
{
- add_coin((CAmount)(distribution(generator)*10000000));
+ CAmount val = distribution(generator)*10000000;
+ add_coin(coins, *wallet, val);
+ balance += val;
}
// Generate a random fee rate in the range of 100 - 400
@@ -658,7 +663,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CoinSet out_set;
CAmount out_value = 0;
CCoinControl cc;
- BOOST_CHECK(SelectCoins(testWallet, vCoins, target, out_set, out_value, cc, cs_params));
+ BOOST_CHECK(SelectCoins(*wallet, coins, target, out_set, out_value, cc, cs_params));
BOOST_CHECK_GE(out_value, target);
}
}
diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp
new file mode 100644
index 0000000000..252832785b
--- /dev/null
+++ b/src/wallet/test/fuzz/notifications.cpp
@@ -0,0 +1,173 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <util/translation.h>
+#include <wallet/context.h>
+#include <wallet/receive.h>
+#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
+#include <wallet/walletutil.h>
+
+#include <cassert>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace {
+const TestingSetup* g_setup;
+
+void initialize_setup()
+{
+ static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
+}
+
+/**
+ * Wraps a descriptor wallet for fuzzing. The constructor writes the sqlite db
+ * to disk, the destructor deletes it.
+ */
+struct FuzzedWallet {
+ ArgsManager args;
+ WalletContext context;
+ std::shared_ptr<CWallet> wallet;
+ FuzzedWallet(const std::string& name)
+ {
+ context.args = &args;
+ context.chain = g_setup->m_node.chain.get();
+
+ DatabaseOptions options;
+ options.require_create = true;
+ options.create_flags = WALLET_FLAG_DESCRIPTORS;
+ const std::optional<bool> load_on_start;
+ gArgs.ForceSetArg("-keypool", "0"); // Avoid timeout in TopUp()
+
+ DatabaseStatus status;
+ bilingual_str error;
+ std::vector<bilingual_str> warnings;
+ wallet = CreateWallet(context, name, load_on_start, options, status, error, warnings);
+ assert(wallet);
+ assert(error.empty());
+ assert(warnings.empty());
+ assert(wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
+ }
+ ~FuzzedWallet()
+ {
+ const auto name{wallet->GetName()};
+ std::vector<bilingual_str> warnings;
+ std::optional<bool> load_on_start;
+ assert(RemoveWallet(context, wallet, load_on_start, warnings));
+ assert(warnings.empty());
+ UnloadWallet(std::move(wallet));
+ fs::remove_all(GetWalletDir() / name);
+ }
+ CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider)
+ {
+ auto type{fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)};
+ if (type == OutputType::BECH32M) {
+ type = OutputType::BECH32; // TODO: Setup taproot descriptor and remove this line
+ }
+ CTxDestination dest;
+ bilingual_str error;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ assert(wallet->GetNewDestination(type, "", dest, error));
+ } else {
+ assert(wallet->GetNewChangeDestination(type, dest, error));
+ }
+ assert(error.empty());
+ return GetScriptForDestination(dest);
+ }
+};
+
+FUZZ_TARGET_INIT(wallet_notifications, initialize_setup)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ // The total amount, to be distributed to the wallets a and b in txs
+ // without fee. Thus, the balance of the wallets should always equal the
+ // total amount.
+ const auto total_amount{ConsumeMoney(fuzzed_data_provider)};
+ FuzzedWallet a{"fuzzed_wallet_a"};
+ FuzzedWallet b{"fuzzed_wallet_b"};
+
+ // Keep track of all coins in this test.
+ // Each tuple in the chain represents the coins and the block created with
+ // those coins. Once the block is mined, the next tuple will have an empty
+ // block and the freshly mined coins.
+ using Coins = std::set<std::tuple<CAmount, COutPoint>>;
+ std::vector<std::tuple<Coins, CBlock>> chain;
+ {
+ // Add the inital entry
+ chain.emplace_back();
+ auto& [coins, block]{chain.back()};
+ coins.emplace(total_amount, COutPoint{uint256::ONE, 1});
+ }
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 200)
+ {
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ auto& [coins_orig, block]{chain.back()};
+ // Copy the coins for this block and consume all of them
+ Coins coins = coins_orig;
+ while (!coins.empty()) {
+ // Create a new tx
+ CMutableTransaction tx{};
+ // Add some coins as inputs to it
+ auto num_inputs{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, coins.size())};
+ CAmount in{0};
+ while (num_inputs-- > 0) {
+ const auto& [coin_amt, coin_outpoint]{*coins.begin()};
+ in += coin_amt;
+ tx.vin.emplace_back(coin_outpoint);
+ coins.erase(coins.begin());
+ }
+ // Create some outputs spending all inputs, without fee
+ LIMITED_WHILE(in > 0 && fuzzed_data_provider.ConsumeBool(), 100)
+ {
+ const auto out_value{ConsumeMoney(fuzzed_data_provider, in)};
+ in -= out_value;
+ auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
+ tx.vout.emplace_back(out_value, wallet.GetScriptPubKey(fuzzed_data_provider));
+ }
+ // Spend the remaining input value, if any
+ auto& wallet{fuzzed_data_provider.ConsumeBool() ? a : b};
+ tx.vout.emplace_back(in, wallet.GetScriptPubKey(fuzzed_data_provider));
+ // Add tx to block
+ block.vtx.emplace_back(MakeTransactionRef(tx));
+ }
+ // Mine block
+ a.wallet->blockConnected(block, chain.size());
+ b.wallet->blockConnected(block, chain.size());
+ // Store the coins for the next block
+ Coins coins_new;
+ for (const auto& tx : block.vtx) {
+ uint32_t i{0};
+ for (const auto& out : tx->vout) {
+ coins_new.emplace(out.nValue, COutPoint{tx->GetHash(), i++});
+ }
+ }
+ chain.emplace_back(coins_new, CBlock{});
+ },
+ [&] {
+ if (chain.size() <= 1) return; // The first entry can't be removed
+ auto& [coins, block]{chain.back()};
+ if (block.vtx.empty()) return; // Can only disconnect if the block was submitted first
+ // Disconnect block
+ a.wallet->blockDisconnected(block, chain.size() - 1);
+ b.wallet->blockDisconnected(block, chain.size() - 1);
+ chain.pop_back();
+ });
+ auto& [coins, first_block]{chain.front()};
+ if (!first_block.vtx.empty()) {
+ // Only check balance when at least one block was submitted
+ const auto bal_a{GetBalance(*a.wallet).m_mine_trusted};
+ const auto bal_b{GetBalance(*b.wallet).m_mine_trusted};
+ assert(total_amount == bal_a + bal_b);
+ }
+ }
+}
+} // namespace
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index 5d25885bd4..dda202d55e 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -126,7 +126,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid)
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -178,7 +178,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -282,7 +282,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -389,7 +389,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -403,7 +403,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard
{
- CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", m_args, CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 8a97f7779d..dd24fa2c19 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -13,10 +13,22 @@
BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
+static void import_descriptor(CWallet& wallet, const std::string& descriptor)
+ EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+{
+ AssertLockHeld(wallet.cs_wallet);
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse(descriptor, provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 10, 0);
+ wallet.AddWalletDescriptor(w_desc, provider, "", false);
+}
+
BOOST_AUTO_TEST_CASE(psbt_updater_test)
{
- auto spk_man = m_wallet.GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(m_wallet.cs_wallet, spk_man->cs_KeyStore);
+ LOCK(m_wallet.cs_wallet);
+ m_wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
// Create prevtxs and add to wallet
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
@@ -29,27 +41,10 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
s_prev_tx2 >> prev_tx2;
m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(prev_tx2));
- // Add scripts
- CScript rs1;
- CDataStream s_rs1(ParseHex("475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae"), SER_NETWORK, PROTOCOL_VERSION);
- s_rs1 >> rs1;
- spk_man->AddCScript(rs1);
-
- CScript rs2;
- CDataStream s_rs2(ParseHex("2200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"), SER_NETWORK, PROTOCOL_VERSION);
- s_rs2 >> rs2;
- spk_man->AddCScript(rs2);
-
- CScript ws1;
- CDataStream s_ws1(ParseHex("47522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"), SER_NETWORK, PROTOCOL_VERSION);
- s_ws1 >> ws1;
- spk_man->AddCScript(ws1);
-
- // Add hd seed
- CKey key = DecodeSecret("5KSSJQ7UNfFGwVgpCZDSHm5rVNhMFcFtvWM3zQ8mW4qNDEN7LFd"); // Mainnet and uncompressed form of cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T
- CPubKey master_pub_key = spk_man->DeriveNewSeed(key);
- spk_man->SetHDSeed(master_pub_key);
- spk_man->NewKeyPool();
+ // Import descriptors for keys and scripts
+ import_descriptor(m_wallet, "sh(multi(2,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/0h,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/1h))");
+ import_descriptor(m_wallet, "sh(wsh(multi(2,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/2h,xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/3h)))");
+ import_descriptor(m_wallet, "wpkh(xprv9s21ZrQH143K2LE7W4Xf3jATf9jECxSb7wj91ZnmY4qEJrS66Qru9RFqq8xbkgT32ya6HqYJweFdJUEDf5Q6JFV7jMiUws7kQfe6Tv4RbfN/0h/0h/*h)");
// Call FillPSBT
PartiallySignedTransaction psbtx;
@@ -71,7 +66,8 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Try to sign the mutated input
SignatureData sigdata;
- BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
+ BOOST_CHECK(m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true, true) != TransactionError::OK);
+ //BOOST_CHECK(spk_man->FillPSBT(psbtx, PrecomputePSBTData(psbtx), SIGHASH_ALL, true, true) != TransactionError::OK);
}
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
index 347a436429..0e78855ced 100644
--- a/src/wallet/test/scriptpubkeyman_tests.cpp
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -17,7 +17,7 @@ BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(CanProvide)
{
// Set up wallet and keyman variables.
- CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index becef70729..926f28686d 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -17,7 +17,7 @@ BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup)
BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- auto wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), coinbaseKey);
+ auto wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), m_args, coinbaseKey);
// Check that a subtract-from-recipient transaction slightly less than the
// coinbase input amount does not create a change output (because it would
@@ -33,6 +33,8 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
CCoinControl coin_control;
coin_control.m_feerate.emplace(10000);
coin_control.fOverrideFeeRate = true;
+ // We need to use a change type with high cost of change so that the leftover amount will be dropped to fee instead of added as a change output
+ coin_control.m_change_type = OutputType::LEGACY;
FeeCalculation fee_calc;
BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
BOOST_CHECK_EQUAL(tx->vout.size(), 1);
diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp
index c3061b93c0..93a3404d2c 100644
--- a/src/wallet/test/util.cpp
+++ b/src/wallet/test/util.cpp
@@ -6,6 +6,7 @@
#include <chain.h>
#include <key.h>
+#include <key_io.h>
#include <test/util/setup_common.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -14,18 +15,25 @@
#include <memory>
-std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
+std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key)
{
- auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockWalletDatabase());
+ auto wallet = std::make_unique<CWallet>(&chain, "", args, CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
}
wallet->LoadWallet();
{
- auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
- spk_man->AddKeyPubKey(key, key.GetPubKey());
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetupDescriptorScriptPubKeyMans();
+
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
}
WalletRescanReserver reserver(*wallet);
reserver.reserve();
diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h
index 288c111571..3adb82b85f 100644
--- a/src/wallet/test/util.h
+++ b/src/wallet/test/util.h
@@ -7,6 +7,7 @@
#include <memory>
+class ArgsManager;
class CChain;
class CKey;
class CWallet;
@@ -14,6 +15,6 @@ namespace interfaces {
class Chain;
} // namespace interfaces
-std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key);
+std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key);
#endif // BITCOIN_WALLET_TEST_UTIL_H
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index fc744ebe5b..762702522e 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -4,11 +4,18 @@
#include <wallet/test/wallet_test_fixture.h>
+#include <scheduler.h>
+
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
- m_wallet(m_node.chain.get(), "", CreateMockWalletDatabase())
+ m_wallet(m_node.chain.get(), "", m_args, CreateMockWalletDatabase())
{
m_wallet.LoadWallet();
m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
m_wallet_client->registerRpcs();
}
+
+WalletTestingSetup::~WalletTestingSetup()
+{
+ if (m_node.scheduler) m_node.scheduler->stop();
+}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index ab7fb8c42b..8bf2d36227 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -19,6 +19,7 @@
*/
struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
+ ~WalletTestingSetup();
std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_node.chain, *Assert(m_node.args));
CWallet m_wallet;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 94b5abfba7..4499eb5903 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -11,6 +11,7 @@
#include <vector>
#include <interfaces/chain.h>
+#include <key_io.h>
#include <node/blockstorage.h>
#include <node/context.h>
#include <policy/policy.h>
@@ -40,9 +41,10 @@ static_assert(WALLET_INCREMENTAL_RELAY_FEE >= DEFAULT_INCREMENTAL_RELAY_FEE, "wa
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
-static std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
+static const std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
{
DatabaseOptions options;
+ options.create_flags = WALLET_FLAG_DESCRIPTORS;
DatabaseStatus status;
bilingual_str error;
std::vector<bilingual_str> warnings;
@@ -77,9 +79,13 @@ static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t in
static void AddKey(CWallet& wallet, const CKey& key)
{
- auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
- LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- spk_man->AddKeyPubKey(key, key.GetPubKey());
+ LOCK(wallet.cs_wallet);
+ FlatSigningProvider provider;
+ std::string error;
+ std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
+ assert(desc);
+ WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
+ if (!wallet.AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
}
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
@@ -92,9 +98,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
- CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -111,9 +118,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -137,9 +145,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -162,9 +171,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
@@ -198,7 +208,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
WalletContext context;
@@ -264,7 +274,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
WalletContext context;
context.args = &gArgs;
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -286,7 +296,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
@@ -319,11 +329,13 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
- CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
- auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
+ CWallet wallet(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
CWalletTx wtx(m_coinbase_txns.back());
- LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
+ LOCK(wallet.cs_wallet);
+ wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet.SetupDescriptorScriptPubKeyMans();
+
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 0);
@@ -336,7 +348,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
// Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated.
wtx.MarkDirty();
- BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()));
+ AddKey(wallet, coinbaseKey);
BOOST_CHECK_EQUAL(CachedTxGetImmatureCredit(wallet, wtx), 50*COIN);
}
@@ -491,7 +503,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), coinbaseKey);
+ wallet = CreateSyncedWallet(*m_node.chain, m_node.chainman->ActiveChain(), m_args, coinbaseKey);
}
~ListCoinsTestingSetup()
@@ -593,14 +605,26 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
- wallet->SetupLegacyScriptPubKeyMan();
- wallet->SetMinVersion(FEATURE_LATEST);
- wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
- BOOST_CHECK(!wallet->TopUpKeyPool(1000));
- CTxDestination dest;
- bilingual_str error;
- BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
+ {
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ wallet->SetupLegacyScriptPubKeyMan();
+ wallet->SetMinVersion(FEATURE_LATEST);
+ wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ BOOST_CHECK(!wallet->TopUpKeyPool(1000));
+ CTxDestination dest;
+ bilingual_str error;
+ BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
+ }
+ {
+ const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
+ LOCK(wallet->cs_wallet);
+ wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
+ wallet->SetMinVersion(FEATURE_LATEST);
+ wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ CTxDestination dest;
+ bilingual_str error;
+ BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
+ }
}
// Explicit calculation which is used to test the wallet constant
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 6fc1bd1eed..1ccef31056 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -162,7 +162,8 @@ public:
int block_height;
uint256 hashBlock;
int nIndex;
- Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
+ Confirmation(Status status = UNCONFIRMED, int block_height = 0, uint256 block_hash = uint256(), int block_index = 0)
+ : status{status}, block_height{block_height}, hashBlock{block_hash}, nIndex{block_index} {}
};
Confirmation m_confirm;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index a749ab8897..7f60dd6906 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -221,7 +221,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::s
}
context.chain->initMessage(_("Loading wallet…").translated);
- std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings);
+ const std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_LOAD;
@@ -301,7 +301,7 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
// Make the wallet
context.chain->initMessage(_("Loading wallet…").translated);
- std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
+ const std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
status = DatabaseStatus::FAILED_CREATE;
@@ -959,7 +959,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
#if HAVE_SYSTEM
// notify an external script when a wallet transaction comes in or is updated
- std::string strCmd = gArgs.GetArg("-walletnotify", "");
+ std::string strCmd = m_args.GetArg("-walletnotify", "");
if (!strCmd.empty())
{
@@ -1212,7 +1212,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
void CWallet::transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
LOCK(cs_wallet);
- SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
@@ -1253,7 +1253,7 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
// distinguishing between conflicted and unconfirmed transactions are
// imperfect, and could be improved in general, see
// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
- SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
}
}
@@ -1281,7 +1281,7 @@ void CWallet::blockDisconnected(const CBlock& block, int height)
m_last_block_processed_height = height - 1;
m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) {
- SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
}
}
@@ -2540,7 +2540,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
int64_t nStart = GetTimeMillis();
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet);
+ const std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, args, std::move(database)), ReleaseWallet);
bool rescan_required = false;
DBErrors nLoadWalletRet = walletInstance->LoadWallet();
if (nLoadWalletRet != DBErrors::LOAD_OK) {
@@ -2732,11 +2732,11 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
walletInstance->m_default_max_tx_fee = max_fee.value();
}
- if (gArgs.IsArgSet("-consolidatefeerate")) {
- if (std::optional<CAmount> consolidate_feerate = ParseMoney(gArgs.GetArg("-consolidatefeerate", ""))) {
+ if (args.IsArgSet("-consolidatefeerate")) {
+ if (std::optional<CAmount> consolidate_feerate = ParseMoney(args.GetArg("-consolidatefeerate", ""))) {
walletInstance->m_consolidate_feerate = CFeeRate(*consolidate_feerate);
} else {
- error = AmountErrMsg("consolidatefeerate", gArgs.GetArg("-consolidatefeerate", ""));
+ error = AmountErrMsg("consolidatefeerate", args.GetArg("-consolidatefeerate", ""));
return nullptr;
}
}
@@ -3160,7 +3160,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
// Get the extended key
CExtKey master_key;
- master_key.SetSeed(seed_key.begin(), seed_key.size());
+ master_key.SetSeed(seed_key);
for (bool internal : {false, true}) {
for (OutputType t : OUTPUT_TYPES) {
@@ -3235,7 +3235,8 @@ void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool intern
auto spk_man = m_spk_managers.at(id).get();
spk_mans[type] = spk_man;
- if (spk_mans_other[type] == spk_man) {
+ const auto it = spk_mans_other.find(type);
+ if (it != spk_mans_other.end() && it->second == spk_man) {
spk_mans_other.erase(type);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 767b24adbb..3855ad821d 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -260,9 +260,9 @@ private:
void AddToSpends(const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
- * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * Add a transaction to the wallet, or update it. confirm.block_* should
* be set when the transaction was known to be included in a block. When
- * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
+ * block_hash.IsNull(), then wallet state is not updated in AddToWallet, but
* notifications happen and cached balances are marked dirty.
*
* If fUpdate is true, existing transactions will be updated.
@@ -270,7 +270,7 @@ private:
* assumption that any further notification of a transaction that was considered
* abandoned is an indication that it is not safe to be considered abandoned.
* Abandoned state should probably be more carefully tracked via different
- * posInBlock signals or by checking mempool presence when necessary.
+ * chain notifications or by checking mempool presence when necessary.
*
* Should be called with rescanning_old_block set to true, if the transaction is
* not discovered in real time, but during a rescan of old blocks.
@@ -285,8 +285,6 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
- * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** WalletFlags set on this wallet. */
@@ -300,6 +298,9 @@ private:
//! Unset the blank wallet flag and saves it to disk
void UnsetBlankWalletFlag(WalletBatch& batch) override;
+ /** Provider of aplication-wide arguments. */
+ const ArgsManager& m_args;
+
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
@@ -361,8 +362,9 @@ public:
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database)
- : m_chain(chain),
+ CWallet(interfaces::Chain* chain, const std::string& name, const ArgsManager& args, std::unique_ptr<WalletDatabase> database)
+ : m_args(args),
+ m_chain(chain),
m_name(name),
m_database(std::move(database))
{
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index a6839f1f78..c920d4af51 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1188,9 +1188,9 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
/** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
{
-#ifdef USE_BDB
- return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
-#elif USE_SQLITE
+#ifdef USE_SQLITE
return std::make_unique<SQLiteDatabase>("", "", true);
+#elif USE_BDB
+ return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
#endif
}
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 788679bbeb..b609ba6881 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -40,7 +40,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
wallet_instance->TopUpKeyPool();
}
-static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
+static const std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, const ArgsManager& args, DatabaseOptions options)
{
DatabaseStatus status;
bilingual_str error;
@@ -51,7 +51,7 @@ static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::pa
}
// dummy chain interface
- std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet};
+ std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, args, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
load_wallet_ret = wallet_instance->LoadWallet();
@@ -120,6 +120,10 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
return false;
}
+ if (args.IsArgSet("-legacy") && command != "create") {
+ tfm::format(std::cerr, "The -legacy option can only be used with the 'create' command.\n");
+ return false;
+ }
if (command == "create" && !args.IsArgSet("-wallet")) {
tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
return false;
@@ -130,12 +134,24 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
if (command == "create") {
DatabaseOptions options;
options.require_create = true;
- if (args.GetBoolArg("-descriptors", false)) {
+ // If -legacy is set, use it. Otherwise default to false.
+ bool make_legacy = args.GetBoolArg("-legacy", false);
+ // If neither -legacy nor -descriptors is set, default to true. If -descriptors is set, use its value.
+ bool make_descriptors = (!args.IsArgSet("-descriptors") && !args.IsArgSet("-legacy")) || (args.IsArgSet("-descriptors") && args.GetBoolArg("-descriptors", true));
+ if (make_legacy && make_descriptors) {
+ tfm::format(std::cerr, "Only one of -legacy or -descriptors can be set to true, not both\n");
+ return false;
+ }
+ if (!make_legacy && !make_descriptors) {
+ tfm::format(std::cerr, "One of -legacy or -descriptors must be set to true (or omitted)\n");
+ return false;
+ }
+ if (make_descriptors) {
options.create_flags |= WALLET_FLAG_DESCRIPTORS;
options.require_format = DatabaseFormat::SQLITE;
}
- std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
@@ -143,7 +159,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
} else if (command == "info") {
DatabaseOptions options;
options.require_existing = true;
- std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
@@ -168,7 +184,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
} else if (command == "dump") {
DatabaseOptions options;
options.require_existing = true;
- std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
+ const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (!wallet_instance) return false;
bilingual_str error;
bool ret = DumpWallet(*wallet_instance, error);